Merge "Snap for 5475808 from 3aff98aee837ac4a416498cfa26c8cc903e1226e to sdk-release" into sdk-release
diff --git a/Android.bp b/Android.bp
index afa0337..0eb5fd9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,77 +26,6 @@
     ],
 }
 
-cc_library {
-    name: "librecovery_ui",
-    recovery_available: true,
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "device.cpp",
-        "screen_ui.cpp",
-        "ui.cpp",
-        "vr_ui.cpp",
-        "wear_ui.cpp"
-    ],
-
-    static_libs: [
-        "libminui",
-        "libotautil",
-        "libfstab",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "libpng",
-        "libz",
-    ],
-}
-
-// Generic device that uses ScreenRecoveryUI.
-cc_library_static {
-    name: "librecovery_ui_default",
-    recovery_available: true,
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "default_device.cpp",
-    ],
-}
-
-// The default wear device that uses WearRecoveryUI.
-cc_library_static {
-    name: "librecovery_ui_wear",
-    recovery_available: true,
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "wear_device.cpp",
-    ],
-}
-
-// The default VR device that uses VrRecoveryUI.
-cc_library_static {
-    name: "librecovery_ui_vr",
-    recovery_available: true,
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "vr_device.cpp",
-    ],
-}
-
 cc_library_static {
     name: "librecovery_fastboot",
     recovery_available: true,
@@ -113,6 +42,7 @@
         "libbootloader_message",
         "libcutils",
         "liblog",
+        "librecovery_ui",
     ],
 
     static_libs: [
@@ -133,32 +63,19 @@
         "libbootloader_message",
         "libcrypto",
         "libcutils",
-        "libext4_utils",
         "libfs_mgr",
-        "libfusesideload",
-        "libhidl-gen-utils",
-        "libhidlbase",
-        "libhidltransport",
         "liblog",
-        "libpng",
-        "libselinux",
-        "libtinyxml2",
-        "libutils",
-        "libz",
         "libziparchive",
     ],
 
     static_libs: [
+        "libinstall",
         "librecovery_fastboot",
         "libminui",
-        "libverifier",
         "libotautil",
 
         // external dependencies
         "libhealthhalutils",
-        "libvintf_recovery",
-        "libvintf",
-        "libfstab",
     ],
 }
 
@@ -171,41 +88,12 @@
     ],
 
     srcs: [
-        "adb_install.cpp",
         "fsck_unshare_blocks.cpp",
-        "fuse_sdcard_provider.cpp",
-        "install.cpp",
         "recovery.cpp",
-        "roots.cpp",
-    ],
-
-    include_dirs: [
-        "system/vold",
-    ],
-}
-
-cc_library_static {
-    name: "libverifier",
-    recovery_available: true,
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "asn1_decoder.cpp",
-        "verifier.cpp",
     ],
 
     shared_libs: [
-        "libbase",
-        "libcrypto",
-        "libcrypto_utils",
-        "libziparchive",
-    ],
-
-    static_libs: [
-        "libotautil",
+        "librecovery_ui",
     ],
 }
 
@@ -214,16 +102,15 @@
     recovery: true,
 
     defaults: [
+        "libinstall_defaults",
         "librecovery_defaults",
     ],
 
     srcs: [
-        "logging.cpp",
         "recovery_main.cpp",
     ],
 
     shared_libs: [
-        "libminadbd_services",
         "librecovery_ui",
     ],
 
@@ -235,6 +122,7 @@
     required: [
         "e2fsdroid.recovery",
         "librecovery_ui_ext",
+        "minadbd",
         "mke2fs.conf.recovery",
         "mke2fs.recovery",
         "recovery_deps",
@@ -250,7 +138,6 @@
     ],
 
     srcs: [
-        "logging.cpp",
         "recovery-persist.cpp",
     ],
 
@@ -262,7 +149,6 @@
 
     static_libs: [
         "libotautil",
-        "libfstab",
     ],
 
     init_rc: [
@@ -279,7 +165,6 @@
     ],
 
     srcs: [
-        "logging.cpp",
         "recovery-refresh.cpp",
     ],
 
@@ -290,7 +175,6 @@
 
     static_libs: [
         "libotautil",
-        "libfstab",
     ],
 
     init_rc: [
diff --git a/CleanSpec.mk b/CleanSpec.mk
index fec823e..a7ab0d9 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -44,9 +44,13 @@
 #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
 
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin)
+
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/adb_install.cpp b/adb_install.cpp
deleted file mode 100644
index 438da9f..0000000
--- a/adb_install.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "adb_install.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <unistd.h>
-
-#include <android-base/logging.h>
-#include <android-base/properties.h>
-
-#include "common.h"
-#include "fuse_sideload.h"
-#include "install.h"
-#include "ui.h"
-
-int apply_from_adb(bool* wipe_cache) {
-  modified_flash = true;
-  // Save the usb state to restore after the sideload operation.
-  std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
-  // Clean up state and stop adbd.
-  if (usb_state != "none" && !SetUsbConfig("none")) {
-    LOG(ERROR) << "Failed to clear USB config";
-    return INSTALL_ERROR;
-  }
-
-  ui->Print(
-      "\n\nNow send the package you want to apply\n"
-      "to the device with \"adb sideload <filename>\"...\n");
-
-  pid_t child;
-  if ((child = fork()) == 0) {
-    execl("/system/bin/recovery", "recovery", "--adbd", nullptr);
-    _exit(EXIT_FAILURE);
-  }
-
-  if (!SetUsbConfig("sideload")) {
-    LOG(ERROR) << "Failed to set usb config to sideload";
-    return INSTALL_ERROR;
-  }
-
-  // How long (in seconds) we wait for the host to start sending us a package, before timing out.
-  static constexpr int ADB_INSTALL_TIMEOUT = 300;
-
-  // 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.)
-  int result = INSTALL_ERROR;
-  int status;
-  bool waited = false;
-  for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
-    if (waitpid(child, &status, WNOHANG) != 0) {
-      result = INSTALL_ERROR;
-      waited = true;
-      break;
-    }
-
-    struct stat st;
-    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
-      if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
-        sleep(1);
-        continue;
-      } else {
-        ui->Print("\nTimed out waiting for package.\n\n");
-        result = INSTALL_ERROR;
-        kill(child, SIGKILL);
-        break;
-      }
-    }
-    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0);
-    break;
-  }
-
-  if (!waited) {
-    // Calling stat() on this magic filename signals the minadbd subprocess to shut down.
-    struct stat st;
-    stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
-
-    // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on
-    // the device). For now you just have to 'adb sideload' a file that's not a valid package, like
-    // "/dev/null".
-    waitpid(child, &status, 0);
-  }
-
-  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-    if (WEXITSTATUS(status) == 3) {
-      ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n");
-    } else if (!WIFSIGNALED(status)) {
-      ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status));
-    }
-  }
-
-  // Clean up before switching to the older state, for example setting the state
-  // to none sets sys/class/android_usb/android0/enable to 0.
-  if (!SetUsbConfig("none")) {
-    LOG(ERROR) << "Failed to clear USB config";
-  }
-
-  if (usb_state != "none") {
-    if (!SetUsbConfig(usb_state)) {
-      LOG(ERROR) << "Failed to set USB config to " << usb_state;
-    }
-  }
-
-  return result;
-}
diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp
index f9383dd..90d8e86 100644
--- a/applypatch/applypatch.cpp
+++ b/applypatch/applypatch.cpp
@@ -76,7 +76,7 @@
   }
 
   android::base::unique_fd dev(open(partition.name.c_str(), O_RDONLY));
-  if (!dev) {
+  if (dev == -1) {
     PLOG(ERROR) << "Failed to open eMMC partition \"" << partition << "\"";
   } else {
     std::vector<unsigned char> buffer(partition.size);
diff --git a/common.h b/common.h
index 38bdd52..a524a41 100644
--- a/common.h
+++ b/common.h
@@ -14,11 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef RECOVERY_COMMON_H
-#define RECOVERY_COMMON_H
-
-#include <stdarg.h>
-#include <stdio.h>
+#pragma once
 
 #include <string>
 
@@ -31,7 +27,6 @@
 
 extern struct selabel_handle* sehandle;
 extern RecoveryUI* ui;
-extern bool modified_flash;
 extern bool has_cache;
 
 // The current stage, e.g. "1/2".
@@ -40,9 +35,4 @@
 // The reason argument provided in "--reason=".
 extern const char* reason;
 
-void ui_print(const char* format, ...) __printflike(1, 2);
-
 bool is_ro_debuggable();
-
-bool SetUsbConfig(const std::string& state);
-#endif  // RECOVERY_COMMON_H
diff --git a/etc/init.rc b/etc/init.rc
index fa8fe26..0822aba 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -73,7 +73,7 @@
     critical
     seclabel u:r:ueventd:s0
 
-service charger /charger -r
+service charger /system/bin/charger
     critical
     seclabel u:r:charger:s0
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 8458c99..14f5e4b 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -27,8 +27,7 @@
 #include <android-base/properties.h>
 #include <bootloader_message/bootloader_message.h>
 
-#include "device.h"
-#include "ui.h"
+#include "recovery_ui/ui.h"
 
 static const std::vector<std::pair<std::string, Device::BuiltinAction>> kFastbootMenuActions{
   { "Reboot system now", Device::REBOOT },
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 53a2adc..1aa7de6 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -19,6 +19,6 @@
 #include <string>
 #include <vector>
 
-#include "device.h"
+#include "recovery_ui/device.h"
 
 Device::BuiltinAction StartFastboot(Device* device, const std::vector<std::string>& args);
diff --git a/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp
index ce6940a..0f8fffa 100644
--- a/fsck_unshare_blocks.cpp
+++ b/fsck_unshare_blocks.cpp
@@ -34,9 +34,8 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 #include <android-base/unique_fd.h>
-#include <fstab/fstab.h>
 
-#include "roots.h"
+#include "otautil/roots.h"
 
 static constexpr const char* SYSTEM_E2FSCK_BIN = "/system/bin/e2fsck_static";
 static constexpr const char* TMP_E2FSCK_BIN = "/tmp/e2fsck.bin";
diff --git a/fuse_sdcard_provider.cpp b/fuse_sdcard_provider.cpp
deleted file mode 100644
index 46bdf17..0000000
--- a/fuse_sdcard_provider.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "fuse_sdcard_provider.h"
-
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mount.h>
-#include <sys/stat.h>
-#include <unistd.h>
-
-#include <functional>
-
-#include <android-base/file.h>
-
-#include "fuse_sideload.h"
-
-struct file_data {
-  int fd;  // the underlying sdcard file
-
-  uint64_t file_size;
-  uint32_t block_size;
-};
-
-static int read_block_file(const file_data& fd, uint32_t block, uint8_t* buffer,
-                           uint32_t fetch_size) {
-  off64_t offset = static_cast<off64_t>(block) * fd.block_size;
-  if (TEMP_FAILURE_RETRY(lseek64(fd.fd, offset, SEEK_SET)) == -1) {
-    fprintf(stderr, "seek on sdcard failed: %s\n", strerror(errno));
-    return -EIO;
-  }
-
-  if (!android::base::ReadFully(fd.fd, buffer, fetch_size)) {
-    fprintf(stderr, "read on sdcard failed: %s\n", strerror(errno));
-    return -EIO;
-  }
-
-  return 0;
-}
-
-bool start_sdcard_fuse(const char* path) {
-  struct stat sb;
-  if (stat(path, &sb) == -1) {
-    fprintf(stderr, "failed to stat %s: %s\n", path, strerror(errno));
-    return false;
-  }
-
-  file_data fd;
-  fd.fd = open(path, O_RDONLY);
-  if (fd.fd == -1) {
-    fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno));
-    return false;
-  }
-  fd.file_size = sb.st_size;
-  fd.block_size = 65536;
-
-  provider_vtab vtab;
-  vtab.read_block = std::bind(&read_block_file, fd, std::placeholders::_1, std::placeholders::_2,
-                              std::placeholders::_3);
-  vtab.close = [&fd]() { close(fd.fd); };
-
-  // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
-  // that our open file continues to work but new references see it as unmounted.
-  umount2("/sdcard", MNT_DETACH);
-
-  return run_fuse_sideload(vtab, fd.file_size, fd.block_size) == 0;
-}
diff --git a/fuse_sdcard_provider.h b/fuse_sdcard_provider.h
deleted file mode 100644
index bdc60f2..0000000
--- a/fuse_sdcard_provider.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __FUSE_SDCARD_PROVIDER_H
-#define __FUSE_SDCARD_PROVIDER_H
-
-bool start_sdcard_fuse(const char* path);
-
-#endif
diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp
index 90c4c22..8548548 100644
--- a/fuse_sideload/Android.bp
+++ b/fuse_sideload/Android.bp
@@ -16,14 +16,17 @@
     name: "libfusesideload",
     recovery_available: true,
 
+    defaults: [
+        "recovery_defaults",
+    ],
+
     cflags: [
         "-D_XOPEN_SOURCE",
         "-D_GNU_SOURCE",
-        "-Wall",
-        "-Werror",
     ],
 
     srcs: [
+        "fuse_provider.cpp",
         "fuse_sideload.cpp",
     ],
 
diff --git a/fuse_sideload/fuse_provider.cpp b/fuse_sideload/fuse_provider.cpp
new file mode 100644
index 0000000..58786f5
--- /dev/null
+++ b/fuse_sideload/fuse_provider.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fuse_provider.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <functional>
+
+#include <android-base/file.h>
+
+#include "fuse_sideload.h"
+
+FuseFileDataProvider::FuseFileDataProvider(const std::string& path, uint32_t block_size) {
+  struct stat sb;
+  if (stat(path.c_str(), &sb) == -1) {
+    fprintf(stderr, "failed to stat %s: %s\n", path.c_str(), strerror(errno));
+    return;
+  }
+
+  fd_.reset(open(path.c_str(), O_RDONLY));
+  if (fd_ == -1) {
+    fprintf(stderr, "failed to open %s: %s\n", path.c_str(), strerror(errno));
+    return;
+  }
+  file_size_ = sb.st_size;
+  fuse_block_size_ = block_size;
+}
+
+bool FuseFileDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+                                                uint32_t start_block) const {
+  uint64_t offset = static_cast<uint64_t>(start_block) * fuse_block_size_;
+  if (fetch_size > file_size_ || offset > file_size_ - fetch_size) {
+    fprintf(stderr,
+            "Out of bound read, start block: %" PRIu32 ", fetch size: %" PRIu32
+            ", file size %" PRIu64 "\n",
+            start_block, fetch_size, file_size_);
+    return false;
+  }
+
+  if (!android::base::ReadFullyAtOffset(fd_, buffer, fetch_size, offset)) {
+    fprintf(stderr, "Failed to read fetch size: %" PRIu32 " bytes data at offset %" PRIu64 ": %s\n",
+            fetch_size, offset, strerror(errno));
+    return false;
+  }
+
+  return true;
+}
+
+void FuseFileDataProvider::Close() {
+  fd_.reset();
+}
diff --git a/fuse_sideload/fuse_sideload.cpp b/fuse_sideload/fuse_sideload.cpp
index 1c7e98f..3d94803 100644
--- a/fuse_sideload/fuse_sideload.cpp
+++ b/fuse_sideload/fuse_sideload.cpp
@@ -76,7 +76,7 @@
 struct fuse_data {
   android::base::unique_fd ffd;  // file descriptor for the fuse socket
 
-  provider_vtab vtab;
+  FuseDataProvider* provider;  // Provider of the source data.
 
   uint64_t file_size;  // bytes
 
@@ -236,7 +236,7 @@
     return 0;
   }
 
-  size_t fetch_size = fd->block_size;
+  uint32_t fetch_size = fd->block_size;
   if (block * fd->block_size + fetch_size > fd->file_size) {
     // If we're reading the last (partial) block of the file, expect a shorter response from the
     // host, and pad the rest of the block with zeroes.
@@ -244,8 +244,9 @@
     memset(fd->block_data + fetch_size, 0, fd->block_size - fetch_size);
   }
 
-  int result = fd->vtab.read_block(block, fd->block_data, fetch_size);
-  if (result < 0) return result;
+  if (!fd->provider->ReadBlockAlignedData(fd->block_data, fetch_size, block)) {
+    return -EIO;
+  }
 
   fd->curr_block = block;
 
@@ -340,12 +341,14 @@
   return NO_STATUS;
 }
 
-int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
-                      const char* mount_point) {
+int run_fuse_sideload(std::unique_ptr<FuseDataProvider>&& provider, const char* mount_point) {
   // If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a
   // previous abnormal exit.)
   umount2(mount_point, MNT_FORCE);
 
+  uint64_t file_size = provider->file_size();
+  uint32_t block_size = provider->fuse_block_size();
+
   // fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read.
   if (block_size < 4096) {
     fprintf(stderr, "block size (%u) is too small\n", block_size);
@@ -357,7 +360,7 @@
   }
 
   fuse_data fd = {};
-  fd.vtab = vtab;
+  fd.provider = provider.get();
   fd.file_size = file_size;
   fd.block_size = block_size;
   fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1);
@@ -389,7 +392,7 @@
   }
 
   fd.ffd.reset(open("/dev/fuse", O_RDWR));
-  if (!fd.ffd) {
+  if (fd.ffd == -1) {
     perror("open /dev/fuse");
     result = -1;
     goto done;
@@ -479,7 +482,7 @@
   }
 
 done:
-  fd.vtab.close();
+  provider->Close();
 
   if (umount2(mount_point, MNT_DETACH) == -1) {
     fprintf(stderr, "fuse_sideload umount failed: %s\n", strerror(errno));
diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h
new file mode 100644
index 0000000..59059cf
--- /dev/null
+++ b/fuse_sideload/include/fuse_provider.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+// This is the base class to read data from source and provide the data to FUSE.
+class FuseDataProvider {
+ public:
+  FuseDataProvider(uint64_t file_size, uint32_t block_size)
+      : file_size_(file_size), fuse_block_size_(block_size) {}
+
+  virtual ~FuseDataProvider() = default;
+
+  uint64_t file_size() const {
+    return file_size_;
+  }
+  uint32_t fuse_block_size() const {
+    return fuse_block_size_;
+  }
+
+  // Reads |fetch_size| bytes data starting from |start_block|. Puts the result in |buffer|.
+  virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+                                    uint32_t start_block) const = 0;
+
+  virtual void Close() {}
+
+ protected:
+  FuseDataProvider() = default;
+
+  // Size in bytes of the file to read.
+  uint64_t file_size_ = 0;
+  // Block size passed to the fuse, this is different from the block size of the block device.
+  uint32_t fuse_block_size_ = 0;
+};
+
+// This class reads data from a file.
+class FuseFileDataProvider : public FuseDataProvider {
+ public:
+  FuseFileDataProvider(const std::string& path, uint32_t block_size);
+
+  bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+                            uint32_t start_block) const override;
+
+  bool Valid() const {
+    return fd_ != -1;
+  }
+
+  void Close() override;
+
+ private:
+  // The underlying source to read data from.
+  android::base::unique_fd fd_;
+};
diff --git a/fuse_sideload/include/fuse_sideload.h b/fuse_sideload/include/fuse_sideload.h
index 1b34cbd..1b7759a 100644
--- a/fuse_sideload/include/fuse_sideload.h
+++ b/fuse_sideload/include/fuse_sideload.h
@@ -17,7 +17,9 @@
 #ifndef __FUSE_SIDELOAD_H
 #define __FUSE_SIDELOAD_H
 
-#include <functional>
+#include <memory>
+
+#include "fuse_provider.h"
 
 // Define the filenames created by the sideload FUSE filesystem.
 static constexpr const char* FUSE_SIDELOAD_HOST_MOUNTPOINT = "/sideload";
@@ -26,15 +28,7 @@
 static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_FLAG = "exit";
 static constexpr const char* FUSE_SIDELOAD_HOST_EXIT_PATHNAME = "/sideload/exit";
 
-struct provider_vtab {
-  // read a block
-  std::function<int(uint32_t block, uint8_t* buffer, uint32_t fetch_size)> read_block;
-
-  // close down
-  std::function<void(void)> close;
-};
-
-int run_fuse_sideload(const provider_vtab& vtab, uint64_t file_size, uint32_t block_size,
+int run_fuse_sideload(std::unique_ptr<FuseDataProvider>&& provider,
                       const char* mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT);
 
 #endif
diff --git a/install/Android.bp b/install/Android.bp
new file mode 100644
index 0000000..b18e290
--- /dev/null
+++ b/install/Android.bp
@@ -0,0 +1,82 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+    name: "libinstall_defaults",
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    header_libs: [
+        "libminadbd_headers",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libbootloader_message",
+        "libcrypto",
+        "libext4_utils",
+        "libfs_mgr",
+        "libfusesideload",
+        "libhidl-gen-utils",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libselinux",
+        "libtinyxml2",
+        "libutils",
+        "libz",
+        "libziparchive",
+    ],
+
+    static_libs: [
+        "libotautil",
+
+        // external dependencies
+        "libvintf_recovery",
+        "libvintf",
+    ],
+}
+
+cc_library_static {
+    name: "libinstall",
+    recovery_available: true,
+
+    defaults: [
+        "libinstall_defaults",
+    ],
+
+    srcs: [
+        "adb_install.cpp",
+        "asn1_decoder.cpp",
+        "fuse_sdcard_install.cpp",
+        "install.cpp",
+        "package.cpp",
+        "verifier.cpp",
+        "wipe_data.cpp",
+    ],
+
+    shared_libs: [
+        "librecovery_ui",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "librecovery_ui",
+    ],
+}
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
new file mode 100644
index 0000000..548b6e5
--- /dev/null
+++ b/install/adb_install.cpp
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "install/adb_install.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/epoll.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <atomic>
+#include <functional>
+#include <map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/properties.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include "fuse_sideload.h"
+#include "install/install.h"
+#include "minadbd_types.h"
+#include "recovery_ui/ui.h"
+
+using CommandFunction = std::function<bool()>;
+
+static bool SetUsbConfig(const std::string& state) {
+  android::base::SetProperty("sys.usb.config", state);
+  return android::base::WaitForProperty("sys.usb.state", state);
+}
+
+// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors.
+static MinadbdCommands ParseMinadbdCommands(const std::string& message) {
+  if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) {
+    LOG(ERROR) << "Failed to parse command in message " << message;
+    return MinadbdCommands::kError;
+  }
+
+  auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix));
+  auto cmd_code = android::base::get_unaligned<uint32_t>(cmd_code_string.c_str());
+  if (cmd_code >= static_cast<uint32_t>(MinadbdCommands::kError)) {
+    LOG(ERROR) << "Unsupported command code: " << cmd_code;
+    return MinadbdCommands::kError;
+  }
+
+  return static_cast<MinadbdCommands>(cmd_code);
+}
+
+static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) {
+  char message[kMinadbdMessageSize];
+  memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix));
+  android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status);
+
+  if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to write message " << message;
+    return false;
+  }
+  return true;
+}
+
+// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise.
+static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) {
+  // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long
+  // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME
+  // will start to exist once the host connects and starts serving a package. Poll for its
+  // appearance. (Note that inotify doesn't work with FUSE.)
+  constexpr int ADB_INSTALL_TIMEOUT = 15;
+  *result = INSTALL_ERROR;
+  for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) {
+    struct stat st;
+    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) {
+      if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) {
+        sleep(1);
+        continue;
+      } else {
+        ui->Print("\nTimed out waiting for fuse to be ready.\n\n");
+        break;
+      }
+    }
+    *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui);
+    break;
+  }
+
+  // Calling stat() on this magic filename signals the FUSE to exit.
+  struct stat st;
+  stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st);
+  return *result == INSTALL_SUCCESS;
+}
+
+// Parses and executes the command from minadbd. Returns false if we enter an invalid state so that
+// the caller can kill the minadbd service properly.
+static bool HandleMessageFromMinadbd(
+    int socket_fd, const std::map<MinadbdCommands, CommandFunction>& command_map) {
+  char buffer[kMinadbdMessageSize];
+  if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to read message from minadbd";
+    return false;
+  }
+
+  std::string message(buffer, buffer + kMinadbdMessageSize);
+  auto command_type = ParseMinadbdCommands(message);
+  if (command_type == MinadbdCommands::kError) {
+    return false;
+  }
+  if (command_map.find(command_type) == command_map.end()) {
+    LOG(ERROR) << "Unsupported command: "
+               << android::base::get_unaligned<unsigned int>(
+                      message.substr(strlen(kMinadbdCommandPrefix)).c_str());
+    return false;
+  }
+
+  // We have received a valid command, execute the corresponding function.
+  const auto& command_func = command_map.at(command_type);
+  if (!command_func()) {
+    LOG(ERROR) << "Failed to execute command " << static_cast<unsigned int>(command_type);
+    return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd);
+  }
+  return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd);
+}
+
+// TODO(xunchang) add a wrapper function and kill the minadbd service there.
+static void ListenAndExecuteMinadbdCommands(
+    pid_t minadbd_pid, android::base::unique_fd&& socket_fd,
+    const std::map<MinadbdCommands, CommandFunction>& command_map) {
+  android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC));
+  if (epoll_fd == -1) {
+    PLOG(ERROR) << "Failed to create epoll";
+    kill(minadbd_pid, SIGKILL);
+    return;
+  }
+
+  constexpr int EPOLL_MAX_EVENTS = 10;
+  struct epoll_event ev = {};
+  ev.events = EPOLLIN | EPOLLHUP;
+  ev.data.fd = socket_fd.get();
+  struct epoll_event events[EPOLL_MAX_EVENTS];
+  if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) {
+    PLOG(ERROR) << "Failed to add socket fd to epoll";
+    kill(minadbd_pid, SIGKILL);
+    return;
+  }
+
+  // Set the timeout to be 300s when waiting for minadbd commands.
+  constexpr int TIMEOUT_MILLIS = 300 * 1000;
+  while (true) {
+    // Poll for the status change of the socket_fd, and handle the message if the fd is ready to
+    // read.
+    int event_count =
+        TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS));
+    if (event_count == -1) {
+      PLOG(ERROR) << "Failed to wait for epoll events";
+      kill(minadbd_pid, SIGKILL);
+      return;
+    }
+    if (event_count == 0) {
+      LOG(ERROR) << "Timeout waiting for messages from minadbd";
+      kill(minadbd_pid, SIGKILL);
+      return;
+    }
+
+    for (int n = 0; n < event_count; n++) {
+      if (events[n].events & EPOLLHUP) {
+        LOG(INFO) << "Socket has been closed";
+        kill(minadbd_pid, SIGKILL);
+        return;
+      }
+      if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) {
+        kill(minadbd_pid, SIGKILL);
+        return;
+      }
+    }
+  }
+}
+
+// Recovery starts minadbd service as a child process, and spawns another thread to listen for the
+// message from minadbd through a socket pair. Here is an example to execute one command from adb
+// host.
+//  a. recovery                    b. listener thread               c. minadbd service
+//
+//  a1. create socket pair
+//  a2. fork minadbd service
+//                                                                c3. wait for the adb commands
+//                                                                    from host
+//                                                                c4. after receiving host commands:
+//                                                                  1) set up pre-condition (i.e.
+//                                                                     start fuse for adb sideload)
+//                                                                  2) issue command through
+//                                                                     socket.
+//                                                                  3) wait for result
+//  a5. start listener thread
+//                               b6. listen for message from
+//                                   minadbd in a loop.
+//                               b7. After receiving a minadbd
+//                                   command from socket
+//                                 1) execute the command function
+//                                 2) send the result back to
+//                                    minadbd
+//  ......
+//                                                                 c8. exit upon receiving the
+//                                                                     result
+// a9.  wait for listener thread
+//      to exit.
+//
+// a10. wait for minadbd to
+//      exit
+//                               b11. exit the listening loop
+//
+static void CreateMinadbdServiceAndExecuteCommands(
+    const std::map<MinadbdCommands, CommandFunction>& command_map) {
+  signal(SIGPIPE, SIG_IGN);
+
+  android::base::unique_fd recovery_socket;
+  android::base::unique_fd minadbd_socket;
+  if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) {
+    PLOG(ERROR) << "Failed to create socket";
+    return;
+  }
+
+  pid_t child = fork();
+  if (child == -1) {
+    PLOG(ERROR) << "Failed to fork child process";
+    return;
+  }
+  if (child == 0) {
+    recovery_socket.reset();
+    execl("/system/bin/minadbd", "minadbd", "--socket_fd",
+          std::to_string(minadbd_socket.release()).c_str(), nullptr);
+
+    _exit(EXIT_FAILURE);
+  }
+
+  minadbd_socket.reset();
+
+  // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for
+  // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon.
+  if (!SetUsbConfig("sideload")) {
+    LOG(ERROR) << "Failed to set usb config to sideload";
+    return;
+  }
+
+  std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket),
+                              std::ref(command_map));
+
+  if (listener_thread.joinable()) {
+    listener_thread.join();
+  }
+
+  int status;
+  waitpid(child, &status, 0);
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+    if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) {
+      LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n";
+    } else if (!WIFSIGNALED(status)) {
+      LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")";
+    }
+  }
+
+  signal(SIGPIPE, SIG_DFL);
+}
+
+int apply_from_adb(RecoveryUI* ui) {
+  // Save the usb state to restore after the sideload operation.
+  std::string usb_state = android::base::GetProperty("sys.usb.state", "none");
+  // Clean up state and stop adbd.
+  if (usb_state != "none" && !SetUsbConfig("none")) {
+    LOG(ERROR) << "Failed to clear USB config";
+    return INSTALL_ERROR;
+  }
+
+  ui->Print(
+      "\n\nNow send the package you want to apply\n"
+      "to the device with \"adb sideload <filename>\"...\n");
+
+  int install_result = INSTALL_ERROR;
+  std::map<MinadbdCommands, CommandFunction> command_map{
+    { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) },
+  };
+
+  CreateMinadbdServiceAndExecuteCommands(command_map);
+
+  // Clean up before switching to the older state, for example setting the state
+  // to none sets sys/class/android_usb/android0/enable to 0.
+  if (!SetUsbConfig("none")) {
+    LOG(ERROR) << "Failed to clear USB config";
+  }
+
+  if (usb_state != "none") {
+    if (!SetUsbConfig(usb_state)) {
+      LOG(ERROR) << "Failed to set USB config to " << usb_state;
+    }
+  }
+
+  return install_result;
+}
diff --git a/asn1_decoder.cpp b/install/asn1_decoder.cpp
similarity index 98%
rename from asn1_decoder.cpp
rename to install/asn1_decoder.cpp
index 285214f..2d81a6e 100644
--- a/asn1_decoder.cpp
+++ b/install/asn1_decoder.cpp
@@ -14,9 +14,7 @@
  * limitations under the License.
  */
 
-#include "asn1_decoder.h"
-
-#include <stdint.h>
+#include "private/asn1_decoder.h"
 
 int asn1_context::peek_byte() const {
   if (length_ == 0) {
diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp
new file mode 100644
index 0000000..1aa8768
--- /dev/null
+++ b/install/fuse_sdcard_install.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "install/fuse_sdcard_install.h"
+
+#include <dirent.h>
+#include <signal.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include "bootloader_message/bootloader_message.h"
+#include "fuse_provider.h"
+#include "fuse_sideload.h"
+#include "install/install.h"
+#include "otautil/roots.h"
+
+static constexpr const char* SDCARD_ROOT = "/sdcard";
+// How long (in seconds) we wait for the fuse-provided package file to
+// appear, before timing out.
+static constexpr int SDCARD_INSTALL_TIMEOUT = 10;
+
+// Set the BCB to reboot back into recovery (it won't resume the install from
+// sdcard though).
+static void SetSdcardUpdateBootloaderMessage() {
+  std::vector<std::string> options;
+  std::string err;
+  if (!update_bootloader_message(options, &err)) {
+    LOG(ERROR) << "Failed to set BCB message: " << err;
+  }
+}
+
+// Returns the selected filename, or an empty string.
+static std::string BrowseDirectory(const std::string& path, Device* device, RecoveryUI* ui) {
+  ensure_path_mounted(path);
+
+  std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
+  if (!d) {
+    PLOG(ERROR) << "error opening " << path;
+    return "";
+  }
+
+  std::vector<std::string> dirs;
+  std::vector<std::string> entries{ "../" };  // "../" is always the first entry.
+
+  dirent* de;
+  while ((de = readdir(d.get())) != nullptr) {
+    std::string name(de->d_name);
+
+    if (de->d_type == DT_DIR) {
+      // Skip "." and ".." entries.
+      if (name == "." || name == "..") continue;
+      dirs.push_back(name + "/");
+    } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
+      entries.push_back(name);
+    }
+  }
+
+  std::sort(dirs.begin(), dirs.end());
+  std::sort(entries.begin(), entries.end());
+
+  // Append dirs to the entries list.
+  entries.insert(entries.end(), dirs.begin(), dirs.end());
+
+  std::vector<std::string> headers{ "Choose a package to install:", path };
+
+  size_t chosen_item = 0;
+  while (true) {
+    chosen_item = ui->ShowMenu(
+        headers, entries, chosen_item, true,
+        std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
+
+    // Return if WaitKey() was interrupted.
+    if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
+      return "";
+    }
+
+    const std::string& item = entries[chosen_item];
+    if (chosen_item == 0) {
+      // Go up but continue browsing (if the caller is BrowseDirectory).
+      return "";
+    }
+
+    std::string new_path = path + "/" + item;
+    if (new_path.back() == '/') {
+      // Recurse down into a subdirectory.
+      new_path.pop_back();
+      std::string result = BrowseDirectory(new_path, device, ui);
+      if (!result.empty()) return result;
+    } else {
+      // Selected a zip file: return the path to the caller.
+      return new_path;
+    }
+  }
+
+  // Unreachable.
+}
+
+static bool StartSdcardFuse(const std::string& path) {
+  auto file_data_reader = std::make_unique<FuseFileDataProvider>(path, 65536);
+
+  if (!file_data_reader->Valid()) {
+    return false;
+  }
+
+  // The installation process expects to find the sdcard unmounted. Unmount it with MNT_DETACH so
+  // that our open file continues to work but new references see it as unmounted.
+  umount2("/sdcard", MNT_DETACH);
+
+  return run_fuse_sideload(std::move(file_data_reader)) == 0;
+}
+
+int ApplyFromSdcard(Device* device, RecoveryUI* ui) {
+  if (ensure_path_mounted(SDCARD_ROOT) != 0) {
+    LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n";
+    return INSTALL_ERROR;
+  }
+
+  std::string path = BrowseDirectory(SDCARD_ROOT, device, ui);
+  if (path.empty()) {
+    LOG(ERROR) << "\n-- No package file selected.\n";
+    ensure_path_unmounted(SDCARD_ROOT);
+    return INSTALL_ERROR;
+  }
+
+  ui->Print("\n-- Install %s ...\n", path.c_str());
+  SetSdcardUpdateBootloaderMessage();
+
+  // We used to use fuse in a thread as opposed to a process. Since accessing
+  // through fuse involves going from kernel to userspace to kernel, it leads
+  // to deadlock when a page fault occurs. (Bug: 26313124)
+  pid_t child;
+  if ((child = fork()) == 0) {
+    bool status = StartSdcardFuse(path);
+
+    _exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
+  }
+
+  // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child
+  // process is ready.
+  int result = INSTALL_ERROR;
+  int status;
+  bool waited = false;
+  for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
+    if (waitpid(child, &status, WNOHANG) == -1) {
+      result = INSTALL_ERROR;
+      waited = true;
+      break;
+    }
+
+    struct stat sb;
+    if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
+      if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT - 1) {
+        sleep(1);
+        continue;
+      } else {
+        LOG(ERROR) << "Timed out waiting for the fuse-provided package.";
+        result = INSTALL_ERROR;
+        kill(child, SIGKILL);
+        break;
+      }
+    }
+
+    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui);
+    break;
+  }
+
+  if (!waited) {
+    // Calling stat() on this magic filename signals the fuse
+    // filesystem to shut down.
+    struct stat sb;
+    stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb);
+
+    waitpid(child, &status, 0);
+  }
+
+  if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+    LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
+  }
+
+  ensure_path_unmounted(SDCARD_ROOT);
+  return result;
+}
diff --git a/adb_install.h b/install/include/install/adb_install.h
similarity index 86%
rename from adb_install.h
rename to install/include/install/adb_install.h
index cc3ca26..f7b065b 100644
--- a/adb_install.h
+++ b/install/include/install/adb_install.h
@@ -14,9 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef _ADB_INSTALL_H
-#define _ADB_INSTALL_H
+#pragma once
 
-int apply_from_adb(bool* wipe_cache);
+#include <recovery_ui/ui.h>
 
-#endif
+int apply_from_adb(RecoveryUI* ui);
diff --git a/default_device.cpp b/install/include/install/fuse_sdcard_install.h
similarity index 75%
copy from default_device.cpp
copy to install/include/install/fuse_sdcard_install.h
index a971866..d9214ca 100644
--- a/default_device.cpp
+++ b/install/include/install/fuse_sdcard_install.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-#include "device.h"
-#include "screen_ui.h"
+#pragma once
 
-Device* make_device() {
-  return new Device(new ScreenRecoveryUI);
-}
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+int ApplyFromSdcard(Device* device, RecoveryUI* ui);
diff --git a/install.h b/install/include/install/install.h
similarity index 68%
rename from install.h
rename to install/include/install/install.h
index c6db1d1..1e41b48 100644
--- a/install.h
+++ b/install/include/install/install.h
@@ -14,16 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef RECOVERY_INSTALL_H_
-#define RECOVERY_INSTALL_H_
+#pragma once
 
 #include <stddef.h>
 
 #include <map>
 #include <string>
+#include <vector>
 
 #include <ziparchive/zip_archive.h>
 
+#include "package.h"
+#include "recovery_ui/ui.h"
+
 enum InstallResult {
   INSTALL_SUCCESS,
   INSTALL_ERROR,
@@ -40,19 +43,23 @@
   BRICK,
 };
 
-// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on
-// exit, caller should wipe the cache partition.
-int install_package(const std::string& package, bool* wipe_cache, bool needs_mount,
-                    int retry_count);
+// Installs the given update package. This function should also wipe the cache partition after a
+// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the
+// cache.
+int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount,
+                    int retry_count, RecoveryUI* ui);
 
-// Verify the package by ota keys. Return true if the package is verified successfully,
-// otherwise return false.
-bool verify_package(const unsigned char* package_data, size_t package_size);
+// Verifies the package by ota keys. Returns true if the package is verified successfully,
+// otherwise returns false.
+bool verify_package(Package* package, RecoveryUI* ui);
 
 // Reads meta data file of the package; parses each line in the format "key=value"; and writes the
 // result to |metadata|. Return true if succeed, otherwise return false.
 bool ReadMetadataFromPackage(ZipArchiveHandle zip, std::map<std::string, std::string>* metadata);
 
+// Reads the "recovery.wipe" entry in the zip archive returns a list of partitions to wipe.
+std::vector<std::string> GetWipePartitionList(Package* wipe_package);
+
 // Verifies the compatibility info in a Treble-compatible package. Returns true directly if the
 // entry doesn't exist.
 bool verify_package_compatibility(ZipArchiveHandle package_zip);
@@ -61,5 +68,3 @@
 // Mandatory checks: ota-type, pre-device and serial number(if presents)
 // AB OTA specific checks: pre-build version, fingerprint, timestamp.
 int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type);
-
-#endif  // RECOVERY_INSTALL_H_
diff --git a/install/include/install/package.h b/install/include/install/package.h
new file mode 100644
index 0000000..cd44d10
--- /dev/null
+++ b/install/include/install/package.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <ziparchive/zip_archive.h>
+
+#include "verifier.h"
+
+// This class serves as a wrapper for an OTA update package. It aims to provide the common
+// interface for both packages loaded in memory and packages read from fd.
+class Package : public VerifierInterface {
+ public:
+  static std::unique_ptr<Package> CreateMemoryPackage(
+      const std::string& path, const std::function<void(float)>& set_progress);
+  static std::unique_ptr<Package> CreateMemoryPackage(
+      std::vector<uint8_t> content, const std::function<void(float)>& set_progress);
+  static std::unique_ptr<Package> CreateFilePackage(const std::string& path,
+                                                    const std::function<void(float)>& set_progress);
+
+  virtual ~Package() = default;
+
+  // Opens the package as a zip file and returns the ZipArchiveHandle.
+  virtual ZipArchiveHandle GetZipArchiveHandle() = 0;
+
+  // Updates the progress in fraction during package verification.
+  void SetProgress(float progress) override;
+
+ protected:
+  // An optional function to update the progress.
+  std::function<void(float)> set_progress_;
+};
diff --git a/install/include/install/verifier.h b/install/include/install/verifier.h
new file mode 100644
index 0000000..f9e9475
--- /dev/null
+++ b/install/include/install/verifier.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include <openssl/ec_key.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+constexpr size_t MiB = 1024 * 1024;
+
+using HasherUpdateCallback = std::function<void(const uint8_t* addr, uint64_t size)>;
+
+struct RSADeleter {
+  void operator()(RSA* rsa) const {
+    RSA_free(rsa);
+  }
+};
+
+struct ECKEYDeleter {
+  void operator()(EC_KEY* ec_key) const {
+    EC_KEY_free(ec_key);
+  }
+};
+
+struct Certificate {
+  typedef enum {
+    KEY_TYPE_RSA,
+    KEY_TYPE_EC,
+  } KeyType;
+
+  Certificate(int hash_len_, KeyType key_type_, std::unique_ptr<RSA, RSADeleter>&& rsa_,
+              std::unique_ptr<EC_KEY, ECKEYDeleter>&& ec_)
+      : hash_len(hash_len_), key_type(key_type_), rsa(std::move(rsa_)), ec(std::move(ec_)) {}
+
+  // SHA_DIGEST_LENGTH (SHA-1) or SHA256_DIGEST_LENGTH (SHA-256)
+  int hash_len;
+  KeyType key_type;
+  std::unique_ptr<RSA, RSADeleter> rsa;
+  std::unique_ptr<EC_KEY, ECKEYDeleter> ec;
+};
+
+class VerifierInterface {
+ public:
+  virtual ~VerifierInterface() = default;
+
+  // Returns the package size in bytes.
+  virtual uint64_t GetPackageSize() const = 0;
+
+  // Reads |byte_count| data starting from |offset|, and puts the result in |buffer|.
+  virtual bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) = 0;
+
+  // Updates the hash contexts for |length| bytes data starting from |start|.
+  virtual bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+                                  uint64_t length) = 0;
+
+  // Updates the progress in fraction during package verification.
+  virtual void SetProgress(float progress) = 0;
+};
+
+//  Looks for an RSA signature embedded in the .ZIP file comment given the path to the zip.
+//  Verifies that it matches one of the given public keys. Returns VERIFY_SUCCESS or
+//  VERIFY_FAILURE (if any error is encountered or no key matches the signature).
+int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys);
+
+// Checks that the RSA key has a modulus of 2048 or 4096 bits long, and public exponent is 3 or
+// 65537.
+bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa);
+
+// Checks that the field size of the curve for the EC key is 256 bits.
+bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key);
+
+// Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns
+// false if there is a parsing failure or the signature's encryption algorithm is not supported.
+bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert);
+
+// Iterates over the zip entries with the suffix "x509.pem" and returns a list of recognized
+// certificates. Returns an empty list if we fail to parse any of the entries.
+std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name);
+
+#define VERIFY_SUCCESS 0
+#define VERIFY_FAILURE 1
diff --git a/default_device.cpp b/install/include/install/wipe_data.h
similarity index 61%
copy from default_device.cpp
copy to install/include/install/wipe_data.h
index a971866..b34891f 100644
--- a/default_device.cpp
+++ b/install/include/install/wipe_data.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,9 +14,17 @@
  * limitations under the License.
  */
 
-#include "device.h"
-#include "screen_ui.h"
+#pragma once
 
-Device* make_device() {
-  return new Device(new ScreenRecoveryUI);
-}
+#include <functional>
+
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
+
+struct selabel_handle;
+
+// Returns true on success.
+bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm);
+
+// Returns true on success.
+bool WipeData(Device* device, bool convert_fbe);
diff --git a/asn1_decoder.h b/install/include/private/asn1_decoder.h
similarity index 98%
rename from asn1_decoder.h
rename to install/include/private/asn1_decoder.h
index 3e99211..e5337d9 100644
--- a/asn1_decoder.h
+++ b/install/include/private/asn1_decoder.h
@@ -17,6 +17,7 @@
 #ifndef ASN1_DECODER_H_
 #define ASN1_DECODER_H_
 
+#include <stddef.h>
 #include <stdint.h>
 
 class asn1_context {
diff --git a/private/install.h b/install/include/private/setup_commands.h
similarity index 100%
rename from private/install.h
rename to install/include/private/setup_commands.h
diff --git a/install.cpp b/install/install.cpp
similarity index 85%
rename from install.cpp
rename to install/install.cpp
index 9d8943f..e2d4700 100644
--- a/install.cpp
+++ b/install/install.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "install.h"
+#include "install/install.h"
 
 #include <ctype.h>
 #include <errno.h>
@@ -46,18 +46,23 @@
 #include <android-base/unique_fd.h>
 #include <vintf/VintfObjectRecovery.h>
 
-#include "common.h"
+#include "install/package.h"
+#include "install/verifier.h"
+#include "install/wipe_data.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
 #include "otautil/thermalutil.h"
-#include "private/install.h"
-#include "roots.h"
-#include "ui.h"
-#include "verifier.h"
+#include "private/setup_commands.h"
+#include "recovery_ui/ui.h"
 
 using namespace std::chrono_literals;
 
+static constexpr int kRecoveryApiVersion = 3;
+// Assert the version defined in code and in Android.mk are consistent.
+static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
+
 // Default allocation of progress bar segments to operations
 static constexpr int VERIFICATION_PROGRESS_TIME = 60;
 static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25;
@@ -292,6 +297,11 @@
     return INSTALL_ERROR;
   }
 
+  // When executing the update binary contained in the package, the arguments passed are:
+  //   - the version number for this interface
+  //   - an FD to which the program can write in order to update the progress bar.
+  //   - the name of the package zip file.
+  //   - an optional argument "retry" if this update is a retry of a failed update attempt.
   *cmd = {
     binary_path,
     std::to_string(kRecoveryApiVersion),
@@ -317,7 +327,7 @@
 // If the package contains an update binary, extract it and run it.
 static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache,
                              std::vector<std::string>* log_buffer, int retry_count,
-                             int* max_temperature) {
+                             int* max_temperature, RecoveryUI* ui) {
   std::map<std::string, std::string> metadata;
   if (!ReadMetadataFromPackage(zip, &metadata)) {
     LOG(ERROR) << "Failed to parse metadata in the zip file";
@@ -334,75 +344,58 @@
 
   ReadSourceTargetBuild(metadata, log_buffer);
 
-  int pipefd[2];
-  pipe(pipefd);
+  // The updater in child process writes to the pipe to communicate with recovery.
+  android::base::unique_fd pipe_read, pipe_write;
+  // Explicitly disable O_CLOEXEC using 0 as the flags (last) parameter to Pipe
+  // so that the child updater process will recieve a non-closed fd.
+  if (!android::base::Pipe(&pipe_read, &pipe_write, 0)) {
+    PLOG(ERROR) << "Failed to create pipe for updater-recovery communication";
+    return INSTALL_CORRUPT;
+  }
+
+  // The updater-recovery communication protocol.
+  //
+  //   progress <frac> <secs>
+  //       fill up the next <frac> part of of the progress bar over <secs> seconds. If <secs> is
+  //       zero, use `set_progress` commands to manually control the progress of this segment of the
+  //       bar.
+  //
+  //   set_progress <frac>
+  //       <frac> should be between 0.0 and 1.0; sets the progress bar within the segment defined by
+  //       the most recent progress command.
+  //
+  //   ui_print <string>
+  //       display <string> on the screen.
+  //
+  //   wipe_cache
+  //       a wipe of cache will be performed following a successful installation.
+  //
+  //   clear_display
+  //       turn off the text display.
+  //
+  //   enable_reboot
+  //       packages can explicitly request that they want the user to be able to reboot during
+  //       installation (useful for debugging packages that don't exit).
+  //
+  //   retry_update
+  //       updater encounters some issue during the update. It requests a reboot to retry the same
+  //       package automatically.
+  //
+  //   log <string>
+  //       updater requests logging the string (e.g. cause of the failure).
+  //
+
   std::vector<std::string> args;
   if (int update_status =
-          is_ab ? SetUpAbUpdateCommands(package, zip, pipefd[1], &args)
-                : SetUpNonAbUpdateCommands(package, zip, retry_count, pipefd[1], &args);
+          is_ab ? SetUpAbUpdateCommands(package, zip, pipe_write.get(), &args)
+                : SetUpNonAbUpdateCommands(package, zip, retry_count, pipe_write.get(), &args);
       update_status != 0) {
-    close(pipefd[0]);
-    close(pipefd[1]);
     log_buffer->push_back(android::base::StringPrintf("error: %d", kUpdateBinaryCommandFailure));
     return update_status;
   }
 
-  // When executing the update binary contained in the package, the
-  // arguments passed are:
-  //
-  //   - the version number for this interface
-  //
-  //   - an FD to which the program can write in order to update the
-  //     progress bar.  The program can write single-line commands:
-  //
-  //        progress <frac> <secs>
-  //            fill up the next <frac> part of of the progress bar
-  //            over <secs> seconds.  If <secs> is zero, use
-  //            set_progress commands to manually control the
-  //            progress of this segment of the bar.
-  //
-  //        set_progress <frac>
-  //            <frac> should be between 0.0 and 1.0; sets the
-  //            progress bar within the segment defined by the most
-  //            recent progress command.
-  //
-  //        ui_print <string>
-  //            display <string> on the screen.
-  //
-  //        wipe_cache
-  //            a wipe of cache will be performed following a successful
-  //            installation.
-  //
-  //        clear_display
-  //            turn off the text display.
-  //
-  //        enable_reboot
-  //            packages can explicitly request that they want the user
-  //            to be able to reboot during installation (useful for
-  //            debugging packages that don't exit).
-  //
-  //        retry_update
-  //            updater encounters some issue during the update. It requests
-  //            a reboot to retry the same package automatically.
-  //
-  //        log <string>
-  //            updater requests logging the string (e.g. cause of the
-  //            failure).
-  //
-  //   - the name of the package zip file.
-  //
-  //   - an optional argument "retry" if this update is a retry of a failed
-  //   update attempt.
-  //
-
-  // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
-  auto chr_args = StringVectorToNullTerminatedArray(args);
-
   pid_t pid = fork();
-
   if (pid == -1) {
-    close(pipefd[0]);
-    close(pipefd[1]);
     PLOG(ERROR) << "Failed to fork update binary";
     log_buffer->push_back(android::base::StringPrintf("error: %d", kForkUpdateBinaryFailure));
     return INSTALL_ERROR;
@@ -410,16 +403,18 @@
 
   if (pid == 0) {
     umask(022);
-    close(pipefd[0]);
+    pipe_read.reset();
+
+    // Convert the std::string vector to a NULL-terminated char* vector suitable for execv.
+    auto chr_args = StringVectorToNullTerminatedArray(args);
     execv(chr_args[0], chr_args.data());
-    // Bug: 34769056
-    // We shouldn't use LOG/PLOG in the forked process, since they may cause
-    // the child process to hang. This deadlock results from an improperly
-    // copied mutex in the ui functions.
+    // We shouldn't use LOG/PLOG in the forked process, since they may cause the child process to
+    // hang. This deadlock results from an improperly copied mutex in the ui functions.
+    // (Bug: 34769056)
     fprintf(stdout, "E:Can't run %s (%s)\n", chr_args[0], strerror(errno));
     _exit(EXIT_FAILURE);
   }
-  close(pipefd[1]);
+  pipe_write.reset();
 
   std::atomic<bool> logger_finished(false);
   std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished));
@@ -428,7 +423,7 @@
   bool retry_update = false;
 
   char buffer[1024];
-  FILE* from_child = fdopen(pipefd[0], "r");
+  FILE* from_child = android::base::Fdopen(std::move(pipe_read), "r");
   while (fgets(buffer, sizeof(buffer), from_child) != nullptr) {
     std::string line(buffer);
     size_t space = line.find_first_of(" \n");
@@ -578,7 +573,7 @@
 
 static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount,
                                   std::vector<std::string>* log_buffer, int retry_count,
-                                  int* max_temperature) {
+                                  int* max_temperature, RecoveryUI* ui) {
   ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
   ui->Print("Finding update package...\n");
   // Give verification half the progress bar...
@@ -597,34 +592,29 @@
     }
   }
 
-  MemMapping map;
-  if (!map.MapFile(path)) {
-    LOG(ERROR) << "failed to map file";
+  auto package = Package::CreateMemoryPackage(
+      path, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+  if (!package) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kMapFileFailure));
     return INSTALL_CORRUPT;
   }
 
   // Verify package.
-  if (!verify_package(map.addr, map.length)) {
+  if (!verify_package(package.get(), ui)) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
     return INSTALL_CORRUPT;
   }
 
   // Try to open the package.
-  ZipArchiveHandle zip;
-  int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip);
-  if (err != 0) {
-    LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err);
+  ZipArchiveHandle zip = package->GetZipArchiveHandle();
+  if (!zip) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure));
-
-    CloseArchive(zip);
     return INSTALL_CORRUPT;
   }
 
-  // Additionally verify the compatibility of the package.
-  if (!verify_package_compatibility(zip)) {
+  // Additionally verify the compatibility of the package if it's a fresh install.
+  if (retry_count == 0 && !verify_package_compatibility(zip)) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure));
-    CloseArchive(zip);
     return INSTALL_CORRUPT;
   }
 
@@ -634,19 +624,18 @@
     ui->Print("Retry attempt: %d\n", retry_count);
   }
   ui->SetEnableReboot(false);
-  int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature);
+  int result =
+      try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature, ui);
   ui->SetEnableReboot(true);
   ui->Print("\n");
 
-  CloseArchive(zip);
   return result;
 }
 
-int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, int retry_count) {
+int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
+                    int retry_count, RecoveryUI* ui) {
   CHECK(!path.empty());
-  CHECK(wipe_cache != nullptr);
 
-  modified_flash = true;
   auto start = std::chrono::system_clock::now();
 
   int start_temperature = GetMaxValueFromThermalZone();
@@ -658,8 +647,10 @@
     LOG(ERROR) << "failed to set up expected mounts for install; aborting";
     result = INSTALL_ERROR;
   } else {
-    result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
-                                    &max_temperature);
+    bool updater_wipe_cache = false;
+    result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
+                                    retry_count, &max_temperature, ui);
+    should_wipe_cache = should_wipe_cache || updater_wipe_cache;
   }
 
   // Measure the time spent to apply OTA update in seconds.
@@ -714,10 +705,16 @@
   // Write a copy into last_log.
   LOG(INFO) << log_content;
 
+  if (result == INSTALL_SUCCESS && should_wipe_cache) {
+    if (!WipeCache(ui, nullptr)) {
+      result = INSTALL_ERROR;
+    }
+  }
+
   return result;
 }
 
-bool verify_package(const unsigned char* package_data, size_t package_size) {
+bool verify_package(Package* package, RecoveryUI* ui) {
   static constexpr const char* CERTIFICATE_ZIP_FILE = "/system/etc/security/otacerts.zip";
   std::vector<Certificate> loaded_keys = LoadKeysFromZipfile(CERTIFICATE_ZIP_FILE);
   if (loaded_keys.empty()) {
@@ -729,8 +726,7 @@
   // Verify package.
   ui->Print("Verifying update package...\n");
   auto t0 = std::chrono::system_clock::now();
-  int err = verify_file(package_data, package_size, loaded_keys,
-                        std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+  int err = verify_file(package, loaded_keys);
   std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0;
   ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err);
   if (err != VERIFY_SUCCESS) {
diff --git a/install/package.cpp b/install/package.cpp
new file mode 100644
index 0000000..4402f48
--- /dev/null
+++ b/install/package.cpp
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "install/package.h"
+
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+
+#include "otautil/error_code.h"
+#include "otautil/sysutil.h"
+
+// This class wraps the package in memory, i.e. a memory mapped package, or a package loaded
+// to a string/vector.
+class MemoryPackage : public Package {
+ public:
+  // Constructs the class from a file. We will memory maps the file later.
+  MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map,
+                const std::function<void(float)>& set_progress);
+
+  // Constructs the class from the package bytes in |content|.
+  MemoryPackage(std::vector<uint8_t> content, const std::function<void(float)>& set_progress);
+
+  ~MemoryPackage() override;
+
+  // Memory maps the package file if necessary. Initializes the start address and size of the
+  // package.
+  uint64_t GetPackageSize() const override {
+    return package_size_;
+  }
+
+  bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
+
+  ZipArchiveHandle GetZipArchiveHandle() override;
+
+  bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+                          uint64_t length) override;
+
+ private:
+  const uint8_t* addr_;    // Start address of the package in memory.
+  uint64_t package_size_;  // Package size in bytes.
+
+  // The memory mapped package.
+  std::unique_ptr<MemMapping> map_;
+  // A copy of the package content, valid only if we create the class with the exact bytes of
+  // the package.
+  std::vector<uint8_t> package_content_;
+  // The physical path to the package, empty if we create the class with the package content.
+  std::string path_;
+
+  // The ZipArchiveHandle of the package.
+  ZipArchiveHandle zip_handle_;
+};
+
+void Package::SetProgress(float progress) {
+  if (set_progress_) {
+    set_progress_(progress);
+  }
+}
+
+class FilePackage : public Package {
+ public:
+  FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
+              const std::function<void(float)>& set_progress);
+
+  ~FilePackage() override;
+
+  uint64_t GetPackageSize() const override {
+    return package_size_;
+  }
+
+  bool ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) override;
+
+  ZipArchiveHandle GetZipArchiveHandle() override;
+
+  bool UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers, uint64_t start,
+                          uint64_t length) override;
+
+ private:
+  android::base::unique_fd fd_;  // The underlying fd to the open package.
+  uint64_t package_size_;
+  std::string path_;  // The physical path to the package.
+
+  ZipArchiveHandle zip_handle_;
+};
+
+std::unique_ptr<Package> Package::CreateMemoryPackage(
+    const std::string& path, const std::function<void(float)>& set_progress) {
+  std::unique_ptr<MemMapping> mmap = std::make_unique<MemMapping>();
+  if (!mmap->MapFile(path)) {
+    LOG(ERROR) << "failed to map file";
+    return nullptr;
+  }
+
+  return std::make_unique<MemoryPackage>(path, std::move(mmap), set_progress);
+}
+
+std::unique_ptr<Package> Package::CreateFilePackage(
+    const std::string& path, const std::function<void(float)>& set_progress) {
+  android::base::unique_fd fd(open(path.c_str(), O_RDONLY));
+  if (fd == -1) {
+    PLOG(ERROR) << "Failed to open " << path;
+    return nullptr;
+  }
+
+  off64_t file_size = lseek64(fd.get(), 0, SEEK_END);
+  if (file_size == -1) {
+    PLOG(ERROR) << "Failed to get the package size";
+    return nullptr;
+  }
+
+  return std::make_unique<FilePackage>(std::move(fd), file_size, path, set_progress);
+}
+
+std::unique_ptr<Package> Package::CreateMemoryPackage(
+    std::vector<uint8_t> content, const std::function<void(float)>& set_progress) {
+  return std::make_unique<MemoryPackage>(std::move(content), set_progress);
+}
+
+MemoryPackage::MemoryPackage(const std::string& path, std::unique_ptr<MemMapping> map,
+                             const std::function<void(float)>& set_progress)
+    : map_(std::move(map)), path_(path), zip_handle_(nullptr) {
+  addr_ = map_->addr;
+  package_size_ = map_->length;
+  set_progress_ = set_progress;
+}
+
+MemoryPackage::MemoryPackage(std::vector<uint8_t> content,
+                             const std::function<void(float)>& set_progress)
+    : package_content_(std::move(content)), zip_handle_(nullptr) {
+  CHECK(!package_content_.empty());
+  addr_ = package_content_.data();
+  package_size_ = package_content_.size();
+  set_progress_ = set_progress;
+}
+
+MemoryPackage::~MemoryPackage() {
+  if (zip_handle_) {
+    CloseArchive(zip_handle_);
+  }
+}
+
+bool MemoryPackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
+  if (byte_count > package_size_ || offset > package_size_ - byte_count) {
+    LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+  memcpy(buffer, addr_ + offset, byte_count);
+  return true;
+}
+
+bool MemoryPackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
+                                       uint64_t start, uint64_t length) {
+  if (length > package_size_ || start > package_size_ - length) {
+    LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+
+  for (const auto& hasher : hashers) {
+    hasher(addr_ + start, length);
+  }
+  return true;
+}
+
+ZipArchiveHandle MemoryPackage::GetZipArchiveHandle() {
+  if (zip_handle_) {
+    return zip_handle_;
+  }
+
+  if (auto err = OpenArchiveFromMemory(const_cast<uint8_t*>(addr_), package_size_, path_.c_str(),
+                                       &zip_handle_);
+      err != 0) {
+    LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
+    return nullptr;
+  }
+
+  return zip_handle_;
+}
+
+FilePackage::FilePackage(android::base::unique_fd&& fd, uint64_t file_size, const std::string& path,
+                         const std::function<void(float)>& set_progress)
+    : fd_(std::move(fd)), package_size_(file_size), path_(path), zip_handle_(nullptr) {
+  set_progress_ = set_progress;
+}
+
+FilePackage::~FilePackage() {
+  if (zip_handle_) {
+    CloseArchive(zip_handle_);
+  }
+}
+
+bool FilePackage::ReadFullyAtOffset(uint8_t* buffer, uint64_t byte_count, uint64_t offset) {
+  if (byte_count > package_size_ || offset > package_size_ - byte_count) {
+    LOG(ERROR) << "Out of bound read, offset: " << offset << ", size: " << byte_count
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+
+  if (!android::base::ReadFullyAtOffset(fd_.get(), buffer, byte_count, offset)) {
+    PLOG(ERROR) << "Failed to read " << byte_count << " bytes data at offset " << offset;
+    return false;
+  }
+
+  return true;
+}
+
+bool FilePackage::UpdateHashAtOffset(const std::vector<HasherUpdateCallback>& hashers,
+                                     uint64_t start, uint64_t length) {
+  if (length > package_size_ || start > package_size_ - length) {
+    LOG(ERROR) << "Out of bound read, offset: " << start << ", size: " << length
+               << ", total package_size: " << package_size_;
+    return false;
+  }
+
+  uint64_t so_far = 0;
+  while (so_far < length) {
+    uint64_t read_size = std::min<uint64_t>(length - so_far, 16 * MiB);
+    std::vector<uint8_t> buffer(read_size);
+    if (!ReadFullyAtOffset(buffer.data(), read_size, start + so_far)) {
+      return false;
+    }
+
+    for (const auto& hasher : hashers) {
+      hasher(buffer.data(), read_size);
+    }
+    so_far += read_size;
+  }
+
+  return true;
+}
+
+ZipArchiveHandle FilePackage::GetZipArchiveHandle() {
+  if (zip_handle_) {
+    return zip_handle_;
+  }
+
+  if (auto err = OpenArchiveFd(fd_.get(), path_.c_str(), &zip_handle_); err != 0) {
+    LOG(ERROR) << "Can't open package" << path_ << " : " << ErrorCodeString(err);
+    return nullptr;
+  }
+
+  return zip_handle_;
+}
diff --git a/verifier.cpp b/install/verifier.cpp
similarity index 83%
rename from verifier.cpp
rename to install/verifier.cpp
index 44bd4e1..6ba1d77 100644
--- a/verifier.cpp
+++ b/install/verifier.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "verifier.h"
+#include "install/verifier.h"
 
 #include <errno.h>
 #include <stdio.h>
@@ -36,10 +36,8 @@
 #include <openssl/rsa.h>
 #include <ziparchive/zip_archive.h>
 
-#include "asn1_decoder.h"
 #include "otautil/print_sha1.h"
-
-static constexpr size_t MiB = 1024 * 1024;
+#include "private/asn1_decoder.h"
 
 /*
  * Simple version of PKCS#7 SignedData extraction. This extracts the
@@ -84,10 +82,8 @@
   }
 
   std::unique_ptr<asn1_context> signed_data_seq(signed_data_app->asn1_sequence_get());
-  if (signed_data_seq == nullptr ||
-      !signed_data_seq->asn1_sequence_next() ||
-      !signed_data_seq->asn1_sequence_next() ||
-      !signed_data_seq->asn1_sequence_next() ||
+  if (signed_data_seq == nullptr || !signed_data_seq->asn1_sequence_next() ||
+      !signed_data_seq->asn1_sequence_next() || !signed_data_seq->asn1_sequence_next() ||
       !signed_data_seq->asn1_constructed_skip_all()) {
     return false;
   }
@@ -98,11 +94,8 @@
   }
 
   std::unique_ptr<asn1_context> sig_seq(sig_set->asn1_sequence_get());
-  if (sig_seq == nullptr ||
-      !sig_seq->asn1_sequence_next() ||
-      !sig_seq->asn1_sequence_next() ||
-      !sig_seq->asn1_sequence_next() ||
-      !sig_seq->asn1_sequence_next()) {
+  if (sig_seq == nullptr || !sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next() ||
+      !sig_seq->asn1_sequence_next() || !sig_seq->asn1_sequence_next()) {
     return false;
   }
 
@@ -117,19 +110,9 @@
   return true;
 }
 
-/*
- * Looks for an RSA signature embedded in the .ZIP file comment given the path to the zip. Verifies
- * that it matches one of the given public keys. A callback function can be optionally provided for
- * posting the progress.
- *
- * Returns VERIFY_SUCCESS or VERIFY_FAILURE (if any error is encountered or no key matches the
- * signature).
- */
-int verify_file(const unsigned char* addr, size_t length, const std::vector<Certificate>& keys,
-                const std::function<void(float)>& set_progress) {
-  if (set_progress) {
-    set_progress(0.0);
-  }
+int verify_file(VerifierInterface* package, const std::vector<Certificate>& keys) {
+  CHECK(package);
+  package->SetProgress(0.0);
 
   // An archive with a whole-file signature will end in six bytes:
   //
@@ -140,13 +123,18 @@
   // the whole comment.
 
 #define FOOTER_SIZE 6
+  uint64_t length = package->GetPackageSize();
 
   if (length < FOOTER_SIZE) {
     LOG(ERROR) << "not big enough to contain footer";
     return VERIFY_FAILURE;
   }
 
-  const unsigned char* footer = addr + length - FOOTER_SIZE;
+  uint8_t footer[FOOTER_SIZE];
+  if (!package->ReadFullyAtOffset(footer, FOOTER_SIZE, length - FOOTER_SIZE)) {
+    LOG(ERROR) << "Failed to read footer";
+    return VERIFY_FAILURE;
+  }
 
   if (footer[2] != 0xff || footer[3] != 0xff) {
     LOG(ERROR) << "footer is wrong";
@@ -159,8 +147,8 @@
             << " bytes from end";
 
   if (signature_start > comment_size) {
-    LOG(ERROR) << "signature start: " << signature_start << " is larger than comment size: "
-               << comment_size;
+    LOG(ERROR) << "signature start: " << signature_start
+               << " is larger than comment size: " << comment_size;
     return VERIFY_FAILURE;
   }
 
@@ -182,9 +170,13 @@
   // Determine how much of the file is covered by the signature. This is everything except the
   // signature data and length, which includes all of the EOCD except for the comment length field
   // (2 bytes) and the comment data.
-  size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
+  uint64_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2;
 
-  const unsigned char* eocd = addr + length - eocd_size;
+  uint8_t eocd[eocd_size];
+  if (!package->ReadFullyAtOffset(eocd, eocd_size, length - eocd_size)) {
+    LOG(ERROR) << "Failed to read EOCD of " << eocd_size << " bytes";
+    return VERIFY_FAILURE;
+  }
 
   // If this is really is the EOCD record, it will begin with the magic number $50 $4b $05 $06.
   if (eocd[0] != 0x50 || eocd[1] != 0x4b || eocd[2] != 0x05 || eocd[3] != 0x06) {
@@ -192,8 +184,8 @@
     return VERIFY_FAILURE;
   }
 
-  for (size_t i = 4; i < eocd_size-3; ++i) {
-    if (eocd[i] == 0x50 && eocd[i+1] == 0x4b && eocd[i+2] == 0x05 && eocd[i+3] == 0x06) {
+  for (size_t i = 4; i < eocd_size - 3; ++i) {
+    if (eocd[i] == 0x50 && eocd[i + 1] == 0x4b && eocd[i + 2] == 0x05 && eocd[i + 3] == 0x06) {
       // If the sequence $50 $4b $05 $06 appears anywhere after the real one, libziparchive will
       // find the later (wrong) one, which could be exploitable. Fail the verification if this
       // sequence occurs anywhere after the real one.
@@ -206,8 +198,12 @@
   bool need_sha256 = false;
   for (const auto& key : keys) {
     switch (key.hash_len) {
-      case SHA_DIGEST_LENGTH: need_sha1 = true; break;
-      case SHA256_DIGEST_LENGTH: need_sha256 = true; break;
+      case SHA_DIGEST_LENGTH:
+        need_sha1 = true;
+        break;
+      case SHA256_DIGEST_LENGTH:
+        need_sha256 = true;
+        break;
     }
   }
 
@@ -216,24 +212,29 @@
   SHA1_Init(&sha1_ctx);
   SHA256_Init(&sha256_ctx);
 
+  std::vector<HasherUpdateCallback> hashers;
+  if (need_sha1) {
+    hashers.emplace_back(
+        std::bind(&SHA1_Update, &sha1_ctx, std::placeholders::_1, std::placeholders::_2));
+  }
+  if (need_sha256) {
+    hashers.emplace_back(
+        std::bind(&SHA256_Update, &sha256_ctx, std::placeholders::_1, std::placeholders::_2));
+  }
+
   double frac = -1.0;
-  size_t so_far = 0;
+  uint64_t so_far = 0;
   while (so_far < signed_len) {
-    // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a
-    // 1196MiB full OTA and 60% for an 89MiB incremental OTA.
-    // http://b/28135231.
-    size_t size = std::min(signed_len - so_far, 16 * MiB);
+    // On a Nexus 5X, experiment showed 16MiB beat 1MiB by 6% faster for a 1196MiB full OTA and
+    // 60% for an 89MiB incremental OTA. http://b/28135231.
+    uint64_t read_size = std::min<uint64_t>(signed_len - so_far, 16 * MiB);
+    package->UpdateHashAtOffset(hashers, so_far, read_size);
+    so_far += read_size;
 
-    if (need_sha1) SHA1_Update(&sha1_ctx, addr + so_far, size);
-    if (need_sha256) SHA256_Update(&sha256_ctx, addr + so_far, size);
-    so_far += size;
-
-    if (set_progress) {
-      double f = so_far / (double)signed_len;
-      if (f > frac + 0.02 || size == so_far) {
-        set_progress(f);
-        frac = f;
-      }
+    double f = so_far / static_cast<double>(signed_len);
+    if (f > frac + 0.02 || read_size == so_far) {
+      package->SetProgress(f);
+      frac = f;
     }
   }
 
@@ -245,8 +246,8 @@
   const uint8_t* signature = eocd + eocd_size - signature_start;
   size_t signature_size = signature_start - FOOTER_SIZE;
 
-  LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start) << ", length: "
-            << signature_size << "): " << print_hex(signature, signature_size);
+  LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start)
+            << ", length: " << signature_size << "): " << print_hex(signature, signature_size);
 
   std::vector<uint8_t> sig_der;
   if (!read_pkcs7(signature, signature_size, &sig_der)) {
@@ -371,8 +372,8 @@
   const BIGNUM* out_e;
   RSA_get0_key(rsa.get(), &out_n, &out_e, nullptr /* private exponent */);
   auto modulus_bits = BN_num_bits(out_n);
-  if (modulus_bits != 2048) {
-    LOG(ERROR) << "Modulus should be 2048 bits long, actual: " << modulus_bits;
+  if (modulus_bits != 2048 && modulus_bits != 4096) {
+    LOG(ERROR) << "Modulus should be 2048 or 4096 bits long, actual: " << modulus_bits;
     return false;
   }
 
diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp
new file mode 100644
index 0000000..765a815
--- /dev/null
+++ b/install/wipe_data.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "install/wipe_data.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <functional>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include "otautil/dirutil.h"
+#include "otautil/logging.h"
+#include "otautil/roots.h"
+#include "recovery_ui/ui.h"
+
+constexpr const char* CACHE_ROOT = "/cache";
+constexpr const char* DATA_ROOT = "/data";
+constexpr const char* METADATA_ROOT = "/metadata";
+
+static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) {
+  bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
+  bool is_data = (strcmp(volume, DATA_ROOT) == 0);
+
+  ui->SetBackground(RecoveryUI::ERASING);
+  ui->SetProgressType(RecoveryUI::INDETERMINATE);
+
+  std::vector<saved_log_file> log_files;
+  if (is_cache) {
+    // If we're reformatting /cache, we load any past logs (i.e. "/cache/recovery/last_*") and the
+    // current log ("/cache/recovery/log") into memory, so we can restore them after the reformat.
+    log_files = ReadLogFilesToMemory();
+  }
+
+  ui->Print("Formatting %s...\n", volume);
+
+  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);
+  }
+
+  if (is_cache) {
+    RestoreLogFilesAfterFormat(log_files);
+  }
+
+  return (result == 0);
+}
+
+bool WipeCache(RecoveryUI* ui, const std::function<bool()>& confirm_func) {
+  bool has_cache = volume_for_mount_point("/cache") != nullptr;
+  if (!has_cache) {
+    ui->Print("No /cache partition found.\n");
+    return false;
+  }
+
+  if (confirm_func && !confirm_func()) {
+    return false;
+  }
+
+  ui->Print("\n-- Wiping cache...\n");
+  bool success = EraseVolume("/cache", ui, false);
+  ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
+  return success;
+}
+
+bool WipeData(Device* device, bool convert_fbe) {
+  RecoveryUI* ui = device->GetUI();
+  ui->Print("\n-- Wiping data...\n");
+  bool success = device->PreWipeData();
+  if (success) {
+    success &= EraseVolume(DATA_ROOT, ui, convert_fbe);
+    bool has_cache = volume_for_mount_point("/cache") != nullptr;
+    if (has_cache) {
+      success &= EraseVolume(CACHE_ROOT, ui, false);
+    }
+    if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
+      success &= EraseVolume(METADATA_ROOT, ui, false);
+    }
+  }
+  if (success) {
+    success &= device->PostWipeData();
+  }
+  ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
+  return success;
+}
\ No newline at end of file
diff --git a/minadbd/Android.bp b/minadbd/Android.bp
index a95d979..e4f7712 100644
--- a/minadbd/Android.bp
+++ b/minadbd/Android.bp
@@ -40,7 +40,6 @@
 
     srcs: [
         "fuse_adb_provider.cpp",
-        "minadbd.cpp",
         "minadbd_services.cpp",
     ],
 
@@ -52,6 +51,36 @@
     ],
 }
 
+cc_library_headers {
+    name: "libminadbd_headers",
+    recovery_available: true,
+    // TODO create a include dir
+    export_include_dirs: [
+        ".",
+    ],
+}
+
+cc_binary {
+    name: "minadbd",
+    recovery: true,
+
+    defaults: [
+        "minadbd_defaults",
+    ],
+
+    srcs: [
+        "minadbd.cpp",
+    ],
+
+    shared_libs: [
+        "libadbd",
+        "libbase",
+        "libcrypto",
+        "libfusesideload",
+        "libminadbd_services",
+    ],
+}
+
 cc_test {
     name: "minadbd_test",
     isolated: true,
@@ -66,6 +95,7 @@
 
     static_libs: [
         "libminadbd_services",
+        "libfusesideload",
         "libadbd",
     ],
 
diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp
index 9bd3f23..9d19a1e 100644
--- a/minadbd/fuse_adb_provider.cpp
+++ b/minadbd/fuse_adb_provider.cpp
@@ -18,39 +18,26 @@
 
 #include <errno.h>
 #include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 
-#include <functional>
-
 #include "adb.h"
 #include "adb_io.h"
-#include "fuse_sideload.h"
 
-int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
-  if (!WriteFdFmt(ad.sfd, "%08u", block)) {
+bool FuseAdbDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+                                               uint32_t start_block) const {
+  if (!WriteFdFmt(fd_, "%08u", start_block)) {
     fprintf(stderr, "failed to write to adb host: %s\n", strerror(errno));
-    return -EIO;
+    return false;
   }
 
-  if (!ReadFdExactly(ad.sfd, buffer, fetch_size)) {
+  if (!ReadFdExactly(fd_, buffer, fetch_size)) {
     fprintf(stderr, "failed to read from adb host: %s\n", strerror(errno));
-    return -EIO;
+    return false;
   }
 
-  return 0;
+  return true;
 }
 
-int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size) {
-  adb_data ad;
-  ad.sfd = sfd;
-  ad.file_size = file_size;
-  ad.block_size = block_size;
-
-  provider_vtab vtab;
-  vtab.read_block = std::bind(read_block_adb, ad, std::placeholders::_1, std::placeholders::_2,
-                              std::placeholders::_3);
-  vtab.close = [&ad]() { WriteFdExactly(ad.sfd, "DONEDONE"); };
-
-  return run_fuse_sideload(vtab, file_size, block_size);
+void FuseAdbDataProvider::Close() {
+  WriteFdExactly(fd_, "DONEDONE");
 }
diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h
index 36d86d5..24a463d 100644
--- a/minadbd/fuse_adb_provider.h
+++ b/minadbd/fuse_adb_provider.h
@@ -14,19 +14,24 @@
  * limitations under the License.
  */
 
-#ifndef __FUSE_ADB_PROVIDER_H
-#define __FUSE_ADB_PROVIDER_H
+#pragma once
 
 #include <stdint.h>
 
-struct adb_data {
-  int sfd;  // file descriptor for the adb channel
+#include "fuse_provider.h"
 
-  uint64_t file_size;
-  uint32_t block_size;
+// This class reads data from adb server.
+class FuseAdbDataProvider : public FuseDataProvider {
+ public:
+  FuseAdbDataProvider(int fd, uint64_t file_size, uint32_t block_size)
+      : FuseDataProvider(file_size, block_size), fd_(fd) {}
+
+  bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size,
+                            uint32_t start_block) const override;
+
+  void Close() override;
+
+ private:
+  // The underlying source to read data from (i.e. the one that talks to the host).
+  int fd_;
 };
-
-int read_block_adb(const adb_data& ad, uint32_t block, uint8_t* buffer, uint32_t fetch_size);
-int run_adb_fuse(int sfd, uint64_t file_size, uint32_t block_size);
-
-#endif
diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp
index 00250e5..0b09712 100644
--- a/minadbd/fuse_adb_provider_test.cpp
+++ b/minadbd/fuse_adb_provider_test.cpp
@@ -21,19 +21,19 @@
 
 #include <string>
 
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 
 #include "adb_io.h"
 #include "fuse_adb_provider.h"
 
 TEST(fuse_adb_provider, read_block_adb) {
-  adb_data data = {};
-  int sockets[2];
+  android::base::unique_fd device_socket;
+  android::base::unique_fd host_socket;
 
-  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
-  data.sfd = sockets[0];
+  ASSERT_TRUE(android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &device_socket, &host_socket));
+  FuseAdbDataProvider data(std::move(device_socket), 0, 0);
 
-  int host_socket = sockets[1];
   fcntl(host_socket, F_SETFL, O_NONBLOCK);
 
   const char expected_data[] = "foobar";
@@ -46,8 +46,8 @@
 
   uint32_t block = 1234U;
   const char expected_block[] = "00001234";
-  ASSERT_EQ(0, read_block_adb(data, block, reinterpret_cast<uint8_t*>(block_data),
-                              sizeof(expected_data) - 1));
+  ASSERT_TRUE(data.ReadBlockAlignedData(reinterpret_cast<uint8_t*>(block_data),
+                                        sizeof(expected_data) - 1, block));
 
   // Check that read_block_adb requested the right block.
   char block_req[sizeof(expected_block)] = {};
@@ -65,26 +65,21 @@
   errno = 0;
   ASSERT_EQ(-1, read(host_socket, &tmp, 1));
   ASSERT_EQ(EWOULDBLOCK, errno);
-
-  close(sockets[0]);
-  close(sockets[1]);
 }
 
 TEST(fuse_adb_provider, read_block_adb_fail_write) {
-  adb_data data = {};
-  int sockets[2];
+  android::base::unique_fd device_socket;
+  android::base::unique_fd host_socket;
 
-  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, sockets));
-  data.sfd = sockets[0];
+  ASSERT_TRUE(android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &device_socket, &host_socket));
+  FuseAdbDataProvider data(std::move(device_socket), 0, 0);
 
-  ASSERT_EQ(0, close(sockets[1]));
+  host_socket.reset();
 
   // write(2) raises SIGPIPE since the reading end has been closed. Ignore the signal to avoid
   // failing the test.
   signal(SIGPIPE, SIG_IGN);
 
   char buf[1];
-  ASSERT_EQ(-EIO, read_block_adb(data, 0, reinterpret_cast<uint8_t*>(buf), 1));
-
-  close(sockets[0]);
+  ASSERT_FALSE(data.ReadBlockAlignedData(reinterpret_cast<uint8_t*>(buf), 1, 0));
 }
diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp
index 349189c..57158ad 100644
--- a/minadbd/minadbd.cpp
+++ b/minadbd/minadbd.cpp
@@ -14,30 +14,54 @@
  * limitations under the License.
  */
 
-#include "minadbd.h"
-
 #include <errno.h>
+#include <fcntl.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <strings.h>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
 
 #include "adb.h"
 #include "adb_auth.h"
 #include "transport.h"
 
-int minadbd_main() {
-    adb_device_banner = "sideload";
+#include "minadbd_services.h"
+#include "minadbd_types.h"
 
-    signal(SIGPIPE, SIG_IGN);
+int main(int argc, char** argv) {
+  android::base::InitLogging(argv, &android::base::StderrLogger);
+  // TODO(xunchang) implement a command parser
+  if (argc != 3 || strcmp("--socket_fd", argv[1]) != 0) {
+    LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc;
+    exit(kMinadbdArgumentsParsingError);
+  }
 
-    // We can't require authentication for sideloading. http://b/22025550.
-    auth_required = false;
+  int socket_fd;
+  if (!android::base::ParseInt(argv[2], &socket_fd)) {
+    LOG(ERROR) << "Failed to parse int in " << argv[2];
+    exit(kMinadbdArgumentsParsingError);
+  }
+  if (fcntl(socket_fd, F_GETFD, 0) == -1) {
+    PLOG(ERROR) << "Failed to get minadbd socket";
+    exit(kMinadbdSocketIOError);
+  }
+  SetMinadbdSocketFd(socket_fd);
 
-    init_transport_registration();
-    usb_init();
+  adb_device_banner = "sideload";
 
-    VLOG(ADB) << "Event loop starting";
-    fdevent_loop();
+  signal(SIGPIPE, SIG_IGN);
 
-    return 0;
+  // We can't require authentication for sideloading. http://b/22025550.
+  auth_required = false;
+
+  init_transport_registration();
+  usb_init();
+
+  VLOG(ADB) << "Event loop starting";
+  fdevent_loop();
+
+  return 0;
 }
diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp
index 9309ed7..f2b65c0 100644
--- a/minadbd/minadbd_services.cpp
+++ b/minadbd/minadbd_services.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "minadbd_services.h"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdio.h>
@@ -22,39 +24,99 @@
 #include <unistd.h>
 
 #include <functional>
+#include <memory>
 #include <string>
 #include <string_view>
 #include <thread>
 
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
 #include "adb.h"
 #include "adb_unique_fd.h"
 #include "fdevent.h"
 #include "fuse_adb_provider.h"
+#include "fuse_sideload.h"
+#include "minadbd_types.h"
 #include "services.h"
 #include "sysdeps.h"
 
+static int minadbd_socket = -1;
+void SetMinadbdSocketFd(int socket_fd) {
+  minadbd_socket = socket_fd;
+}
+
+static bool WriteCommandToFd(MinadbdCommands cmd, int fd) {
+  char message[kMinadbdMessageSize];
+  memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix));
+  android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd);
+
+  if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to write message " << message;
+    return false;
+  }
+  return true;
+}
+
+// Blocks and reads the command status from |fd|. Returns false if the received message has a
+// format error.
+static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) {
+  char buffer[kMinadbdMessageSize];
+  if (!android::base::ReadFully(fd, buffer, kMinadbdMessageSize)) {
+    PLOG(ERROR) << "Failed to response status from socket";
+    exit(kMinadbdSocketIOError);
+  }
+
+  std::string message(buffer, buffer + kMinadbdMessageSize);
+  if (!android::base::StartsWith(message, kMinadbdStatusPrefix)) {
+    LOG(ERROR) << "Failed to parse status in " << message;
+    return false;
+  }
+
+  *status = android::base::get_unaligned<MinadbdCommandStatus>(
+      message.substr(strlen(kMinadbdStatusPrefix)).c_str());
+  return true;
+}
+
 static void sideload_host_service(unique_fd sfd, const std::string& args) {
   int64_t file_size;
   int block_size;
   if ((sscanf(args.c_str(), "%" SCNd64 ":%d", &file_size, &block_size) != 2) || file_size <= 0 ||
       block_size <= 0) {
-    printf("bad sideload-host arguments: %s\n", args.c_str());
-    exit(1);
+    LOG(ERROR) << "bad sideload-host arguments: " << args;
+    exit(kMinadbdPackageSizeError);
   }
 
-  printf("sideload-host file size %" PRId64 " block size %d\n", file_size, block_size);
+  LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size;
 
-  int result = run_adb_fuse(sfd, file_size, block_size);
+  if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) {
+    exit(kMinadbdSocketIOError);
+  }
 
-  printf("sideload_host finished\n");
-  exit(result == 0 ? 0 : 1);
+  auto adb_data_reader = std::make_unique<FuseAdbDataProvider>(sfd, file_size, block_size);
+  if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) {
+    LOG(ERROR) << "Failed to start fuse";
+    exit(kMinadbdFuseStartError);
+  }
+
+  MinadbdCommandStatus status;
+  if (!WaitForCommandStatus(minadbd_socket, &status)) {
+    exit(kMinadbdMessageFormatError);
+  }
+  LOG(INFO) << "Got command status: " << static_cast<unsigned int>(status);
+
+  LOG(INFO) << "sideload_host finished";
+  exit(kMinadbdSuccess);
 }
 
 unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) {
   if (name.starts_with("sideload:")) {
     // This exit status causes recovery to print a special error message saying to use a newer adb
     // (that supports sideload-host).
-    exit(3);
+    exit(kMinadbdAdbVersionError);
   } else if (name.starts_with("sideload-host:")) {
     std::string arg(name.substr(strlen("sideload-host:")));
     return create_service_thread("sideload-host",
diff --git a/minadbd/minadbd.h b/minadbd/minadbd_services.h
similarity index 89%
rename from minadbd/minadbd.h
rename to minadbd/minadbd_services.h
index 3570a5d..6835bd7 100644
--- a/minadbd/minadbd.h
+++ b/minadbd/minadbd_services.h
@@ -14,9 +14,6 @@
  * limitations under the License.
  */
 
-#ifndef MINADBD_H__
-#define MINADBD_H__
+#pragma once
 
-int minadbd_main();
-
-#endif
+void SetMinadbdSocketFd(int socket_fd);
diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h
new file mode 100644
index 0000000..7bd6909
--- /dev/null
+++ b/minadbd/minadbd_types.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+// The message between recovery and minadbd is 8 bytes in size unless the length is explicitly
+// specified. Both the command and status has the format |prefix(4 bytes) + encoded enum(4 bytes)|.
+constexpr size_t kMinadbdMessageSize = 8;
+constexpr char const kMinadbdCommandPrefix[] = "COMD";
+constexpr char const kMinadbdStatusPrefix[] = "STAT";
+
+enum MinadbdErrorCode : int {
+  kMinadbdSuccess = 0,
+  kMinadbdArgumentsParsingError = 1,
+  kMinadbdSocketIOError = 2,
+  kMinadbdMessageFormatError = 3,
+  kMinadbdAdbVersionError = 4,
+  kMinadbdPackageSizeError = 5,
+  kMinadbdFuseStartError = 6,
+  kMinadbdUnsupportedCommandError = 7,
+  kMinadbdCommandExecutionError = 8,
+  kMinadbdErrorUnknown = 9,
+};
+
+enum class MinadbdCommandStatus : uint32_t {
+  kSuccess = 0,
+  kFailure = 1,
+};
+
+enum class MinadbdCommands : uint32_t {
+  kInstall = 0,
+  kUiPrint = 1,
+  kError = 2,
+};
+
+static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands));
+static_assert(kMinadbdMessageSize ==
+              sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus));
diff --git a/minui/events.cpp b/minui/events.cpp
index d94e977..7d0250e 100644
--- a/minui/events.cpp
+++ b/minui/events.cpp
@@ -23,144 +23,148 @@
 #include <string.h>
 #include <sys/epoll.h>
 #include <sys/ioctl.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include <functional>
+#include <memory>
+
+#include <android-base/unique_fd.h>
 
 #include "minui/minui.h"
 
-#define MAX_DEVICES 16
-#define MAX_MISC_FDS 16
+constexpr size_t MAX_DEVICES = 16;
+constexpr size_t MAX_MISC_FDS = 16;
 
-#define BITS_PER_LONG (sizeof(unsigned long) * 8)
-#define BITS_TO_LONGS(x) (((x) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+constexpr size_t BITS_PER_LONG = sizeof(unsigned long) * 8;
+constexpr size_t BITS_TO_LONGS(size_t bits) {
+  return ((bits + BITS_PER_LONG - 1) / BITS_PER_LONG);
+}
 
-struct fd_info {
-  int fd;
+struct FdInfo {
+  android::base::unique_fd fd;
   ev_callback cb;
 };
 
-static int g_epoll_fd;
-static epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS];
-static int npolledevents;
+static android::base::unique_fd g_epoll_fd;
+static epoll_event g_polled_events[MAX_DEVICES + MAX_MISC_FDS];
+static int g_polled_events_count;
 
-static fd_info ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
+static FdInfo ev_fdinfo[MAX_DEVICES + MAX_MISC_FDS];
 
-static unsigned ev_count = 0;
-static unsigned ev_dev_count = 0;
-static unsigned ev_misc_count = 0;
+static size_t g_ev_count = 0;
+static size_t g_ev_dev_count = 0;
+static size_t g_ev_misc_count = 0;
 
 static bool test_bit(size_t bit, unsigned long* array) { // NOLINT
-    return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
+  return (array[bit / BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0;
 }
 
 int ev_init(ev_callback input_cb, bool allow_touch_inputs) {
-  g_epoll_fd = epoll_create1(EPOLL_CLOEXEC);
-  if (g_epoll_fd == -1) {
+  g_epoll_fd.reset();
+
+  android::base::unique_fd epoll_fd(epoll_create1(EPOLL_CLOEXEC));
+  if (epoll_fd == -1) {
     return -1;
   }
 
-  bool epollctlfail = false;
-  DIR* dir = opendir("/dev/input");
-  if (dir != nullptr) {
-    dirent* de;
-    while ((de = readdir(dir))) {
-      if (strncmp(de->d_name, "event", 5)) continue;
-      int fd = openat(dirfd(dir), de->d_name, O_RDONLY);
-      if (fd == -1) continue;
+  std::unique_ptr<DIR, decltype(&closedir)> dir(opendir("/dev/input"), closedir);
+  if (!dir) {
+    return -1;
+  }
 
-      // Use unsigned long to match ioctl's parameter type.
-      unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];  // NOLINT
+  bool epoll_ctl_failed = false;
+  dirent* de;
+  while ((de = readdir(dir.get())) != nullptr) {
+    if (strncmp(de->d_name, "event", 5)) continue;
+    android::base::unique_fd fd(openat(dirfd(dir.get()), de->d_name, O_RDONLY | O_CLOEXEC));
+    if (fd == -1) continue;
 
-      // Read the evbits of the input device.
-      if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
-        close(fd);
-        continue;
-      }
+    // Use unsigned long to match ioctl's parameter type.
+    unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];  // NOLINT
 
-      // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
-      // allowed if allow_touch_inputs is set.
-      if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
-        if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
-          close(fd);
-          continue;
-        }
-      }
-
-      epoll_event ev;
-      ev.events = EPOLLIN | EPOLLWAKEUP;
-      ev.data.ptr = &ev_fdinfo[ev_count];
-      if (epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
-        close(fd);
-        epollctlfail = true;
-        continue;
-      }
-
-      ev_fdinfo[ev_count].fd = fd;
-      ev_fdinfo[ev_count].cb = std::move(input_cb);
-      ev_count++;
-      ev_dev_count++;
-      if (ev_dev_count == MAX_DEVICES) break;
+    // Read the evbits of the input device.
+    if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+      continue;
     }
 
-    closedir(dir);
+    // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also
+    // allowed if allow_touch_inputs is set.
+    if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) {
+      if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) {
+        continue;
+      }
+    }
+
+    epoll_event ev;
+    ev.events = EPOLLIN | EPOLLWAKEUP;
+    ev.data.ptr = &ev_fdinfo[g_ev_count];
+    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
+      epoll_ctl_failed = true;
+      continue;
+    }
+
+    ev_fdinfo[g_ev_count].fd.reset(fd.release());
+    ev_fdinfo[g_ev_count].cb = input_cb;
+    g_ev_count++;
+    g_ev_dev_count++;
+    if (g_ev_dev_count == MAX_DEVICES) break;
   }
 
-  if (epollctlfail && !ev_count) {
-    close(g_epoll_fd);
-    g_epoll_fd = -1;
+  if (epoll_ctl_failed && !g_ev_count) {
     return -1;
   }
 
+  g_epoll_fd.reset(epoll_fd.release());
   return 0;
 }
 
 int ev_get_epollfd(void) {
-    return g_epoll_fd;
+  return g_epoll_fd.get();
 }
 
-int ev_add_fd(int fd, ev_callback cb) {
-  if (ev_misc_count == MAX_MISC_FDS || cb == NULL) {
+int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb) {
+  if (g_ev_misc_count == MAX_MISC_FDS || cb == nullptr) {
     return -1;
   }
 
   epoll_event ev;
   ev.events = EPOLLIN | EPOLLWAKEUP;
-  ev.data.ptr = static_cast<void*>(&ev_fdinfo[ev_count]);
+  ev.data.ptr = static_cast<void*>(&ev_fdinfo[g_ev_count]);
   int ret = epoll_ctl(g_epoll_fd, EPOLL_CTL_ADD, fd, &ev);
   if (!ret) {
-    ev_fdinfo[ev_count].fd = fd;
-    ev_fdinfo[ev_count].cb = std::move(cb);
-    ev_count++;
-    ev_misc_count++;
+    ev_fdinfo[g_ev_count].fd.reset(fd.release());
+    ev_fdinfo[g_ev_count].cb = std::move(cb);
+    g_ev_count++;
+    g_ev_misc_count++;
   }
 
   return ret;
 }
 
 void ev_exit(void) {
-    while (ev_count > 0) {
-        close(ev_fdinfo[--ev_count].fd);
-    }
-    ev_misc_count = 0;
-    ev_dev_count = 0;
-    close(g_epoll_fd);
+  while (g_ev_count > 0) {
+    ev_fdinfo[--g_ev_count].fd.reset();
+  }
+  g_ev_misc_count = 0;
+  g_ev_dev_count = 0;
+  g_epoll_fd.reset();
 }
 
 int ev_wait(int timeout) {
-    npolledevents = epoll_wait(g_epoll_fd, polledevents, ev_count, timeout);
-    if (npolledevents <= 0) {
-        return -1;
-    }
-    return 0;
+  g_polled_events_count = epoll_wait(g_epoll_fd, g_polled_events, g_ev_count, timeout);
+  if (g_polled_events_count <= 0) {
+    return -1;
+  }
+  return 0;
 }
 
 void ev_dispatch(void) {
-  for (int n = 0; n < npolledevents; n++) {
-    fd_info* fdi = static_cast<fd_info*>(polledevents[n].data.ptr);
+  for (int n = 0; n < g_polled_events_count; n++) {
+    FdInfo* fdi = static_cast<FdInfo*>(g_polled_events[n].data.ptr);
     const ev_callback& cb = fdi->cb;
     if (cb) {
-      cb(fdi->fd, polledevents[n].events);
+      cb(fdi->fd, g_polled_events[n].events);
     }
   }
 }
@@ -180,7 +184,7 @@
   unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];    // NOLINT
   unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];  // NOLINT
 
-  for (size_t i = 0; i < ev_dev_count; ++i) {
+  for (size_t i = 0; i < g_ev_dev_count; ++i) {
     memset(ev_bits, 0, sizeof(ev_bits));
     memset(key_bits, 0, sizeof(key_bits));
 
@@ -205,37 +209,36 @@
 }
 
 void ev_iterate_available_keys(const std::function<void(int)>& f) {
-    // Use unsigned long to match ioctl's parameter type.
-    unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT
-    unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)]; // NOLINT
+  // Use unsigned long to match ioctl's parameter type.
+  unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)];    // NOLINT
+  unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)];  // NOLINT
 
-    for (size_t i = 0; i < ev_dev_count; ++i) {
-        memset(ev_bits, 0, sizeof(ev_bits));
-        memset(key_bits, 0, sizeof(key_bits));
+  for (size_t i = 0; i < g_ev_dev_count; ++i) {
+    memset(ev_bits, 0, sizeof(ev_bits));
+    memset(key_bits, 0, sizeof(key_bits));
 
-        // Does this device even have keys?
-        if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
-            continue;
-        }
-        if (!test_bit(EV_KEY, ev_bits)) {
-            continue;
-        }
-
-        int rc = ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits);
-        if (rc == -1) {
-            continue;
-        }
-
-        for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
-            if (test_bit(key_code, key_bits)) {
-                f(key_code);
-            }
-        }
+    // Does this device even have keys?
+    if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
+      continue;
     }
+    if (!test_bit(EV_KEY, ev_bits)) {
+      continue;
+    }
+
+    if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_KEY, KEY_MAX), key_bits) == -1) {
+      continue;
+    }
+
+    for (int key_code = 0; key_code <= KEY_MAX; ++key_code) {
+      if (test_bit(key_code, key_bits)) {
+        f(key_code);
+      }
+    }
+  }
 }
 
 void ev_iterate_touch_inputs(const std::function<void(int)>& action) {
-  for (size_t i = 0; i < ev_dev_count; ++i) {
+  for (size_t i = 0; i < g_ev_dev_count; ++i) {
     // Use unsigned long to match ioctl's parameter type.
     unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {};  // NOLINT
     if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) {
diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp
index 9eea497..10cd607 100644
--- a/minui/graphics_adf.cpp
+++ b/minui/graphics_adf.cpp
@@ -101,7 +101,7 @@
   err = adf_device_attach(dev, eng_id, intf_id);
   if (err < 0 && err != -EALREADY) return err;
 
-  intf_fd = adf_interface_open(dev, intf_id, O_RDWR);
+  intf_fd = adf_interface_open(dev, intf_id, O_RDWR | O_CLOEXEC);
   if (intf_fd < 0) return intf_fd;
 
   err = InterfaceInit();
diff --git a/minui/graphics_drm.cpp b/minui/graphics_drm.cpp
index 765e262..7b2eed1 100644
--- a/minui/graphics_drm.cpp
+++ b/minui/graphics_drm.cpp
@@ -285,7 +285,7 @@
   /* Consider DRM devices in order. */
   for (int i = 0; i < DRM_MAX_MINOR; i++) {
     auto dev_name = android::base::StringPrintf(DRM_DEV_NAME, DRM_DIR_NAME, i);
-    android::base::unique_fd fd(open(dev_name.c_str(), O_RDWR));
+    android::base::unique_fd fd(open(dev_name.c_str(), O_RDWR | O_CLOEXEC));
     if (fd == -1) continue;
 
     /* We need dumb buffers. */
diff --git a/minui/graphics_fbdev.cpp b/minui/graphics_fbdev.cpp
index 8d9c974..2584017 100644
--- a/minui/graphics_fbdev.cpp
+++ b/minui/graphics_fbdev.cpp
@@ -56,7 +56,7 @@
 }
 
 GRSurface* MinuiBackendFbdev::Init() {
-  android::base::unique_fd fd(open("/dev/graphics/fb0", O_RDWR));
+  android::base::unique_fd fd(open("/dev/graphics/fb0", O_RDWR | O_CLOEXEC));
   if (fd == -1) {
     perror("cannot open fb0");
     return nullptr;
diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h
index e49c6ac..36bdcf1 100644
--- a/minui/include/minui/minui.h
+++ b/minui/include/minui/minui.h
@@ -26,6 +26,7 @@
 #include <vector>
 
 #include <android-base/macros.h>
+#include <android-base/unique_fd.h>
 
 //
 // Graphics.
@@ -153,7 +154,7 @@
 
 int ev_init(ev_callback input_cb, bool allow_touch_inputs = false);
 void ev_exit();
-int ev_add_fd(int fd, ev_callback cb);
+int ev_add_fd(android::base::unique_fd&& fd, ev_callback cb);
 void ev_iterate_available_keys(const std::function<void(int)>& f);
 void ev_iterate_touch_inputs(const std::function<void(int)>& action);
 int ev_sync_key_state(const ev_set_key_callback& set_key_cb);
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 41018dd..73398c3 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -40,16 +40,32 @@
         android: {
             srcs: [
                 "dirutil.cpp",
+                "logging.cpp",
                 "mounts.cpp",
                 "parse_install_logs.cpp",
+                "roots.cpp",
                 "sysutil.cpp",
                 "thermalutil.cpp",
             ],
 
+            include_dirs: [
+                "system/vold",
+            ],
+
+            static_libs: [
+                "libfstab",
+            ],
+
             shared_libs: [
                 "libcutils",
+                "libext4_utils",
+                "libfs_mgr",
                 "libselinux",
             ],
+
+            export_static_lib_headers: [
+                "libfstab",
+            ],
         },
     },
 }
diff --git a/logging.h b/otautil/include/otautil/logging.h
similarity index 79%
rename from logging.h
rename to otautil/include/otautil/logging.h
index 3cfbc7a..6083497 100644
--- a/logging.h
+++ b/otautil/include/otautil/logging.h
@@ -18,14 +18,26 @@
 #define _LOGGING_H
 
 #include <stddef.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 
 #include <string>
+#include <vector>
 
 #include <log/log_id.h>
 
 static constexpr int KEEP_LOG_COUNT = 10;
 
+struct selabel_handle;
+
+struct saved_log_file {
+  std::string name;
+  struct stat sb;
+  std::string data;
+};
+
+void SetLoggingSehandle(selabel_handle* handle);
+
 ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len,
                     void* arg);
 
@@ -41,10 +53,13 @@
 void check_and_fclose(FILE* fp, const std::string& name);
 
 void copy_log_file_to_pmsg(const std::string& source, const std::string& destination);
-void copy_log_file(const std::string& source, const std::string& destination, bool append);
-void copy_logs(bool modified_flash, bool has_cache);
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle);
 void reset_tmplog_offset();
 
 void save_kernel_log(const char* destination);
 
+std::vector<saved_log_file> ReadLogFilesToMemory();
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files);
+
 #endif  //_LOGGING_H
diff --git a/roots.h b/otautil/include/otautil/roots.h
similarity index 95%
rename from roots.h
rename to otautil/include/otautil/roots.h
index 7b031a1..482f3d0 100644
--- a/roots.h
+++ b/otautil/include/otautil/roots.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef RECOVERY_ROOTS_H_
-#define RECOVERY_ROOTS_H_
+#pragma once
 
 #include <string>
 
@@ -58,5 +57,3 @@
 bool logical_partitions_mapped();
 
 std::string get_system_root();
-
-#endif  // RECOVERY_ROOTS_H_
diff --git a/otautil/include/otautil/sysutil.h b/otautil/include/otautil/sysutil.h
index 2eeb7c3..692a99e 100644
--- a/otautil/include/otautil/sysutil.h
+++ b/otautil/include/otautil/sysutil.h
@@ -22,6 +22,57 @@
 #include <string>
 #include <vector>
 
+#include "rangeset.h"
+
+// This class holds the content of a block map file.
+class BlockMapData {
+ public:
+  // A "block map" which looks like this (from uncrypt/uncrypt.cpp):
+  //
+  //   /dev/block/platform/msm_sdcc.1/by-name/userdata     # block device
+  //   49652 4096                                          # file size in bytes, block size
+  //   3                                                   # count of block ranges
+  //   1000 1008                                           # block range 0
+  //   2100 2102                                           # ... block range 1
+  //   30 33                                               # ... block range 2
+  //
+  // Each block range represents a half-open interval; the line "30 33" reprents the blocks
+  // [30, 31, 32].
+  static BlockMapData ParseBlockMapFile(const std::string& block_map_path);
+
+  explicit operator bool() const {
+    return !path_.empty();
+  }
+
+  std::string path() const {
+    return path_;
+  }
+  uint64_t file_size() const {
+    return file_size_;
+  }
+  uint32_t block_size() const {
+    return block_size_;
+  }
+  RangeSet block_ranges() const {
+    return block_ranges_;
+  }
+
+ private:
+  BlockMapData() = default;
+
+  BlockMapData(const std::string& path, uint64_t file_size, uint32_t block_size,
+               RangeSet block_ranges)
+      : path_(path),
+        file_size_(file_size),
+        block_size_(block_size),
+        block_ranges_(std::move(block_ranges)) {}
+
+  std::string path_;
+  uint64_t file_size_ = 0;
+  uint32_t block_size_ = 0;
+  RangeSet block_ranges_;
+};
+
 /*
  * Use this to keep track of mapped segments.
  */
diff --git a/logging.cpp b/otautil/logging.cpp
similarity index 65%
rename from logging.cpp
rename to otautil/logging.cpp
index 283d115..484f115 100644
--- a/logging.cpp
+++ b/otautil/logging.cpp
@@ -14,38 +14,51 @@
  * limitations under the License.
  */
 
-#include "logging.h"
+#include "otautil/logging.h"
 
+#include <dirent.h>
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/klog.h>
 #include <sys/types.h>
 
+#include <algorithm>
+#include <memory>
 #include <string>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
 #include <private/android_filesystem_config.h> /* for AID_SYSTEM */
 #include <private/android_logger.h>            /* private pmsg functions */
+#include <selinux/label.h>
 
-#include "common.h"
 #include "otautil/dirutil.h"
 #include "otautil/paths.h"
-#include "roots.h"
+#include "otautil/roots.h"
 
-static constexpr const char* LOG_FILE = "/cache/recovery/log";
-static constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
-static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
-static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
+constexpr const char* LOG_FILE = "/cache/recovery/log";
+constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install";
+constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
 
-static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg";
-static const std::string LAST_LOG_FILTER = "recovery/last_log";
+constexpr const char* LAST_KMSG_FILTER = "recovery/last_kmsg";
+constexpr const char* LAST_LOG_FILTER = "recovery/last_log";
+
+constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
+
+static struct selabel_handle* logging_sehandle;
+
+void SetLoggingSehandle(selabel_handle* handle) {
+  logging_sehandle = handle;
+}
 
 // fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the
 // file pointer, or nullptr on error.
-static FILE* fopen_path(const std::string& path, const char* mode) {
+static FILE* fopen_path(const std::string& path, const char* mode, const selabel_handle* sehandle) {
   if (ensure_path_mounted(path) != 0) {
     LOG(ERROR) << "Can't mount " << path;
     return nullptr;
@@ -74,8 +87,8 @@
 ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */,
                     size_t len, void* arg) {
   bool* do_rotate = static_cast<bool*>(arg);
-  if (LAST_KMSG_FILTER.find(filename) != std::string::npos ||
-      LAST_LOG_FILTER.find(filename) != std::string::npos) {
+  if (std::string(LAST_KMSG_FILTER).find(filename) != std::string::npos ||
+      std::string(LAST_LOG_FILTER).find(filename) != std::string::npos) {
     *do_rotate = true;
   }
   return len;
@@ -92,8 +105,8 @@
   size_t dot = name.find_last_of('.');
   std::string sub = name.substr(0, dot);
 
-  if (LAST_KMSG_FILTER.find(sub) == std::string::npos &&
-      LAST_LOG_FILTER.find(sub) == std::string::npos) {
+  if (std::string(LAST_KMSG_FILTER).find(sub) == std::string::npos &&
+      std::string(LAST_LOG_FILTER).find(sub) == std::string::npos) {
     return __android_log_pmsg_file_write(id, prio, filename, buf, len);
   }
 
@@ -165,8 +178,9 @@
   tmplog_offset = 0;
 }
 
-void copy_log_file(const std::string& source, const std::string& destination, bool append) {
-  FILE* dest_fp = fopen_path(destination, append ? "ae" : "we");
+static void copy_log_file(const std::string& source, const std::string& destination, bool append,
+                          const selabel_handle* sehandle) {
+  FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle);
   if (dest_fp == nullptr) {
     PLOG(ERROR) << "Can't open " << destination;
   } else {
@@ -189,11 +203,11 @@
   }
 }
 
-void copy_logs(bool modified_flash, bool has_cache) {
-  // We only rotate and record the log of the current session if there are actual attempts to modify
-  // the flash, such as wipes, installs from BCB or menu selections. This is to avoid unnecessary
+void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) {
+  // We only rotate and record the log of the current session if explicitly requested. This usually
+  // happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary
   // rotation (and possible deletion) of log files, if it does not do anything loggable.
-  if (!modified_flash) {
+  if (!save_current_log) {
     return;
   }
 
@@ -211,9 +225,9 @@
   rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE);
 
   // Copy logs to cache so the system can find out what happened.
-  copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true);
-  copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false);
-  copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false);
+  copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle);
+  copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle);
+  copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle);
   save_kernel_log(LAST_KMSG_FILE);
   chmod(LOG_FILE, 0600);
   chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM);
@@ -242,3 +256,70 @@
   buffer.resize(n);
   android::base::WriteStringToFile(buffer, destination);
 }
+
+std::vector<saved_log_file> ReadLogFilesToMemory() {
+  ensure_path_mounted("/cache");
+
+  struct dirent* de;
+  std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
+  if (!d) {
+    if (errno != ENOENT) {
+      PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
+    }
+    return {};
+  }
+
+  std::vector<saved_log_file> log_files;
+  while ((de = readdir(d.get())) != nullptr) {
+    if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
+      std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
+
+      struct stat sb;
+      if (stat(path.c_str(), &sb) != 0) {
+        PLOG(ERROR) << "Failed to stat " << path;
+        continue;
+      }
+      // Truncate files to 512kb
+      size_t read_size = std::min<size_t>(sb.st_size, 1 << 19);
+      std::string data(read_size, '\0');
+
+      android::base::unique_fd log_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY)));
+      if (log_fd == -1 || !android::base::ReadFully(log_fd, data.data(), read_size)) {
+        PLOG(ERROR) << "Failed to read log file " << path;
+        continue;
+      }
+
+      log_files.emplace_back(saved_log_file{ path, sb, data });
+    }
+  }
+
+  return log_files;
+}
+
+bool RestoreLogFilesAfterFormat(const std::vector<saved_log_file>& log_files) {
+  // Re-create the log dir and write back the log entries.
+  if (ensure_path_mounted(CACHE_LOG_DIR) != 0) {
+    PLOG(ERROR) << "Failed to mount " << CACHE_LOG_DIR;
+    return false;
+  }
+
+  if (mkdir_recursively(CACHE_LOG_DIR, 0777, false, logging_sehandle) != 0) {
+    PLOG(ERROR) << "Failed to create " << CACHE_LOG_DIR;
+    return false;
+  }
+
+  for (const auto& log : log_files) {
+    if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
+                                          log.sb.st_gid)) {
+      PLOG(ERROR) << "Failed to write to " << log.name;
+    }
+  }
+
+  // Any part of the log we'd copied to cache is now gone.
+  // Reset the pointer so we copy from the beginning of the temp
+  // log.
+  reset_tmplog_offset();
+  copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle);
+
+  return true;
+}
diff --git a/roots.cpp b/otautil/roots.cpp
similarity index 97%
rename from roots.cpp
rename to otautil/roots.cpp
index 7a922b8..815d644 100644
--- a/roots.cpp
+++ b/otautil/roots.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "roots.h"
+#include "otautil/roots.h"
 
 #include <ctype.h>
 #include <fcntl.h>
@@ -51,8 +51,6 @@
 
 static Fstab fstab;
 
-extern struct selabel_handle* sehandle;
-
 void load_volume_table() {
   if (!ReadDefaultFstab(&fstab)) {
     LOG(ERROR) << "Failed to read default fstab";
@@ -176,11 +174,9 @@
       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 ? -v->length : CRYPT_FOOTER_OFFSET);
     if (length <= 0) {
-      LOG(ERROR) << "get_file_size: invalid size " << length << " for "
-                 << v->blk_device;
+      LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device;
       return -1;
     }
   }
diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp
index d8969a0..8366fa0 100644
--- a/otautil/sysutil.cpp
+++ b/otautil/sysutil.cpp
@@ -18,12 +18,13 @@
 
 #include <errno.h>  // TEMP_FAILURE_RETRY
 #include <fcntl.h>
-#include <stdint.h>  // SIZE_MAX
+#include <inttypes.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 
 #include <algorithm>
+#include <limits>
 #include <string>
 #include <vector>
 
@@ -34,6 +35,68 @@
 #include <android-base/unique_fd.h>
 #include <cutils/android_reboot.h>
 
+BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) {
+  std::string content;
+  if (!android::base::ReadFileToString(block_map_path, &content)) {
+    LOG(ERROR) << "Failed to read " << block_map_path;
+    return {};
+  }
+
+  std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
+  if (lines.size() < 4) {
+    LOG(ERROR) << "Block map file is too short: " << lines.size();
+    return {};
+  }
+
+  const std::string& block_dev = lines[0];
+
+  uint64_t file_size;
+  uint32_t blksize;
+  if (sscanf(lines[1].c_str(), "%" SCNu64 "%" SCNu32, &file_size, &blksize) != 2) {
+    LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
+    return {};
+  }
+
+  if (file_size == 0 || blksize == 0) {
+    LOG(ERROR) << "Invalid size in block map file: size " << file_size << ", blksize " << blksize;
+    return {};
+  }
+
+  size_t range_count;
+  if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
+    LOG(ERROR) << "Failed to parse block map header: " << lines[2];
+    return {};
+  }
+
+  uint64_t blocks = ((file_size - 1) / blksize) + 1;
+  if (blocks > std::numeric_limits<uint32_t>::max() || range_count == 0 ||
+      lines.size() != 3 + range_count) {
+    LOG(ERROR) << "Invalid data in block map file: size " << file_size << ", blksize " << blksize
+               << ", range_count " << range_count << ", lines " << lines.size();
+    return {};
+  }
+
+  RangeSet ranges;
+  uint64_t remaining_blocks = blocks;
+  for (size_t i = 0; i < range_count; ++i) {
+    const std::string& line = lines[i + 3];
+    uint64_t start, end;
+    if (sscanf(line.c_str(), "%" SCNu64 "%" SCNu64, &start, &end) != 2) {
+      LOG(ERROR) << "failed to parse range " << i << ": " << line;
+      return {};
+    }
+    uint64_t range_blocks = end - start;
+    if (end <= start || range_blocks > remaining_blocks) {
+      LOG(ERROR) << "Invalid range: " << start << " " << end;
+      return {};
+    }
+    ranges.PushBack({ start, end });
+    remaining_blocks -= range_blocks;
+  }
+
+  return BlockMapData(block_dev, file_size, blksize, std::move(ranges));
+}
+
 bool MemMapping::MapFD(int fd) {
   struct stat sb;
   if (fstat(fd, &sb) == -1) {
@@ -55,115 +118,61 @@
   return true;
 }
 
-// A "block map" which looks like this (from uncrypt/uncrypt.cpp):
-//
-//   /dev/block/platform/msm_sdcc.1/by-name/userdata     # block device
-//   49652 4096                                          # file size in bytes, block size
-//   3                                                   # count of block ranges
-//   1000 1008                                           # block range 0
-//   2100 2102                                           # ... block range 1
-//   30 33                                               # ... block range 2
-//
-// Each block range represents a half-open interval; the line "30 33" reprents the blocks
-// [30, 31, 32].
 bool MemMapping::MapBlockFile(const std::string& filename) {
-  std::string content;
-  if (!android::base::ReadFileToString(filename, &content)) {
-    PLOG(ERROR) << "Failed to read " << filename;
+  auto block_map_data = BlockMapData::ParseBlockMapFile(filename);
+  if (!block_map_data) {
     return false;
   }
 
-  std::vector<std::string> lines = android::base::Split(android::base::Trim(content), "\n");
-  if (lines.size() < 4) {
-    LOG(ERROR) << "Block map file is too short: " << lines.size();
-    return false;
-  }
-
-  size_t size;
-  size_t blksize;
-  if (sscanf(lines[1].c_str(), "%zu %zu", &size, &blksize) != 2) {
-    LOG(ERROR) << "Failed to parse file size and block size: " << lines[1];
-    return false;
-  }
-
-  size_t range_count;
-  if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) {
-    LOG(ERROR) << "Failed to parse block map header: " << lines[2];
-    return false;
-  }
-
-  size_t blocks;
-  if (blksize != 0) {
-    blocks = ((size - 1) / blksize) + 1;
-  }
-  if (size == 0 || blksize == 0 || blocks > SIZE_MAX / blksize || range_count == 0 ||
-      lines.size() != 3 + range_count) {
-    LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize
-               << ", range_count " << range_count << ", lines " << lines.size();
+  if (block_map_data.file_size() > std::numeric_limits<size_t>::max()) {
+    LOG(ERROR) << "File size is too large for mmap " << block_map_data.file_size();
     return false;
   }
 
   // Reserve enough contiguous address space for the whole file.
+  uint32_t blksize = block_map_data.block_size();
+  uint64_t blocks = ((block_map_data.file_size() - 1) / blksize) + 1;
   void* reserve = mmap(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
   if (reserve == MAP_FAILED) {
     PLOG(ERROR) << "failed to reserve address space";
     return false;
   }
 
-  const std::string& block_dev = lines[0];
-  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_dev.c_str(), O_RDONLY)));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(block_map_data.path().c_str(), O_RDONLY)));
   if (fd == -1) {
-    PLOG(ERROR) << "failed to open block device " << block_dev;
+    PLOG(ERROR) << "failed to open block device " << block_map_data.path();
     munmap(reserve, blocks * blksize);
     return false;
   }
 
   ranges_.clear();
 
-  unsigned char* next = static_cast<unsigned char*>(reserve);
+  auto next = static_cast<unsigned char*>(reserve);
   size_t remaining_size = blocks * blksize;
-  bool success = true;
-  for (size_t i = 0; i < range_count; ++i) {
-    const std::string& line = lines[i + 3];
-
-    size_t start, end;
-    if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) {
-      LOG(ERROR) << "failed to parse range " << i << ": " << line;
-      success = false;
-      break;
-    }
+  for (const auto& [start, end] : block_map_data.block_ranges()) {
     size_t range_size = (end - start) * blksize;
-    if (end <= start || (end - start) > SIZE_MAX / blksize || range_size > remaining_size) {
-      LOG(ERROR) << "Invalid range: " << start << " " << end;
-      success = false;
-      break;
-    }
-
     void* range_start = mmap(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd,
                              static_cast<off_t>(start) * blksize);
     if (range_start == MAP_FAILED) {
-      PLOG(ERROR) << "failed to map range " << i << ": " << line;
-      success = false;
-      break;
+      PLOG(ERROR) << "failed to map range " << start << ": " << end;
+      munmap(reserve, blocks * blksize);
+      return false;
     }
     ranges_.emplace_back(MappedRange{ range_start, range_size });
 
     next += range_size;
     remaining_size -= range_size;
   }
-  if (success && remaining_size != 0) {
+  if (remaining_size != 0) {
     LOG(ERROR) << "Invalid ranges: remaining_size " << remaining_size;
-    success = false;
-  }
-  if (!success) {
     munmap(reserve, blocks * blksize);
     return false;
   }
 
   addr = static_cast<unsigned char*>(reserve);
-  length = size;
+  length = block_map_data.file_size();
 
-  LOG(INFO) << "mmapped " << range_count << " ranges";
+  LOG(INFO) << "mmapped " << block_map_data.block_ranges().size() << " ranges";
 
   return true;
 }
diff --git a/recovery-persist.cpp b/recovery-persist.cpp
index e2a6699..294017a 100644
--- a/recovery-persist.cpp
+++ b/recovery-persist.cpp
@@ -43,7 +43,7 @@
 #include <metricslogger/metrics_logger.h>
 #include <private/android_logger.h> /* private pmsg functions */
 
-#include "logging.h"
+#include "otautil/logging.h"
 #include "otautil/parse_install_logs.h"
 
 constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log";
diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp
index aee1ca5..d41755d 100644
--- a/recovery-refresh.cpp
+++ b/recovery-refresh.cpp
@@ -42,7 +42,7 @@
 
 #include <private/android_logger.h> /* private pmsg functions */
 
-#include "logging.h"
+#include "otautil/logging.h"
 
 int main(int argc, char **argv) {
     static const char filter[] = "recovery/";
diff --git a/recovery.cpp b/recovery.cpp
index 703923e..0e6e497 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -17,7 +17,6 @@
 #include "recovery.h"
 
 #include <ctype.h>
-#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
@@ -25,13 +24,10 @@
 #include <limits.h>
 #include <linux/fs.h>
 #include <linux/input.h>
-#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
 #include <algorithm>
@@ -52,38 +48,33 @@
 #include <healthhalutils/HealthHalUtils.h>
 #include <ziparchive/zip_archive.h>
 
-#include "adb_install.h"
 #include "common.h"
-#include "device.h"
 #include "fsck_unshare_blocks.h"
-#include "fuse_sdcard_provider.h"
-#include "fuse_sideload.h"
-#include "install.h"
-#include "logging.h"
-#include "otautil/dirutil.h"
+#include "install/adb_install.h"
+#include "install/fuse_sdcard_install.h"
+#include "install/install.h"
+#include "install/package.h"
+#include "install/wipe_data.h"
 #include "otautil/error_code.h"
+#include "otautil/logging.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
-#include "roots.h"
-#include "screen_ui.h"
-#include "ui.h"
+#include "recovery_ui/screen_ui.h"
+#include "recovery_ui/ui.h"
 
-static constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
 static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
 static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
 static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log";
 static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
 
 static constexpr const char* CACHE_ROOT = "/cache";
-static constexpr const char* DATA_ROOT = "/data";
-static constexpr const char* METADATA_ROOT = "/metadata";
-static constexpr const char* SDCARD_ROOT = "/sdcard";
 
 // We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed
 // into target_files.zip. Assert the version defined in code and in Android.mk are consistent.
 static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions.");
 
-bool modified_flash = false;
+static bool save_current_log = false;
 std::string stage;
 const char* reason = nullptr;
 
@@ -137,16 +128,6 @@
     return android::base::GetBoolProperty("ro.debuggable", false);
 }
 
-// Set the BCB to reboot back into recovery (it won't resume the install from
-// sdcard though).
-static void set_sdcard_update_bootloader_message() {
-  std::vector<std::string> options;
-  std::string err;
-  if (!update_bootloader_message(options, &err)) {
-    LOG(ERROR) << "Failed to set BCB message: " << err;
-  }
-}
-
 // Clear the recovery command and prepare to boot a (hopefully working) system,
 // copy our log file to cache as well (for the system to read). This function is
 // idempotent: call it as many times as you like.
@@ -163,7 +144,7 @@
     }
   }
 
-  copy_logs(modified_flash, has_cache);
+  copy_logs(save_current_log, has_cache, sehandle);
 
   // Reset to normal system boot so recovery won't cycle indefinitely.
   std::string err;
@@ -182,182 +163,6 @@
   sync();  // For good measure.
 }
 
-struct saved_log_file {
-  std::string name;
-  struct stat sb;
-  std::string data;
-};
-
-static bool erase_volume(const char* volume) {
-  bool is_cache = (strcmp(volume, CACHE_ROOT) == 0);
-  bool is_data = (strcmp(volume, DATA_ROOT) == 0);
-
-  ui->SetBackground(RecoveryUI::ERASING);
-  ui->SetProgressType(RecoveryUI::INDETERMINATE);
-
-  std::vector<saved_log_file> log_files;
-
-  if (is_cache) {
-    // If we're reformatting /cache, we load any past logs
-    // (i.e. "/cache/recovery/last_*") and the current log
-    // ("/cache/recovery/log") into memory, so we can restore them after
-    // the reformat.
-
-    ensure_path_mounted(volume);
-
-    struct dirent* de;
-    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(CACHE_LOG_DIR), closedir);
-    if (d) {
-      while ((de = readdir(d.get())) != nullptr) {
-        if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) {
-          std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name);
-
-          struct stat sb;
-          if (stat(path.c_str(), &sb) == 0) {
-            // truncate files to 512kb
-            if (sb.st_size > (1 << 19)) {
-              sb.st_size = 1 << 19;
-            }
-
-            std::string data(sb.st_size, '\0');
-            FILE* f = fopen(path.c_str(), "rbe");
-            fread(&data[0], 1, data.size(), f);
-            fclose(f);
-
-            log_files.emplace_back(saved_log_file{ path, sb, data });
-          }
-        }
-      }
-    } else {
-      if (errno != ENOENT) {
-        PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR;
-      }
-    }
-  }
-
-  ui->Print("Formatting %s...\n", volume);
-
-  ensure_path_unmounted(volume);
-
-  int result;
-  if (is_data && reason && strcmp(reason, "convert_fbe") == 0) {
-    static constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe";
-    static 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);
-  }
-
-  if (is_cache) {
-    // Re-create the log dir and write back the log entries.
-    if (ensure_path_mounted(CACHE_LOG_DIR) == 0 &&
-        mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) {
-      for (const auto& log : log_files) {
-        if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid,
-                                              log.sb.st_gid)) {
-          PLOG(ERROR) << "Failed to write to " << log.name;
-        }
-      }
-    } else {
-      PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR;
-    }
-
-    // Any part of the log we'd copied to cache is now gone.
-    // Reset the pointer so we copy from the beginning of the temp
-    // log.
-    reset_tmplog_offset();
-    copy_logs(modified_flash, has_cache);
-  }
-
-  return (result == 0);
-}
-
-// Sets the usb config to 'state'
-bool SetUsbConfig(const std::string& state) {
-  android::base::SetProperty("sys.usb.config", state);
-  return android::base::WaitForProperty("sys.usb.state", state);
-}
-
-// Returns the selected filename, or an empty string.
-static std::string browse_directory(const std::string& path, Device* device) {
-  ensure_path_mounted(path);
-
-  std::unique_ptr<DIR, decltype(&closedir)> d(opendir(path.c_str()), closedir);
-  if (!d) {
-    PLOG(ERROR) << "error opening " << path;
-    return "";
-  }
-
-  std::vector<std::string> dirs;
-  std::vector<std::string> entries{ "../" };  // "../" is always the first entry.
-
-  dirent* de;
-  while ((de = readdir(d.get())) != nullptr) {
-    std::string name(de->d_name);
-
-    if (de->d_type == DT_DIR) {
-      // Skip "." and ".." entries.
-      if (name == "." || name == "..") continue;
-      dirs.push_back(name + "/");
-    } else if (de->d_type == DT_REG && android::base::EndsWithIgnoreCase(name, ".zip")) {
-      entries.push_back(name);
-    }
-  }
-
-  std::sort(dirs.begin(), dirs.end());
-  std::sort(entries.begin(), entries.end());
-
-  // Append dirs to the entries list.
-  entries.insert(entries.end(), dirs.begin(), dirs.end());
-
-  std::vector<std::string> headers{ "Choose a package to install:", path };
-
-  size_t chosen_item = 0;
-  while (true) {
-    chosen_item = ui->ShowMenu(
-        headers, entries, chosen_item, true,
-        std::bind(&Device::HandleMenuKey, device, std::placeholders::_1, std::placeholders::_2));
-
-    // Return if WaitKey() was interrupted.
-    if (chosen_item == static_cast<size_t>(RecoveryUI::KeyError::INTERRUPTED)) {
-      return "";
-    }
-
-    const std::string& item = entries[chosen_item];
-    if (chosen_item == 0) {
-      // Go up but continue browsing (if the caller is browse_directory).
-      return "";
-    }
-
-    std::string new_path = path + "/" + item;
-    if (new_path.back() == '/') {
-      // Recurse down into a subdirectory.
-      new_path.pop_back();
-      std::string result = browse_directory(new_path, device);
-      if (!result.empty()) return result;
-    } else {
-      // Selected a zip file: return the path to the caller.
-      return new_path;
-    }
-  }
-
-  // Unreachable.
-}
-
 static bool yes_no(Device* device, const char* question1, const char* question2) {
   std::vector<std::string> headers{ question1, question2 };
   std::vector<std::string> items{ " No", " Yes" };
@@ -379,28 +184,6 @@
   return (chosen_item == 1);
 }
 
-// Return true on success.
-static bool wipe_data(Device* device) {
-    modified_flash = true;
-
-    ui->Print("\n-- Wiping data...\n");
-    bool success = device->PreWipeData();
-    if (success) {
-      success &= erase_volume(DATA_ROOT);
-      if (has_cache) {
-        success &= erase_volume(CACHE_ROOT);
-      }
-      if (volume_for_mount_point(METADATA_ROOT) != nullptr) {
-        success &= erase_volume(METADATA_ROOT);
-      }
-    }
-    if (success) {
-      success &= device->PostWipeData();
-    }
-    ui->Print("Data wipe %s.\n", success ? "complete" : "failed");
-    return success;
-}
-
 static InstallResult prompt_and_wipe_data(Device* device) {
   // Use a single string and let ScreenRecoveryUI handles the wrapping.
   std::vector<std::string> wipe_data_menu_headers{
@@ -428,7 +211,8 @@
     }
 
     if (ask_to_wipe_data(device)) {
-      if (wipe_data(device)) {
+      bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+      if (WipeData(device, convert_fbe)) {
         return INSTALL_SUCCESS;
       } else {
         return INSTALL_ERROR;
@@ -437,25 +221,6 @@
   }
 }
 
-// Return true on success.
-static bool wipe_cache(bool should_confirm, Device* device) {
-    if (!has_cache) {
-        ui->Print("No /cache partition found.\n");
-        return false;
-    }
-
-    if (should_confirm && !yes_no(device, "Wipe cache?", "  THIS CAN NOT BE UNDONE!")) {
-        return false;
-    }
-
-    modified_flash = true;
-
-    ui->Print("\n-- Wiping cache...\n");
-    bool success = erase_volume("/cache");
-    ui->Print("Cache wipe %s.\n", success ? "complete" : "failed");
-    return success;
-}
-
 // Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with
 // BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT.
 static bool secure_wipe_partition(const std::string& partition) {
@@ -497,45 +262,93 @@
   return true;
 }
 
-// Check if the wipe package matches expectation:
+static std::unique_ptr<Package> ReadWipePackage(size_t wipe_package_size) {
+  if (wipe_package_size == 0) {
+    LOG(ERROR) << "wipe_package_size is zero";
+    return nullptr;
+  }
+
+  std::string wipe_package;
+  std::string err_str;
+  if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
+    PLOG(ERROR) << "Failed to read wipe package" << err_str;
+    return nullptr;
+  }
+
+  return Package::CreateMemoryPackage(
+      std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
+}
+
+// Checks if the wipe package matches expectation. If the check passes, reads the list of
+// partitions to wipe from the package. Checks include
 // 1. verify the package.
 // 2. check metadata (ota-type, pre-device and serial number if having one).
-static bool check_wipe_package(size_t wipe_package_size) {
-    if (wipe_package_size == 0) {
-        LOG(ERROR) << "wipe_package_size is zero";
-        return false;
-    }
-    std::string wipe_package;
-    std::string err_str;
-    if (!read_wipe_package(&wipe_package, wipe_package_size, &err_str)) {
-        PLOG(ERROR) << "Failed to read wipe package";
-        return false;
-    }
-    if (!verify_package(reinterpret_cast<const unsigned char*>(wipe_package.data()),
-                        wipe_package.size())) {
-        LOG(ERROR) << "Failed to verify package";
-        return false;
-    }
+static bool CheckWipePackage(Package* wipe_package) {
+  if (!verify_package(wipe_package, ui)) {
+    LOG(ERROR) << "Failed to verify package";
+    return false;
+  }
 
-    // Extract metadata
-    ZipArchiveHandle zip;
-    int err = OpenArchiveFromMemory(static_cast<void*>(&wipe_package[0]), wipe_package.size(),
-                                    "wipe_package", &zip);
-    if (err != 0) {
-        LOG(ERROR) << "Can't open wipe package : " << ErrorCodeString(err);
-        return false;
+  ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
+  if (!zip) {
+    LOG(ERROR) << "Failed to get ZipArchiveHandle";
+    return false;
+  }
+
+  std::map<std::string, std::string> metadata;
+  if (!ReadMetadataFromPackage(zip, &metadata)) {
+    LOG(ERROR) << "Failed to parse metadata in the zip file";
+    return false;
+  }
+
+  return CheckPackageMetadata(metadata, OtaType::BRICK) == 0;
+}
+
+std::vector<std::string> GetWipePartitionList(Package* wipe_package) {
+  ZipArchiveHandle zip = wipe_package->GetZipArchiveHandle();
+  if (!zip) {
+    LOG(ERROR) << "Failed to get ZipArchiveHandle";
+    return {};
+  }
+
+  static constexpr const char* RECOVERY_WIPE_ENTRY_NAME = "recovery.wipe";
+
+  std::string partition_list_content;
+  ZipString path(RECOVERY_WIPE_ENTRY_NAME);
+  ZipEntry entry;
+  if (FindEntry(zip, path, &entry) == 0) {
+    uint32_t length = entry.uncompressed_length;
+    partition_list_content = std::string(length, '\0');
+    if (auto err = ExtractToMemory(
+            zip, &entry, reinterpret_cast<uint8_t*>(partition_list_content.data()), length);
+        err != 0) {
+      LOG(ERROR) << "Failed to extract " << RECOVERY_WIPE_ENTRY_NAME << ": "
+                 << ErrorCodeString(err);
+      return {};
     }
+  } else {
+    LOG(INFO) << "Failed to find " << RECOVERY_WIPE_ENTRY_NAME
+              << ", falling back to use the partition list on device.";
 
-    std::map<std::string, std::string> metadata;
-    if (!ReadMetadataFromPackage(zip, &metadata)) {
-      LOG(ERROR) << "Failed to parse metadata in the zip file";
-      return false;
+    static constexpr const char* RECOVERY_WIPE_ON_DEVICE = "/etc/recovery.wipe";
+    if (!android::base::ReadFileToString(RECOVERY_WIPE_ON_DEVICE, &partition_list_content)) {
+      PLOG(ERROR) << "failed to read \"" << RECOVERY_WIPE_ON_DEVICE << "\"";
+      return {};
     }
+  }
 
-    int result = CheckPackageMetadata(metadata, OtaType::BRICK);
-    CloseArchive(zip);
+  std::vector<std::string> result;
+  std::vector<std::string> lines = android::base::Split(partition_list_content, "\n");
+  for (const std::string& line : lines) {
+    std::string partition = android::base::Trim(line);
+    // Ignore '#' comment or empty lines.
+    if (android::base::StartsWith(partition, "#") || partition.empty()) {
+      continue;
+    }
+    result.push_back(line);
+  }
 
-    return result == 0;
+  return result;
 }
 
 // Wipes the current A/B device, with a secure wipe of all the partitions in RECOVERY_WIPE.
@@ -543,25 +356,24 @@
   ui->SetBackground(RecoveryUI::ERASING);
   ui->SetProgressType(RecoveryUI::INDETERMINATE);
 
-  if (!check_wipe_package(wipe_package_size)) {
+  auto wipe_package = ReadWipePackage(wipe_package_size);
+  if (!wipe_package) {
+    LOG(ERROR) << "Failed to open wipe package";
+    return false;
+  }
+
+  if (!CheckWipePackage(wipe_package.get())) {
     LOG(ERROR) << "Failed to verify wipe package";
     return false;
   }
-  static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe";
-  std::string partition_list;
-  if (!android::base::ReadFileToString(RECOVERY_WIPE, &partition_list)) {
-    LOG(ERROR) << "failed to read \"" << RECOVERY_WIPE << "\"";
+
+  auto partition_list = GetWipePartitionList(wipe_package.get());
+  if (partition_list.empty()) {
+    LOG(ERROR) << "Empty wipe ab partition list";
     return false;
   }
 
-  std::vector<std::string> lines = android::base::Split(partition_list, "\n");
-  for (const std::string& line : lines) {
-    std::string partition = android::base::Trim(line);
-    // Ignore '#' comment or empty lines.
-    if (android::base::StartsWith(partition, "#") || partition.empty()) {
-      continue;
-    }
-
+  for (const auto& partition : partition_list) {
     // Proceed anyway even if it fails to wipe some partition.
     secure_wipe_partition(partition);
   }
@@ -661,84 +473,6 @@
   ui->ShowText(true);
 }
 
-// How long (in seconds) we wait for the fuse-provided package file to
-// appear, before timing out.
-#define SDCARD_INSTALL_TIMEOUT 10
-
-static int apply_from_sdcard(Device* device, bool* wipe_cache) {
-    modified_flash = true;
-
-    if (ensure_path_mounted(SDCARD_ROOT) != 0) {
-        ui->Print("\n-- Couldn't mount %s.\n", SDCARD_ROOT);
-        return INSTALL_ERROR;
-    }
-
-    std::string path = browse_directory(SDCARD_ROOT, device);
-    if (path.empty()) {
-        ui->Print("\n-- No package file selected.\n");
-        ensure_path_unmounted(SDCARD_ROOT);
-        return INSTALL_ERROR;
-    }
-
-    ui->Print("\n-- Install %s ...\n", path.c_str());
-    set_sdcard_update_bootloader_message();
-
-    // We used to use fuse in a thread as opposed to a process. Since accessing
-    // through fuse involves going from kernel to userspace to kernel, it leads
-    // to deadlock when a page fault occurs. (Bug: 26313124)
-    pid_t child;
-    if ((child = fork()) == 0) {
-        bool status = start_sdcard_fuse(path.c_str());
-
-        _exit(status ? EXIT_SUCCESS : EXIT_FAILURE);
-    }
-
-    // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the fuse in child
-    // process is ready.
-    int result = INSTALL_ERROR;
-    int status;
-    bool waited = false;
-    for (int i = 0; i < SDCARD_INSTALL_TIMEOUT; ++i) {
-        if (waitpid(child, &status, WNOHANG) == -1) {
-            result = INSTALL_ERROR;
-            waited = true;
-            break;
-        }
-
-        struct stat sb;
-        if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &sb) == -1) {
-            if (errno == ENOENT && i < SDCARD_INSTALL_TIMEOUT-1) {
-                sleep(1);
-                continue;
-            } else {
-                LOG(ERROR) << "Timed out waiting for the fuse-provided package.";
-                result = INSTALL_ERROR;
-                kill(child, SIGKILL);
-                break;
-            }
-        }
-
-        result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/);
-        break;
-    }
-
-    if (!waited) {
-        // Calling stat() on this magic filename signals the fuse
-        // filesystem to shut down.
-        struct stat sb;
-        stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &sb);
-
-        waitpid(child, &status, 0);
-    }
-
-    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
-        LOG(ERROR) << "Error exit from the fuse process: " << WEXITSTATUS(status);
-    }
-
-    ensure_path_unmounted(SDCARD_ROOT);
-    return result;
-}
-
 // Returns REBOOT, SHUTDOWN, or REBOOT_BOOTLOADER. Returning NO_ACTION means to take the default,
 // which is to reboot or shutdown depending on if the --shutdown_after flag was passed to recovery.
 static Device::BuiltinAction prompt_and_wait(Device* device, int status) {
@@ -771,7 +505,6 @@
             ? Device::REBOOT
             : device->InvokeMenuItem(chosen_item);
 
-    bool should_wipe_cache = false;
     switch (chosen_action) {
       case Device::NO_ACTION:
         break;
@@ -784,48 +517,47 @@
         return chosen_action;
 
       case Device::WIPE_DATA:
+        save_current_log = true;
         if (ui->IsTextVisible()) {
           if (ask_to_wipe_data(device)) {
-            wipe_data(device);
+            WipeData(device, false);
           }
         } else {
-          wipe_data(device);
+          WipeData(device, false);
           return Device::NO_ACTION;
         }
         break;
 
-      case Device::WIPE_CACHE:
-        wipe_cache(ui->IsTextVisible(), device);
+      case Device::WIPE_CACHE: {
+        save_current_log = true;
+        std::function<bool()> confirm_func = [&device]() {
+          return yes_no(device, "Wipe cache?", "  THIS CAN NOT BE UNDONE!");
+        };
+        WipeCache(ui, ui->IsTextVisible() ? confirm_func : nullptr);
         if (!ui->IsTextVisible()) return Device::NO_ACTION;
         break;
-
+      }
       case Device::APPLY_ADB_SIDELOAD:
-      case Device::APPLY_SDCARD:
-        {
-          bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
-          if (adb) {
-            status = apply_from_adb(&should_wipe_cache);
-          } else {
-            status = apply_from_sdcard(device, &should_wipe_cache);
-          }
+      case Device::APPLY_SDCARD: {
+        save_current_log = true;
+        bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
+        if (adb) {
+          status = apply_from_adb(ui);
+        } else {
+          status = ApplyFromSdcard(device, ui);
+        }
 
-          if (status == INSTALL_SUCCESS && should_wipe_cache) {
-            if (!wipe_cache(false, device)) {
-              status = INSTALL_ERROR;
-            }
-          }
-
-          if (status != INSTALL_SUCCESS) {
-            ui->SetBackground(RecoveryUI::ERROR);
-            ui->Print("Installation aborted.\n");
-            copy_logs(modified_flash, has_cache);
-          } else if (!ui->IsTextVisible()) {
-            return Device::NO_ACTION;  // reboot if logs aren't visible
-          } else {
-            ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
-          }
+        if (status != INSTALL_SUCCESS) {
+          ui->SetBackground(RecoveryUI::ERROR);
+          ui->Print("Installation aborted.\n");
+          copy_logs(save_current_log, has_cache, sehandle);
+        } else if (!ui->IsTextVisible()) {
+          return Device::NO_ACTION;  // reboot if logs aren't visible
+        } else {
+          ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card");
         }
         break;
+      }
 
       case Device::VIEW_RECOVERY_LOGS:
         choose_recovery_file(device);
@@ -857,20 +589,6 @@
   printf("%s=%s\n", key, name);
 }
 
-void ui_print(const char* format, ...) {
-    std::string buffer;
-    va_list ap;
-    va_start(ap, format);
-    android::base::StringAppendV(&buffer, format, ap);
-    va_end(ap);
-
-    if (ui != nullptr) {
-        ui->Print("%s", buffer.c_str());
-    } else {
-        fputs(buffer.c_str(), stdout);
-    }
-}
-
 static bool is_battery_ok(int* required_battery_level) {
   using android::hardware::health::V1_0::BatteryStatus;
   using android::hardware::health::V2_0::get_health_service;
@@ -1119,7 +837,7 @@
   if (update_package != nullptr) {
     // It's not entirely true that we will modify the flash. But we want
     // to log the update attempt since update_package is non-NULL.
-    modified_flash = true;
+    save_current_log = true;
 
     int required_battery_level;
     if (retry_count == 0 && !is_battery_ok(&required_battery_level)) {
@@ -1141,10 +859,7 @@
         set_retry_bootloader_message(retry_count + 1, args);
       }
 
-      status = install_package(update_package, &should_wipe_cache, true, retry_count);
-      if (status == INSTALL_SUCCESS && should_wipe_cache) {
-        wipe_cache(false, device);
-      }
+      status = install_package(update_package, should_wipe_cache, true, retry_count, ui);
       if (status != INSTALL_SUCCESS) {
         ui->Print("Installation aborted.\n");
 
@@ -1152,7 +867,7 @@
         // RETRY_LIMIT times before we abandon this OTA update.
         static constexpr int RETRY_LIMIT = 4;
         if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
-          copy_logs(modified_flash, has_cache);
+          copy_logs(save_current_log, has_cache, sehandle);
           retry_count += 1;
           set_retry_bootloader_message(retry_count, args);
           // Print retry count on screen.
@@ -1176,12 +891,14 @@
       }
     }
   } else if (should_wipe_data) {
-    if (!wipe_data(device)) {
+    save_current_log = true;
+    bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0;
+    if (!WipeData(device, convert_fbe)) {
       status = INSTALL_ERROR;
     }
   } else if (should_prompt_and_wipe_data) {
     // Trigger the logging to capture the cause, even if user chooses to not wipe data.
-    modified_flash = true;
+    save_current_log = true;
 
     ui->ShowText(true);
     ui->SetBackground(RecoveryUI::ERROR);
@@ -1190,7 +907,8 @@
       ui->ShowText(false);
     }
   } else if (should_wipe_cache) {
-    if (!wipe_cache(false, device)) {
+    save_current_log = true;
+    if (!WipeCache(ui, nullptr)) {
       status = INSTALL_ERROR;
     }
   } else if (should_wipe_ab) {
@@ -1204,15 +922,11 @@
     // sideload finishes even if there are errors. Unless one turns on the
     // text display during the installation. This is to enable automated
     // testing.
+    save_current_log = true;
     if (!sideload_auto_reboot) {
       ui->ShowText(true);
     }
-    status = apply_from_adb(&should_wipe_cache);
-    if (status == INSTALL_SUCCESS && should_wipe_cache) {
-      if (!wipe_cache(false, device)) {
-        status = INSTALL_ERROR;
-      }
-    }
+    status = apply_from_adb(ui);
     ui->Print("\nInstall from ADB complete (status: %d).\n", status);
     if (sideload_auto_reboot) {
       ui->Print("Rebooting automatically.\n");
diff --git a/recovery.h b/recovery.h
index 00e22da..f050549 100644
--- a/recovery.h
+++ b/recovery.h
@@ -19,6 +19,6 @@
 #include <string>
 #include <vector>
 
-#include "device.h"
+#include "recovery_ui/device.h"
 
 Device::BuiltinAction start_recovery(Device* device, const std::vector<std::string>& args);
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 6f50802..5f3ab76 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -49,16 +49,16 @@
 #include <selinux/selinux.h>
 
 #include "common.h"
-#include "device.h"
 #include "fastboot/fastboot.h"
-#include "logging.h"
-#include "minadbd/minadbd.h"
+#include "install/wipe_data.h"
+#include "otautil/logging.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
 #include "recovery.h"
-#include "roots.h"
-#include "stub_ui.h"
-#include "ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/stub_ui.h"
+#include "recovery_ui/ui.h"
 
 static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
 static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale";
@@ -178,6 +178,12 @@
   return android::base::Trim(content);
 }
 
+// Sets the usb config to 'state'.
+static bool SetUsbConfig(const std::string& state) {
+  android::base::SetProperty("sys.usb.config", state);
+  return android::base::WaitForProperty("sys.usb.state", state);
+}
+
 static void ListenRecoverySocket(RecoveryUI* ui, std::atomic<Device::BuiltinAction>& action) {
   android::base::unique_fd sock_fd(android_get_control_socket("recovery"));
   if (sock_fd < 0) {
@@ -217,9 +223,10 @@
 }
 
 static void redirect_stdio(const char* filename) {
-  int pipefd[2];
-  if (pipe(pipefd) == -1) {
-    PLOG(ERROR) << "pipe failed";
+  android::base::unique_fd pipe_read, pipe_write;
+  // Create a pipe that allows parent process sending logs over.
+  if (!android::base::Pipe(&pipe_read, &pipe_write)) {
+    PLOG(ERROR) << "Failed to create pipe for redirecting stdio";
 
     // Fall back to traditional logging mode without timestamps. If these fail, there's not really
     // anywhere to complain...
@@ -233,7 +240,7 @@
 
   pid_t pid = fork();
   if (pid == -1) {
-    PLOG(ERROR) << "fork failed";
+    PLOG(ERROR) << "Failed to fork for redirecting stdio";
 
     // Fall back to traditional logging mode without timestamps. If these fail, there's not really
     // anywhere to complain...
@@ -246,8 +253,8 @@
   }
 
   if (pid == 0) {
-    /// Close the unused write end.
-    close(pipefd[1]);
+    // Child process reads the incoming logs and doesn't write to the pipe.
+    pipe_write.reset();
 
     auto start = std::chrono::steady_clock::now();
 
@@ -255,15 +262,13 @@
     FILE* log_fp = fopen(filename, "ae");
     if (log_fp == nullptr) {
       PLOG(ERROR) << "fopen \"" << filename << "\" failed";
-      close(pipefd[0]);
       _exit(EXIT_FAILURE);
     }
 
-    FILE* pipe_fp = fdopen(pipefd[0], "r");
+    FILE* pipe_fp = android::base::Fdopen(std::move(pipe_read), "r");
     if (pipe_fp == nullptr) {
       PLOG(ERROR) << "fdopen failed";
       check_and_fclose(log_fp, filename);
-      close(pipefd[0]);
       _exit(EXIT_FAILURE);
     }
 
@@ -283,25 +288,23 @@
 
     PLOG(ERROR) << "getline failed";
 
+    fclose(pipe_fp);
     free(line);
     check_and_fclose(log_fp, filename);
-    close(pipefd[0]);
     _exit(EXIT_FAILURE);
   } else {
     // Redirect stdout/stderr to the logger process. Close the unused read end.
-    close(pipefd[0]);
+    pipe_read.reset();
 
     setbuf(stdout, nullptr);
     setbuf(stderr, nullptr);
 
-    if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
+    if (dup2(pipe_write.get(), STDOUT_FILENO) == -1) {
       PLOG(ERROR) << "dup2 stdout failed";
     }
-    if (dup2(pipefd[1], STDERR_FILENO) == -1) {
+    if (dup2(pipe_write.get(), STDERR_FILENO) == -1) {
       PLOG(ERROR) << "dup2 stderr failed";
     }
-
-    close(pipefd[1]);
   }
 }
 
@@ -319,16 +322,6 @@
   // Take action to refresh pmsg contents
   __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate);
 
-  // If this binary is started with the single argument "--adbd", instead of being the normal
-  // recovery binary, it turns into kind of a stripped-down version of adbd that only supports the
-  // 'sideload' command.  Note this must be a real argument, not anything in the command file or
-  // bootloader control block; the only way recovery should be run with this argument is when it
-  // starts a copy of itself from the apply_from_adb() function.
-  if (argc == 2 && strcmp(argv[1], "--adbd") == 0) {
-    minadbd_main();
-    return 0;
-  }
-
   time_t start = time(nullptr);
 
   // redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger
@@ -442,6 +435,8 @@
     ui->Print("Warning: No file_contexts\n");
   }
 
+  SetLoggingSehandle(sehandle);
+
   std::atomic<Device::BuiltinAction> action;
   std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action));
   listener_thread.detach();
@@ -458,6 +453,8 @@
       }
     }
 
+    ui->SetEnableFastbootdLogo(fastboot);
+
     auto ret = fastboot ? StartFastboot(device, args) : start_recovery(device, args);
 
     if (ret == Device::KEY_INTERRUPTED) {
diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp
new file mode 100644
index 0000000..ee3149d
--- /dev/null
+++ b/recovery_ui/Android.bp
@@ -0,0 +1,91 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+    name: "librecovery_ui",
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "device.cpp",
+        "screen_ui.cpp",
+        "ui.cpp",
+        "vr_ui.cpp",
+        "wear_ui.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+
+    static_libs: [
+        "libminui",
+        "libotautil",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libpng",
+        "libz",
+    ],
+}
+
+// Generic device that uses ScreenRecoveryUI.
+cc_library_static {
+    name: "librecovery_ui_default",
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "default_device.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+// The default wear device that uses WearRecoveryUI.
+cc_library_static {
+    name: "librecovery_ui_wear",
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "wear_device.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+}
+
+// The default VR device that uses VrRecoveryUI.
+cc_library_static {
+    name: "librecovery_ui_vr",
+    recovery_available: true,
+
+    defaults: [
+        "recovery_defaults",
+    ],
+
+    srcs: [
+        "vr_device.cpp",
+    ],
+
+    export_include_dirs: ["include"],
+}
diff --git a/default_device.cpp b/recovery_ui/default_device.cpp
similarity index 90%
rename from default_device.cpp
rename to recovery_ui/default_device.cpp
index a971866..4db461a 100644
--- a/default_device.cpp
+++ b/recovery_ui/default_device.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#include "device.h"
-#include "screen_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/screen_ui.h"
 
 Device* make_device() {
   return new Device(new ScreenRecoveryUI);
diff --git a/device.cpp b/recovery_ui/device.cpp
similarity index 97%
rename from device.cpp
rename to recovery_ui/device.cpp
index eec1932..ddb0118 100644
--- a/device.cpp
+++ b/recovery_ui/device.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "device.h"
+#include "recovery_ui/device.h"
 
 #include <algorithm>
 #include <string>
@@ -23,7 +23,7 @@
 
 #include <android-base/logging.h>
 
-#include "ui.h"
+#include "recovery_ui/ui.h"
 
 static std::vector<std::pair<std::string, Device::BuiltinAction>> g_menu_actions{
   { "Reboot system now", Device::REBOOT },
diff --git a/device.h b/recovery_ui/include/recovery_ui/device.h
similarity index 95%
rename from device.h
rename to recovery_ui/include/recovery_ui/device.h
index 6a8daf8..3c44510 100644
--- a/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -68,7 +68,7 @@
 
   // Called when recovery starts up (after the UI has been obtained and initialized and after the
   // arguments have been parsed, but before anything else).
-  virtual void StartRecovery() {};
+  virtual void StartRecovery() {}
 
   // Called from the main thread when recovery is at the main menu and waiting for input, and a key
   // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible;
@@ -93,8 +93,8 @@
 
   // Performs a recovery action selected from the menu. 'menu_position' will be the index of the
   // selected menu item, or a non-negative value returned from HandleMenuKey(). The menu will be
-  // hidden when this is called; implementations can call ui_print() to print information to the
-  // screen. If the menu position is one of the builtin actions, you can just return the
+  // hidden when this is called; implementations can call GetUI()->Print() to print information to
+  // the screen. If the menu position is one of the builtin actions, you can just return the
   // corresponding enum value. If it is an action specific to your device, you actually perform it
   // here and return NO_ACTION.
   virtual BuiltinAction InvokeMenuItem(size_t menu_position);
diff --git a/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h
similarity index 99%
rename from screen_ui.h
rename to recovery_ui/include/recovery_ui/screen_ui.h
index acd44c8..5cda2a2 100644
--- a/screen_ui.h
+++ b/recovery_ui/include/recovery_ui/screen_ui.h
@@ -345,6 +345,8 @@
   std::unique_ptr<GRSurface> wipe_data_confirmation_text_;
   std::unique_ptr<GRSurface> wipe_data_menu_header_text_;
 
+  std::unique_ptr<GRSurface> fastbootd_logo_;
+
   // current_icon_ points to one of the frames in intro_frames_ or loop_frames_, indexed by
   // current_frame_, or error_icon_.
   Icon current_icon_;
diff --git a/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h
similarity index 100%
rename from stub_ui.h
rename to recovery_ui/include/recovery_ui/stub_ui.h
diff --git a/ui.h b/recovery_ui/include/recovery_ui/ui.h
similarity index 96%
rename from ui.h
rename to recovery_ui/include/recovery_ui/ui.h
index 4924fec..d55322c 100644
--- a/ui.h
+++ b/recovery_ui/include/recovery_ui/ui.h
@@ -35,20 +35,20 @@
     INSTALLING_UPDATE,
     ERASING,
     NO_COMMAND,
-    ERROR
+    ERROR,
   };
 
   enum ProgressType {
     EMPTY,
     INDETERMINATE,
-    DETERMINATE
+    DETERMINATE,
   };
 
   enum KeyAction {
     ENQUEUE,
     TOGGLE,
     REBOOT,
-    IGNORE
+    IGNORE,
   };
 
   enum class KeyError : int {
@@ -60,8 +60,8 @@
 
   virtual ~RecoveryUI();
 
-  // Initializes the object; called before anything else. UI texts will be initialized according to
-  // the given locale. Returns true on success.
+  // Initializes the object; called before anything else. UI texts will be initialized according
+  // to the given locale. Returns true on success.
   virtual bool Init(const std::string& locale);
 
   virtual std::string GetLocale() const = 0;
@@ -168,7 +168,6 @@
   virtual size_t ShowPromptWipeDataMenu(const std::vector<std::string>& backup_headers,
                                         const std::vector<std::string>& backup_items,
                                         const std::function<int(int, bool)>& key_handler) = 0;
-
   // Displays the localized wipe data confirmation menu with pre-generated images. Falls back to
   // the text strings upon failures. The initial selection is the 0th item, which returns to the
   // upper level menu.
@@ -176,6 +175,11 @@
       const std::vector<std::string>& backup_headers, const std::vector<std::string>& backup_items,
       const std::function<int(int, bool)>& key_handler) = 0;
 
+  // Set whether or not the fastbootd logo is displayed.
+  void SetEnableFastbootdLogo(bool enable) {
+    fastbootd_logo_enabled_ = enable;
+  }
+
   // Resets the key interrupt status.
   void ResetKeyInterruptStatus() {
     key_interrupted_ = false;
@@ -200,12 +204,14 @@
   // Whether we should listen for touch inputs (default: false).
   bool touch_screen_allowed_;
 
+  bool fastbootd_logo_enabled_;
+
  private:
   enum class ScreensaverState {
     DISABLED,
     NORMAL,
     DIMMED,
-    OFF
+    OFF,
   };
 
   // The sensitivity when detecting a swipe.
diff --git a/vr_ui.h b/recovery_ui/include/recovery_ui/vr_ui.h
similarity index 100%
rename from vr_ui.h
rename to recovery_ui/include/recovery_ui/vr_ui.h
diff --git a/wear_ui.h b/recovery_ui/include/recovery_ui/wear_ui.h
similarity index 100%
rename from wear_ui.h
rename to recovery_ui/include/recovery_ui/wear_ui.h
diff --git a/screen_ui.cpp b/recovery_ui/screen_ui.cpp
similarity index 97%
rename from screen_ui.cpp
rename to recovery_ui/screen_ui.cpp
index 6c00a22..870db62 100644
--- a/screen_ui.cpp
+++ b/recovery_ui/screen_ui.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "screen_ui.h"
+#include "recovery_ui/screen_ui.h"
 
 #include <dirent.h>
 #include <errno.h>
@@ -42,10 +42,10 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 
-#include "device.h"
 #include "minui/minui.h"
 #include "otautil/paths.h"
-#include "ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/ui.h"
 
 // Return the current time as a double (including fractions of a second).
 static double now() {
@@ -388,10 +388,10 @@
 enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX };
 enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX };
 static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = {
-  { 32,  68, },  // PORTRAIT
-  { 32,  68, },  // PORTRAIT_LARGE
-  { 26,  56, },  // LANDSCAPE
-  { 52, 112, },  // LANDSCAPE_LARGE
+  { 32, 68 },   // PORTRAIT
+  { 32, 68 },   // PORTRAIT_LARGE
+  { 26, 56 },   // LANDSCAPE
+  { 52, 112 },  // LANDSCAPE_LARGE
 };
 
 int ScreenRecoveryUI::GetAnimationBaseline() const {
@@ -701,6 +701,16 @@
 void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(
     const std::vector<std::string>& help_message) {
   int y = margin_height_;
+
+  if (fastbootd_logo_ && fastbootd_logo_enabled_) {
+    // Try to get this centered on screen.
+    auto width = gr_get_width(fastbootd_logo_.get());
+    auto height = gr_get_height(fastbootd_logo_.get());
+    auto centered_x = ScreenWidth() / 2 - width / 2;
+    DrawSurface(fastbootd_logo_.get(), 0, 0, width, height, centered_x, y);
+    y += height;
+  }
+
   if (menu_) {
     int x = margin_width_ + kMenuIndent;
 
@@ -890,6 +900,10 @@
   no_command_text_ = LoadLocalizedBitmap("no_command_text");
   error_text_ = LoadLocalizedBitmap("error_text");
 
+  if (android::base::GetBoolProperty("ro.boot.dynamic_partitions", false)) {
+    fastbootd_logo_ = LoadBitmap("fastbootd");
+  }
+
   // Background text for "installing_update" could be "installing update" or
   // "installing security update". It will be set after Init() according to the commands in BCB.
   installing_text_.reset();
@@ -1028,7 +1042,7 @@
   va_end(ap);
 }
 
-void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) {
+void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) {
   va_list ap;
   va_start(ap, fmt);
   PrintV(fmt, false, ap);
diff --git a/ui.cpp b/recovery_ui/ui.cpp
similarity index 98%
rename from ui.cpp
rename to recovery_ui/ui.cpp
index 14b5b09..b7107ff 100644
--- a/ui.cpp
+++ b/recovery_ui/ui.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "ui.h"
+#include "recovery_ui/ui.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -39,7 +39,6 @@
 
 #include "minui/minui.h"
 #include "otautil/sysutil.h"
-#include "roots.h"
 
 using namespace std::chrono_literals;
 
@@ -59,6 +58,7 @@
       brightness_file_(BRIGHTNESS_FILE),
       max_brightness_file_(MAX_BRIGHTNESS_FILE),
       touch_screen_allowed_(false),
+      fastbootd_logo_enabled_(false),
       touch_low_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_low_threshold",
                                                          kDefaultTouchLowThreshold)),
       touch_high_threshold_(android::base::GetIntProperty("ro.recovery.ui.touch_high_threshold",
@@ -589,8 +589,7 @@
   return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE;
 }
 
-void RecoveryUI::KeyLongPress(int) {
-}
+void RecoveryUI::KeyLongPress(int) {}
 
 void RecoveryUI::SetEnableReboot(bool enabled) {
   std::lock_guard<std::mutex> lg(key_queue_mutex);
diff --git a/vr_device.cpp b/recovery_ui/vr_device.cpp
similarity index 86%
rename from vr_device.cpp
rename to recovery_ui/vr_device.cpp
index 61e15cb..fd76133 100644
--- a/vr_device.cpp
+++ b/recovery_ui/vr_device.cpp
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-#include "device.h"
-#include "vr_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/vr_ui.h"
 
 Device* make_device() {
-    return new Device(new VrRecoveryUI);
+  return new Device(new VrRecoveryUI);
 }
-
diff --git a/vr_ui.cpp b/recovery_ui/vr_ui.cpp
similarity index 98%
rename from vr_ui.cpp
rename to recovery_ui/vr_ui.cpp
index 1f0292c..5b9b1b4 100644
--- a/vr_ui.cpp
+++ b/recovery_ui/vr_ui.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "vr_ui.h"
+#include "recovery_ui/vr_ui.h"
 
 #include <android-base/properties.h>
 
diff --git a/wear_device.cpp b/recovery_ui/wear_device.cpp
similarity index 90%
rename from wear_device.cpp
rename to recovery_ui/wear_device.cpp
index 3268130..bf21bc9 100644
--- a/wear_device.cpp
+++ b/recovery_ui/wear_device.cpp
@@ -14,10 +14,9 @@
  * limitations under the License.
  */
 
-#include "device.h"
-#include "wear_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/wear_ui.h"
 
 Device* make_device() {
   return new Device(new WearRecoveryUI);
 }
-
diff --git a/wear_ui.cpp b/recovery_ui/wear_ui.cpp
similarity index 98%
rename from wear_ui.cpp
rename to recovery_ui/wear_ui.cpp
index 6da84c9..8d8108f 100644
--- a/wear_ui.cpp
+++ b/recovery_ui/wear_ui.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "wear_ui.h"
+#include "recovery_ui/wear_ui.h"
 
 #include <string.h>
 
diff --git a/res-hdpi/images/fastbootd.png b/res-hdpi/images/fastbootd.png
new file mode 100644
index 0000000..5127b3b
--- /dev/null
+++ b/res-hdpi/images/fastbootd.png
Binary files differ
diff --git a/res-mdpi/images/fastbootd.png b/res-mdpi/images/fastbootd.png
new file mode 100644
index 0000000..c1b7bc2
--- /dev/null
+++ b/res-mdpi/images/fastbootd.png
Binary files differ
diff --git a/res-xhdpi/images/fastbootd.png b/res-xhdpi/images/fastbootd.png
new file mode 100644
index 0000000..e3da85a
--- /dev/null
+++ b/res-xhdpi/images/fastbootd.png
Binary files differ
diff --git a/res-xxhdpi/images/fastbootd.png b/res-xxhdpi/images/fastbootd.png
new file mode 100644
index 0000000..482dbb2
--- /dev/null
+++ b/res-xxhdpi/images/fastbootd.png
Binary files differ
diff --git a/res-xxxhdpi/images/fastbootd.png b/res-xxxhdpi/images/fastbootd.png
new file mode 100644
index 0000000..a878b1f
--- /dev/null
+++ b/res-xxxhdpi/images/fastbootd.png
Binary files differ
diff --git a/tests/Android.bp b/tests/Android.bp
index 898ed7d..09ef716 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -76,8 +76,9 @@
 librecovery_static_libs = [
     "librecovery",
     "librecovery_fastboot",
+    "libinstall",
+    "librecovery_ui",
     "libminui",
-    "libverifier",
     "libotautil",
 
     "libhealthhalutils",
@@ -115,9 +116,9 @@
     ],
 
     static_libs: libapplypatch_static_libs + [
+        "libinstall",
         "librecovery_ui",
         "libminui",
-        "libverifier",
         "libotautil",
         "libupdater",
         "libgtest_prod",
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
index 47a5471..3851329 100644
--- a/tests/component/install_test.cpp
+++ b/tests/component/install_test.cpp
@@ -32,9 +32,9 @@
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_writer.h>
 
-#include "install.h"
+#include "install/install.h"
 #include "otautil/paths.h"
-#include "private/install.h"
+#include "private/setup_commands.h"
 
 static void BuildZipArchive(const std::map<std::string, std::string>& file_map, int fd,
                             int compression_type) {
@@ -107,6 +107,32 @@
   CloseArchive(zip);
 }
 
+TEST(InstallTest, read_wipe_ab_partition_list) {
+  std::vector<std::string> partition_list = {
+    "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b",
+    "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b",
+    "/dev/block/bootdevice/by-name/userdata", "# Wipe the boot partitions last",
+    "/dev/block/bootdevice/by-name/boot_a",   "/dev/block/bootdevice/by-name/boot_b",
+  };
+  TemporaryFile temp_file;
+  BuildZipArchive({ { "recovery.wipe", android::base::Join(partition_list, '\n') } },
+                  temp_file.release(), kCompressDeflated);
+  std::string wipe_package;
+  ASSERT_TRUE(android::base::ReadFileToString(temp_file.path, &wipe_package));
+
+  auto package = Package::CreateMemoryPackage(
+      std::vector<uint8_t>(wipe_package.begin(), wipe_package.end()), nullptr);
+
+  auto read_partition_list = GetWipePartitionList(package.get());
+  std::vector<std::string> expected = {
+    "/dev/block/bootdevice/by-name/system_a", "/dev/block/bootdevice/by-name/system_b",
+    "/dev/block/bootdevice/by-name/vendor_a", "/dev/block/bootdevice/by-name/vendor_b",
+    "/dev/block/bootdevice/by-name/userdata", "/dev/block/bootdevice/by-name/boot_a",
+    "/dev/block/bootdevice/by-name/boot_b",
+  };
+  ASSERT_EQ(expected, read_partition_list);
+}
+
 TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) {
   TemporaryFile compatibility_zip_file;
   std::string malformed_xml = "malformed";
diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp
index d5e074c..6add99f 100644
--- a/tests/component/sideload_test.cpp
+++ b/tests/component/sideload_test.cpp
@@ -16,6 +16,7 @@
 
 #include <unistd.h>
 
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -23,21 +24,34 @@
 #include <android-base/strings.h>
 #include <gtest/gtest.h>
 
+#include "fuse_provider.h"
 #include "fuse_sideload.h"
 
 TEST(SideloadTest, fuse_device) {
   ASSERT_EQ(0, access("/dev/fuse", R_OK | W_OK));
 }
 
+class FuseTestDataProvider : public FuseDataProvider {
+ public:
+  FuseTestDataProvider(uint64_t file_size, uint32_t block_size)
+      : FuseDataProvider(file_size, block_size) {}
+
+ private:
+  bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override {
+    return true;
+  }
+};
+
 TEST(SideloadTest, run_fuse_sideload_wrong_parameters) {
-  provider_vtab vtab;
-  vtab.close = [](void) {};
+  auto provider_small_block = std::make_unique<FuseTestDataProvider>(4096, 4095);
+  ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_small_block)));
 
-  ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, 4095));
-  ASSERT_EQ(-1, run_fuse_sideload(vtab, 4096, (1 << 22) + 1));
+  auto provider_large_block = std::make_unique<FuseTestDataProvider>(4096, (1 << 22) + 1);
+  ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_large_block)));
 
-  // Too many blocks.
-  ASSERT_EQ(-1, run_fuse_sideload(vtab, ((1 << 18) + 1) * 4096, 4096));
+  auto provider_too_many_blocks =
+      std::make_unique<FuseTestDataProvider>(((1 << 18) + 1) * 4096, 4096);
+  ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_too_many_blocks)));
 }
 
 TEST(SideloadTest, run_fuse_sideload) {
@@ -50,18 +64,15 @@
   const std::string content = android::base::Join(blocks, "");
   ASSERT_EQ(16384U, content.size());
 
-  provider_vtab vtab;
-  vtab.close = [](void) {};
-  vtab.read_block = [&blocks](uint32_t block, uint8_t* buffer, uint32_t fetch_size) {
-    if (block >= 4) return -1;
-    blocks[block].copy(reinterpret_cast<char*>(buffer), fetch_size);
-    return 0;
-  };
+  TemporaryFile temp_file;
+  ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path));
 
+  auto provider = std::make_unique<FuseFileDataProvider>(temp_file.path, 4096);
+  ASSERT_TRUE(provider->Valid());
   TemporaryDir mount_point;
   pid_t pid = fork();
   if (pid == 0) {
-    ASSERT_EQ(0, run_fuse_sideload(vtab, 16384, 4096, mount_point.path));
+    ASSERT_EQ(0, run_fuse_sideload(std::move(provider), mount_point.path));
     _exit(EXIT_SUCCESS);
   }
 
diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp
index 0a59403..e27e58c 100644
--- a/tests/component/update_verifier_test.cpp
+++ b/tests/component/update_verifier_test.cpp
@@ -98,7 +98,7 @@
   ASSERT_FALSE(verifier_.ParseCareMap());
 }
 
-TEST_F(UpdateVerifierTest, verify_image_smoke) {
+TEST_F(UpdateVerifierTest, verify_image_text_format) {
   // This test relies on dm-verity support.
   if (!verity_supported) {
     GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
@@ -107,52 +107,14 @@
 
   std::string content = "system\n2,0,1";
   ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_));
-  ASSERT_TRUE(verifier_.ParseCareMap());
-  ASSERT_TRUE(verifier_.VerifyPartitions());
-
-  // Leading and trailing newlines should be accepted.
-  ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", care_map_txt_));
-  ASSERT_TRUE(verifier_.ParseCareMap());
-  ASSERT_TRUE(verifier_.VerifyPartitions());
+  // CareMap in text format is no longer supported.
+  ASSERT_FALSE(verifier_.ParseCareMap());
 }
 
 TEST_F(UpdateVerifierTest, verify_image_empty_care_map) {
   ASSERT_FALSE(verifier_.ParseCareMap());
 }
 
-TEST_F(UpdateVerifierTest, verify_image_wrong_lines) {
-  // The care map file can have only 2 / 4 / 6 lines.
-  ASSERT_TRUE(android::base::WriteStringToFile("line1", care_map_txt_));
-  ASSERT_FALSE(verifier_.ParseCareMap());
-
-  ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", care_map_txt_));
-  ASSERT_FALSE(verifier_.ParseCareMap());
-}
-
-TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) {
-  // This test relies on dm-verity support.
-  if (!verity_supported) {
-    GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
-    return;
-  }
-
-  std::string content = "system\n2,1,0";
-  ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_));
-  ASSERT_FALSE(verifier_.ParseCareMap());
-}
-
-TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) {
-  // This test relies on dm-verity support.
-  if (!verity_supported) {
-    GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support.";
-    return;
-  }
-
-  std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0";
-  ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_));
-  ASSERT_FALSE(verifier_.ParseCareMap());
-}
-
 TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) {
   // This test relies on dm-verity support.
   if (!verity_supported) {
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index 9fcaa0b..ded23c5 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -35,8 +35,9 @@
 #include <ziparchive/zip_writer.h>
 
 #include "common/test_constants.h"
+#include "install/package.h"
+#include "install/verifier.h"
 #include "otautil/sysutil.h"
-#include "verifier.h"
 
 using namespace std::string_literals;
 
@@ -47,15 +48,22 @@
       std::vector<uint8_t>(testkey_string.begin(), testkey_string.end()), cert));
 }
 
+static void VerifyFile(const std::string& content, const std::vector<Certificate>& keys,
+                       int expected) {
+  auto package =
+      Package::CreateMemoryPackage(std::vector<uint8_t>(content.begin(), content.end()), nullptr);
+  ASSERT_NE(nullptr, package);
+
+  ASSERT_EQ(expected, verify_file(package.get(), keys));
+}
+
 static void VerifyPackageWithCertificates(const std::string& name,
                                           const std::vector<Certificate>& certs) {
-  std::string package = from_testdata_base(name);
-  MemMapping memmap;
-  if (!memmap.MapFile(package)) {
-    FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
-  }
+  std::string path = from_testdata_base(name);
+  auto package = Package::CreateMemoryPackage(path, nullptr);
+  ASSERT_NE(nullptr, package);
 
-  ASSERT_EQ(VERIFY_SUCCESS, verify_file(memmap.addr, memmap.length, certs));
+  ASSERT_EQ(VERIFY_SUCCESS, verify_file(package.get(), certs));
 }
 
 static void VerifyPackageWithSingleCertificate(const std::string& name, Certificate&& cert) {
@@ -150,6 +158,17 @@
   VerifyPackageWithSingleCertificate("otasigned_v5.zip", std::move(cert));
 }
 
+TEST(VerifierTest, LoadCertificateFromBuffer_sha256_rsa4096_bits) {
+  Certificate cert(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
+  LoadKeyFromFile(from_testdata_base("testkey_4096bits.x509.pem"), &cert);
+
+  ASSERT_EQ(SHA256_DIGEST_LENGTH, cert.hash_len);
+  ASSERT_EQ(Certificate::KEY_TYPE_RSA, cert.key_type);
+  ASSERT_EQ(nullptr, cert.ec);
+
+  VerifyPackageWithSingleCertificate("otasigned_4096bits.zip", std::move(cert));
+}
+
 TEST(VerifierTest, LoadCertificateFromBuffer_check_rsa_keys) {
   std::unique_ptr<RSA, RSADeleter> rsa(RSA_new());
   std::unique_ptr<BIGNUM, decltype(&BN_free)> exponent(BN_new(), BN_free);
@@ -231,20 +250,22 @@
  protected:
   void SetUp() override {
     std::vector<std::string> args = GetParam();
-    std::string package = from_testdata_base(args[0]);
-    if (!memmap.MapFile(package)) {
-      FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n";
-    }
+    std::string path = from_testdata_base(args[0]);
+    memory_package_ = Package::CreateMemoryPackage(path, nullptr);
+    ASSERT_NE(nullptr, memory_package_);
+    file_package_ = Package::CreateFilePackage(path, nullptr);
+    ASSERT_NE(nullptr, file_package_);
 
     for (auto it = ++args.cbegin(); it != args.cend(); ++it) {
       std::string public_key_file = from_testdata_base("testkey_" + *it + ".x509.pem");
-      certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
-      LoadKeyFromFile(public_key_file, &certs.back());
+      certs_.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr);
+      LoadKeyFromFile(public_key_file, &certs_.back());
     }
   }
 
-  MemMapping memmap;
-  std::vector<Certificate> certs;
+  std::unique_ptr<Package> memory_package_;
+  std::unique_ptr<Package> file_package_;
+  std::vector<Certificate> certs_;
 };
 
 class VerifierSuccessTest : public VerifierTest {
@@ -264,9 +285,7 @@
 
   // Alter the footer.
   package[package.size() - 5] = '\x05';
-  ASSERT_EQ(VERIFY_FAILURE,
-            verify_file(reinterpret_cast<const unsigned char*>(package.data()), package.size(),
-                        certs));
+  VerifyFile(package, certs, VERIFY_FAILURE);
 }
 
 TEST(VerifierTest, BadPackage_AlteredContent) {
@@ -281,15 +300,11 @@
   // Alter the content.
   std::string altered1(package);
   altered1[50] += 1;
-  ASSERT_EQ(VERIFY_FAILURE,
-            verify_file(reinterpret_cast<const unsigned char*>(altered1.data()), altered1.size(),
-                        certs));
+  VerifyFile(altered1, certs, VERIFY_FAILURE);
 
   std::string altered2(package);
   altered2[10] += 1;
-  ASSERT_EQ(VERIFY_FAILURE,
-            verify_file(reinterpret_cast<const unsigned char*>(altered2.data()), altered2.size(),
-                        certs));
+  VerifyFile(altered2, certs, VERIFY_FAILURE);
 }
 
 TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) {
@@ -299,16 +314,17 @@
 
   // Signature start is 65535 (0xffff) while comment size is 0 (Bug: 31914369).
   std::string package = "\x50\x4b\x05\x06"s + std::string(12, '\0') + "\xff\xff\xff\xff\x00\x00"s;
-  ASSERT_EQ(VERIFY_FAILURE, verify_file(reinterpret_cast<const unsigned char*>(package.data()),
-                                        package.size(), certs));
+  VerifyFile(package, certs, VERIFY_FAILURE);
 }
 
 TEST_P(VerifierSuccessTest, VerifySucceed) {
-  ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_SUCCESS);
+  ASSERT_EQ(VERIFY_SUCCESS, verify_file(memory_package_.get(), certs_));
+  ASSERT_EQ(VERIFY_SUCCESS, verify_file(file_package_.get(), certs_));
 }
 
 TEST_P(VerifierFailureTest, VerifyFailure) {
-  ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_FAILURE);
+  ASSERT_EQ(VERIFY_FAILURE, verify_file(memory_package_.get(), certs_));
+  ASSERT_EQ(VERIFY_FAILURE, verify_file(file_package_.get(), certs_));
 }
 
 INSTANTIATE_TEST_CASE_P(SingleKeySuccess, VerifierSuccessTest,
diff --git a/tests/testdata/otasigned_4096bits.zip b/tests/testdata/otasigned_4096bits.zip
new file mode 100644
index 0000000..5016dfc
--- /dev/null
+++ b/tests/testdata/otasigned_4096bits.zip
Binary files differ
diff --git a/tests/testdata/testkey_4096bits.x509.pem b/tests/testdata/testkey_4096bits.x509.pem
new file mode 100644
index 0000000..cba30d6
--- /dev/null
+++ b/tests/testdata/testkey_4096bits.x509.pem
@@ -0,0 +1,35 @@
+-----BEGIN CERTIFICATE-----
+MIIGADCCA+igAwIBAgIJAJiRMVvanGUaMA0GCSqGSIb3DQEBCwUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4g
+VmlldzEQMA4GA1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UE
+AwwHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0xODEwMzAxMjEzNTFaFw00NjAzMTcxMjEzNTFaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEQMA4G
+A1UECgwHQW5kcm9pZDEQMA4GA1UECwwHQW5kcm9pZDEQMA4GA1UEAwwHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCAiIwDQYJKoZI
+hvcNAQEBBQADggIPADCCAgoCggIBAL3ghKA8Gz9qOORY8gMY4wlB2tCJLDUO2tFG
+LVK1UphtQMp+YEcz/0VQKVV7de7z6V4EMQ5P1HbxHOsjcKn/zXAl4YgFt7b5kZbC
+bpNK4CYHEfho3j6fpYtq5d9q8rIA2kI0uZkkqPy1zXKTl2C2PjOoAnLQRk5xBVQG
+M10/wYsf7yX36mSWoJJwKPp/EzVFpA+hX8HpljeIiZ6CFzKwJdqv9zO/xzfp6NsX
+Tv5EGdkDxmw3qQqKgyl8dLMTZ/2zNfvVOMeZDusEPDF7A/lbU1byLWrKQdCzVb40
+yc7BCSRGYwM29R/byOcgD+lslwKSGzgzNmQXICt1tXz9bSJR8qh4tlAaiRc3ZKBe
+hJWIFGkGtD/cDGtDE5DbNAOz6CdSDdE2XN0Qf0cfN1RHVE6fo2FtFicRRVuFBt8M
+2cbQ7bzmEvtHD6W6dsf120FH7gppXKmnhMx1WazpxR2QltbiYDTy2ZZi4paS/jDB
+fL9gMCWp3Ohg2y74NGfUw5CQWQsDpcki6I7RvwClBCyOV51LHn5LE/nY4DkVrZxk
+Pw0/YrTWz5J5PbdMetTuIunE4ec4lm8nZnh1ET+2MHx2+RoyF5vBs4rp1KHHRaEA
+veD2AfQOWxz7kOG9+akFot7n+QoWEGdwY0mJ9jsO/IITCjv3VbD7o0OoJv1R2AW5
+sK2KQ4PDAgMBAAGjUzBRMB0GA1UdDgQWBBT2EbrayXGhY6VCvSlLtRNyjW9ceDAf
+BgNVHSMEGDAWgBT2EbrayXGhY6VCvSlLtRNyjW9ceDAPBgNVHRMBAf8EBTADAQH/
+MA0GCSqGSIb3DQEBCwUAA4ICAQC7SsWap9zDKmuR0qMUZ6wlualnag0hUG1jZHQP
+t63KO6LmNNMSuXRX60Zcq6WWzgLOyoT4HqHZZ47Jamfb4XQQcnWMMW0tJ3pDtTkz
+dZILBInHJO8QPYI8Du6XWsDLSvMajq6ueBtO3NdcgsNL7eiHf3WoOtajLZxFM94Z
+MESkUQOIsqHolYeTMHLTsuGkX1CK2Zw3Xn18bUSTYwZCHa6mYH00ItUBfetGCnWh
+Y7bth/R15Cc+hocSB7ZsOa/R5kDyDdFDIKrnV5nH5Yd7CryrYC6Ac5UarYrxSJTq
+eKPwqUlJB/tJW/lvdLt8YaURbFGzf/ZqU12zZRafYjmMjcQvfpzMoDSnbvHTA9IR
+ZGO7dwhwykoSaL4/8LWde49xQUq6F2pQBRmEr+7mTzml1MaM5cWEk5emkCMXgLog
+k+c56CAk1EdM1teWik7wR0TIqkkYyYJHTSg61GkXUIXrZJ6iYx2ejDg1+QTPm9rU
+Yr7nP52gVkQuUAX1+xB6wKLSDizQJw8SNiUGXl5+2vwV6+0BI3/CXlQ8I/nRPBC1
+oqOIkRSbE+IF7DP9QvYuNG/3bZZQ8LUVeHxqI5Mq8K2VIJZd95AIwPNMH34SaDGz
+9xjG28Fq4ZkuDP0pCsHM9d2XEwK5PEVS18WW5fJ/QcJKMno4IPTB70ZBBjVzv6Y+
+MYjOrw==
+-----END CERTIFICATE-----
diff --git a/tests/unit/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp
index b334a65..d94dd43 100644
--- a/tests/unit/asn1_decoder_test.cpp
+++ b/tests/unit/asn1_decoder_test.cpp
@@ -20,7 +20,7 @@
 
 #include <gtest/gtest.h>
 
-#include "asn1_decoder.h"
+#include "private/asn1_decoder.h"
 
 TEST(Asn1DecoderTest, Empty_Failure) {
   uint8_t empty[] = {};
diff --git a/tests/unit/package_test.cpp b/tests/unit/package_test.cpp
new file mode 100644
index 0000000..a735a69
--- /dev/null
+++ b/tests/unit/package_test.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agree to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include <functional>
+#include <string>
+#include <vector>
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+#include <openssl/sha.h>
+#include <ziparchive/zip_writer.h>
+
+#include "common/test_constants.h"
+#include "install/package.h"
+
+class PackageTest : public ::testing::Test {
+ protected:
+  void SetUp() override;
+
+  // A list of package classes for test, including MemoryPackage and FilePackage.
+  std::vector<std::unique_ptr<Package>> packages_;
+
+  TemporaryFile temp_file_;   // test package file.
+  std::string file_content_;  // actual bytes of the package file.
+};
+
+void PackageTest::SetUp() {
+  std::vector<std::string> entries = { "file1.txt", "file2.txt", "dir1/file3.txt" };
+  FILE* file_ptr = fdopen(temp_file_.release(), "wb");
+  ZipWriter writer(file_ptr);
+  for (const auto& entry : entries) {
+    ASSERT_EQ(0, writer.StartEntry(entry.c_str(), ZipWriter::kCompress));
+    ASSERT_EQ(0, writer.WriteBytes(entry.c_str(), entry.size()));
+    ASSERT_EQ(0, writer.FinishEntry());
+  }
+  writer.Finish();
+  ASSERT_EQ(0, fclose(file_ptr));
+
+  ASSERT_TRUE(android::base::ReadFileToString(temp_file_.path, &file_content_));
+  auto memory_package = Package::CreateMemoryPackage(temp_file_.path, nullptr);
+  ASSERT_TRUE(memory_package);
+  packages_.emplace_back(std::move(memory_package));
+
+  auto file_package = Package::CreateFilePackage(temp_file_.path, nullptr);
+  ASSERT_TRUE(file_package);
+  packages_.emplace_back(std::move(file_package));
+}
+
+TEST_F(PackageTest, ReadFullyAtOffset_success) {
+  for (const auto& package : packages_) {
+    std::vector<uint8_t> buffer(file_content_.size());
+    ASSERT_TRUE(package->ReadFullyAtOffset(buffer.data(), file_content_.size(), 0));
+    ASSERT_EQ(file_content_, std::string(buffer.begin(), buffer.end()));
+
+    ASSERT_TRUE(package->ReadFullyAtOffset(buffer.data(), file_content_.size() - 10, 10));
+    ASSERT_EQ(file_content_.substr(10), std::string(buffer.begin(), buffer.end() - 10));
+  }
+}
+
+TEST_F(PackageTest, ReadFullyAtOffset_failure) {
+  for (const auto& package : packages_) {
+    std::vector<uint8_t> buffer(file_content_.size());
+    // Out of bound read.
+    ASSERT_FALSE(package->ReadFullyAtOffset(buffer.data(), file_content_.size(), 10));
+  }
+}
+
+TEST_F(PackageTest, UpdateHashAtOffset_sha1_hash) {
+  // Check that the hash matches for first half of the file.
+  uint64_t hash_size = file_content_.size() / 2;
+  std::vector<uint8_t> expected_sha(SHA_DIGEST_LENGTH);
+  SHA1(reinterpret_cast<uint8_t*>(file_content_.data()), hash_size, expected_sha.data());
+
+  for (const auto& package : packages_) {
+    SHA_CTX ctx;
+    SHA1_Init(&ctx);
+    std::vector<HasherUpdateCallback> hashers{ std::bind(&SHA1_Update, &ctx, std::placeholders::_1,
+                                                         std::placeholders::_2) };
+    package->UpdateHashAtOffset(hashers, 0, hash_size);
+
+    std::vector<uint8_t> calculated_sha(SHA_DIGEST_LENGTH);
+    SHA1_Final(calculated_sha.data(), &ctx);
+    ASSERT_EQ(expected_sha, calculated_sha);
+  }
+}
+
+TEST_F(PackageTest, GetZipArchiveHandle_extract_entry) {
+  for (const auto& package : packages_) {
+    ZipArchiveHandle zip = package->GetZipArchiveHandle();
+    ASSERT_TRUE(zip);
+
+    // Check that we can extract one zip entry.
+    std::string entry_name = "dir1/file3.txt";
+    ZipString path(entry_name.c_str());
+    ZipEntry entry;
+    ASSERT_EQ(0, FindEntry(zip, path, &entry));
+
+    std::vector<uint8_t> extracted(entry_name.size());
+    ASSERT_EQ(0, ExtractToMemory(zip, &entry, extracted.data(), extracted.size()));
+    ASSERT_EQ(entry_name, std::string(extracted.begin(), extracted.end()));
+  }
+}
diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp
index 647c7b2..883dfbd 100644
--- a/tests/unit/screen_ui_test.cpp
+++ b/tests/unit/screen_ui_test.cpp
@@ -30,11 +30,11 @@
 #include <gtest/gtest_prod.h>
 
 #include "common/test_constants.h"
-#include "device.h"
 #include "minui/minui.h"
 #include "otautil/paths.h"
 #include "private/resources.h"
-#include "screen_ui.h"
+#include "recovery_ui/device.h"
+#include "recovery_ui/screen_ui.h"
 
 static const std::vector<std::string> HEADERS{ "header" };
 static const std::vector<std::string> ITEMS{ "item1", "item2", "item3", "item4", "1234567890" };
diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp
index 77625db..3466e8e 100644
--- a/tests/unit/sysutil_test.cpp
+++ b/tests/unit/sysutil_test.cpp
@@ -17,8 +17,10 @@
 #include <string>
 
 #include <android-base/file.h>
+#include <android-base/strings.h>
 #include <gtest/gtest.h>
 
+#include "otautil/rangeset.h"
 #include "otautil/sysutil.h"
 
 TEST(SysUtilTest, InvalidArgs) {
@@ -28,6 +30,65 @@
   ASSERT_FALSE(mapping.MapFile(""));
 }
 
+TEST(SysUtilTest, ParseBlockMapFile_smoke) {
+  std::vector<std::string> content = {
+    "/dev/abc", "49652 4096", "3", "1000 1008", "2100 2102", "30 33",
+  };
+
+  TemporaryFile temp_file;
+  ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+
+  auto block_map_data = BlockMapData::ParseBlockMapFile(temp_file.path);
+  ASSERT_EQ("/dev/abc", block_map_data.path());
+  ASSERT_EQ(49652, block_map_data.file_size());
+  ASSERT_EQ(4096, block_map_data.block_size());
+  ASSERT_EQ(RangeSet(std::vector<Range>{
+                { 1000, 1008 },
+                { 2100, 2102 },
+                { 30, 33 },
+            }),
+            block_map_data.block_ranges());
+}
+
+TEST(SysUtilTest, ParseBlockMapFile_invalid_line_count) {
+  std::vector<std::string> content = {
+    "/dev/abc", "49652 4096", "2", "1000 1008", "2100 2102", "30 33",
+  };
+
+  TemporaryFile temp_file;
+  ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+
+  auto block_map_data1 = BlockMapData::ParseBlockMapFile(temp_file.path);
+  ASSERT_FALSE(block_map_data1);
+}
+
+TEST(SysUtilTest, ParseBlockMapFile_invalid_size) {
+  std::vector<std::string> content = {
+    "/dev/abc",
+    "42949672950 4294967295",
+    "1",
+    "0 9",
+  };
+
+  TemporaryFile temp_file;
+  ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+
+  auto block_map_data = BlockMapData::ParseBlockMapFile(temp_file.path);
+  ASSERT_EQ("/dev/abc", block_map_data.path());
+  ASSERT_EQ(42949672950, block_map_data.file_size());
+  ASSERT_EQ(4294967295, block_map_data.block_size());
+
+  content[1] = "42949672950 4294967296";
+  ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+  auto large_block_size = BlockMapData::ParseBlockMapFile(temp_file.path);
+  ASSERT_FALSE(large_block_size);
+
+  content[1] = "4294967296 1";
+  ASSERT_TRUE(android::base::WriteStringToFile(android::base::Join(content, '\n'), temp_file.path));
+  auto too_many_blocks = BlockMapData::ParseBlockMapFile(temp_file.path);
+  ASSERT_FALSE(too_many_blocks);
+}
+
 TEST(SysUtilTest, MapFileRegularFile) {
   TemporaryFile temp_file1;
   std::string content = "abc";
diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml
index e51b36d..e6f2ffd 100644
--- a/tools/recovery_l10n/res/values-gl/strings.xml
+++ b/tools/recovery_l10n/res/values-gl/strings.xml
@@ -6,9 +6,9 @@
     <string name="recovery_no_command" msgid="4465476568623024327">"Non hai ningún comando"</string>
     <string name="recovery_error" msgid="5748178989622716736">"Erro"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"Instalando actualización de seguranza"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos de usuario almacenados neste dispositivo."</string>
+    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos do usuario almacenados neste dispositivo."</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"Tentar de novo"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"Restablecemento dos datos de fábrica"</string>
-    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
+    <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER."</string>
     <string name="recovery_cancel_wipe_data" msgid="66987687653647384">"Cancelar"</string>
 </resources>
diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml
index 3d66372..2d6c0ab 100644
--- a/tools/recovery_l10n/res/values-ja/strings.xml
+++ b/tools/recovery_l10n/res/values-ja/strings.xml
@@ -6,7 +6,7 @@
     <string name="recovery_no_command" msgid="4465476568623024327">"コマンドが指定されていません"</string>
     <string name="recovery_error" msgid="5748178989622716736">"エラーが発生しました。"</string>
     <string name="recovery_installing_security" msgid="9184031299717114342">"セキュリティ アップデートをインストールしています"</string>
-    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
+    <string name="recovery_wipe_data_menu_header" msgid="550255032058254478">"Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。"</string>
     <string name="recovery_try_again" msgid="7168248750158873496">"再試行"</string>
     <string name="recovery_factory_data_reset" msgid="7321351565602894783">"データの初期化"</string>
     <string name="recovery_wipe_data_confirmation" msgid="5439823343348043954">"すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。"</string>
diff --git a/update_verifier/include/update_verifier/update_verifier.h b/update_verifier/include/update_verifier/update_verifier.h
index b00890e..4c64b1e 100644
--- a/update_verifier/include/update_verifier/update_verifier.h
+++ b/update_verifier/include/update_verifier/update_verifier.h
@@ -50,9 +50,6 @@
 
  private:
   friend class UpdateVerifierTest;
-  // Parses the legacy care_map.txt in plain text format.
-  bool ParseCareMapPlainText(const std::string& content);
-
   // Finds all the dm-enabled partitions, and returns a map of <partition_name, block_device>.
   std::map<std::string, std::string> FindDmPartitions();
 
diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp
index 5e5eac7..d04c455 100644
--- a/update_verifier/update_verifier.cpp
+++ b/update_verifier/update_verifier.cpp
@@ -38,17 +38,19 @@
  */
 
 #include "update_verifier/update_verifier.h"
-#include <android/os/IVold.h>
 
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <stdint.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <algorithm>
 #include <future>
+#include <thread>
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -57,6 +59,7 @@
 #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>
 #include <cutils/android_reboot.h>
@@ -68,6 +71,7 @@
 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";
 
 // Find directories in format of "/sys/block/dm-X".
@@ -111,6 +115,15 @@
       // AVB is using 'vroot' for the root block device but we're expecting 'system'.
       if (dm_block_name == "vroot") {
         dm_block_name = "system";
+      } else if (android::base::EndsWith(dm_block_name, "-verity")) {
+        auto npos = dm_block_name.rfind("-verity");
+        dm_block_name = dm_block_name.substr(0, npos);
+      } else if (!android::base::GetProperty("ro.boot.avb_version", "").empty()) {
+        // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices,
+        // if DAP is enabled, then a -verity suffix must be used to
+        // differentiate between dm-linear and dm-verity devices. If we get
+        // here, we're AVB 2 and looking at a non-verity partition.
+        continue;
       }
 
       dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name));
@@ -196,51 +209,13 @@
   return true;
 }
 
-bool UpdateVerifier::ParseCareMapPlainText(const std::string& content) {
-  // care_map file has up to six lines, where every two lines make a pair. Within each pair, the
-  // first line has the partition name (e.g. "system"), while the second line holds the ranges of
-  // all the blocks to verify.
-  auto lines = android::base::Split(android::base::Trim(content), "\n");
-  if (lines.size() != 2 && lines.size() != 4 && lines.size() != 6) {
-    LOG(WARNING) << "Invalid lines in care_map: found " << lines.size()
-                 << " lines, expecting 2 or 4 or 6 lines.";
-    return false;
-  }
-
-  for (size_t i = 0; i < lines.size(); i += 2) {
-    const std::string& partition_name = lines[i];
-    const std::string& range_str = lines[i + 1];
-    // We're seeing an N care_map.txt. Skip the verification since it's not compatible with O
-    // update_verifier (the last few metadata blocks can't be read via device mapper).
-    if (android::base::StartsWith(partition_name, "/dev/block/")) {
-      LOG(WARNING) << "Found legacy care_map.txt; skipped.";
-      return false;
-    }
-
-    // For block range string, first integer 'count' equals 2 * total number of valid ranges,
-    // followed by 'count' number comma separated integers. Every two integers reprensent a
-    // block range with the first number included in range but second number not included.
-    // For example '4,64536,65343,74149,74150' represents: [64536,65343) and [74149,74150).
-    RangeSet ranges = RangeSet::Parse(range_str);
-    if (!ranges) {
-      LOG(WARNING) << "Error parsing RangeSet string " << range_str;
-      return false;
-    }
-
-    partition_map_.emplace(partition_name, ranges);
-  }
-
-  return true;
-}
-
 bool UpdateVerifier::ParseCareMap() {
   partition_map_.clear();
 
   std::string care_map_name = care_map_prefix_ + ".pb";
   if (access(care_map_name.c_str(), R_OK) == -1) {
-    LOG(WARNING) << care_map_name
-                 << " doesn't exist, falling back to read the care_map in plain text format.";
-    care_map_name = care_map_prefix_ + ".txt";
+    LOG(ERROR) << care_map_name << " doesn't exist";
+    return false;
   }
 
   android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY)));
@@ -263,10 +238,6 @@
     return false;
   }
 
-  if (android::base::EndsWith(care_map_name, ".txt")) {
-    return ParseCareMapPlainText(file_content);
-  }
-
   recovery_update_verifier::CareMap care_map;
   if (!care_map.ParseFromString(file_content)) {
     LOG(WARNING) << "Failed to parse " << care_map_name << " in protobuf format.";
diff --git a/updater_sample/README.md b/updater_sample/README.md
index bc66a9b..2070ebc 100644
--- a/updater_sample/README.md
+++ b/updater_sample/README.md
@@ -191,7 +191,7 @@
     </privapp-permissions>
    ```
    to `frameworks/base/data/etc/privapp-permissions-platform.xml`
-5. Build sample app `mmma -j bootable/recovery/updater_sample`.
+5. Build sample app `make -j SystemUpdaterSample`.
 6. Build Android `make -j`
 7. [Flash the device](https://source.android.com/setup/build/running)
 8. Add update config files; look above at `## Update Config file`;
@@ -225,7 +225,9 @@
 
 ## Running tests
 
-1. Build `mmma bootable/recovery/updater_sample/`
+The commands are expected to be run from `$ANDROID_BUILD_TOP`.
+
+1. Build `make -j SystemUpdaterSample` and `make -j SystemUpdaterSampleTests`.
 2. Install app
    `adb install $OUT/system/priv-app/SystemUpdaterSample/SystemUpdaterSample.apk`
 3. Install tests
diff --git a/verifier.h b/verifier.h
deleted file mode 100644
index df9a4b6..0000000
--- a/verifier.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef _RECOVERY_VERIFIER_H
-#define _RECOVERY_VERIFIER_H
-
-#include <stdint.h>
-
-#include <functional>
-#include <memory>
-#include <vector>
-
-#include <openssl/ec_key.h>
-#include <openssl/rsa.h>
-#include <openssl/sha.h>
-
-struct RSADeleter {
-  void operator()(RSA* rsa) const {
-    RSA_free(rsa);
-  }
-};
-
-struct ECKEYDeleter {
-  void operator()(EC_KEY* ec_key) const {
-    EC_KEY_free(ec_key);
-  }
-};
-
-struct Certificate {
-    typedef enum {
-        KEY_TYPE_RSA,
-        KEY_TYPE_EC,
-    } KeyType;
-
-    Certificate(int hash_len_,
-                KeyType key_type_,
-                std::unique_ptr<RSA, RSADeleter>&& rsa_,
-                std::unique_ptr<EC_KEY, ECKEYDeleter>&& ec_)
-        : hash_len(hash_len_),
-          key_type(key_type_),
-          rsa(std::move(rsa_)),
-          ec(std::move(ec_)) {}
-
-    // SHA_DIGEST_LENGTH (SHA-1) or SHA256_DIGEST_LENGTH (SHA-256)
-    int hash_len;
-    KeyType key_type;
-    std::unique_ptr<RSA, RSADeleter> rsa;
-    std::unique_ptr<EC_KEY, ECKEYDeleter> ec;
-};
-
-/*
- * 'addr' and 'length' define an update package file that has been loaded (or mmap'ed, or
- * whatever) into memory. Verifies that the file is signed and the signature matches one of the
- * given keys. It optionally accepts a callback function for posting the progress to. Returns one
- * of the constants of VERIFY_SUCCESS and VERIFY_FAILURE.
- */
-int verify_file(const unsigned char* addr, size_t length, const std::vector<Certificate>& keys,
-                const std::function<void(float)>& set_progress = nullptr);
-
-// Checks that the RSA key has a modulus of 2048 bits long, and public exponent is 3 or 65537.
-bool CheckRSAKey(const std::unique_ptr<RSA, RSADeleter>& rsa);
-
-// Checks that the field size of the curve for the EC key is 256 bits.
-bool CheckECKey(const std::unique_ptr<EC_KEY, ECKEYDeleter>& ec_key);
-
-// Parses a PEM-encoded x509 certificate from the given buffer and saves it into |cert|. Returns
-// false if there is a parsing failure or the signature's encryption algorithm is not supported.
-bool LoadCertificateFromBuffer(const std::vector<uint8_t>& pem_content, Certificate* cert);
-
-// Iterates over the zip entries with the suffix "x509.pem" and returns a list of recognized
-// certificates. Returns an empty list if we fail to parse any of the entries.
-std::vector<Certificate> LoadKeysFromZipfile(const std::string& zip_name);
-
-#define VERIFY_SUCCESS        0
-#define VERIFY_FAILURE        1
-
-#endif  /* _RECOVERY_VERIFIER_H */