Merge master@5406228 into qt-dev.
am: a1382c23b4

Change-Id: I09d027b01e2e94035d71a65cdf6fd5f79fc22ba7
diff --git a/Android.bp b/Android.bp
index 7b67f40..a44a2c6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -63,32 +63,18 @@
         "libbootloader_message",
         "libcrypto",
         "libcutils",
-        "libext4_utils",
         "libfs_mgr",
-        "libfusesideload",
-        "libhidl-gen-utils",
-        "libhidlbase",
-        "libhidltransport",
         "liblog",
-        "libpng",
-        "libselinux",
-        "libtinyxml2",
-        "libutils",
-        "libz",
         "libziparchive",
     ],
 
     static_libs: [
         "librecovery_fastboot",
         "libminui",
-        "libpackage",
-        "libverifier",
         "libotautil",
 
         // external dependencies
         "libhealthhalutils",
-        "libvintf_recovery",
-        "libvintf",
         "libfstab",
     ],
 }
@@ -102,69 +88,14 @@
     ],
 
     srcs: [
-        "adb_install.cpp",
         "fsck_unshare_blocks.cpp",
-        "fuse_sdcard_install.cpp",
-        "install.cpp",
         "recovery.cpp",
-        "roots.cpp",
     ],
 
     shared_libs: [
+        "libinstall",
         "librecovery_ui",
     ],
-
-    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",
-    ],
-}
-
-cc_library_static {
-    name: "libpackage",
-    recovery_available: true,
-
-    defaults: [
-        "recovery_defaults",
-    ],
-
-    srcs: [
-        "package.cpp",
-    ],
-
-    shared_libs: [
-        "libbase",
-        "libcrypto",
-        "libziparchive",
-    ],
-
-    static_libs: [
-        "libotautil",
-    ],
 }
 
 cc_binary {
@@ -172,6 +103,7 @@
     recovery: true,
 
     defaults: [
+        "libinstall_defaults",
         "librecovery_defaults",
     ],
 
@@ -181,6 +113,7 @@
     ],
 
     shared_libs: [
+        "libinstall",
         "libminadbd_services",
         "librecovery_ui",
     ],
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/fsck_unshare_blocks.cpp b/fsck_unshare_blocks.cpp
index ce6940a..e74f8ba 100644
--- a/fsck_unshare_blocks.cpp
+++ b/fsck_unshare_blocks.cpp
@@ -36,7 +36,7 @@
 #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_sideload/fuse_sideload.cpp b/fuse_sideload/fuse_sideload.cpp
index b5b6ac1..3d94803 100644
--- a/fuse_sideload/fuse_sideload.cpp
+++ b/fuse_sideload/fuse_sideload.cpp
@@ -392,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;
diff --git a/install/Android.bp b/install/Android.bp
new file mode 100644
index 0000000..aa47990
--- /dev/null
+++ b/install/Android.bp
@@ -0,0 +1,78 @@
+// 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",
+    ],
+
+    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",
+        "libfstab",
+    ],
+}
+
+cc_library {
+    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",
+    ],
+
+    shared_libs: [
+        "librecovery_ui",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    export_shared_lib_headers: [
+        "librecovery_ui",
+    ],
+}
diff --git a/adb_install.cpp b/install/adb_install.cpp
similarity index 91%
rename from adb_install.cpp
rename to install/adb_install.cpp
index 1d19fd3..5296ff6 100644
--- a/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "adb_install.h"
+#include "install/adb_install.h"
 
 #include <errno.h>
 #include <fcntl.h>
@@ -29,12 +29,16 @@
 #include <android-base/logging.h>
 #include <android-base/properties.h>
 
-#include "common.h"
 #include "fuse_sideload.h"
-#include "install.h"
+#include "install/install.h"
 #include "recovery_ui/ui.h"
 
-int apply_from_adb(bool* wipe_cache) {
+static bool SetUsbConfig(const std::string& state) {
+  android::base::SetProperty("sys.usb.config", state);
+  return android::base::WaitForProperty("sys.usb.state", state);
+}
+
+int apply_from_adb(bool* wipe_cache, 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.
@@ -85,7 +89,7 @@
         break;
       }
     }
-    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0);
+    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui);
     break;
   }
 
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/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp
similarity index 97%
rename from fuse_sdcard_install.cpp
rename to install/fuse_sdcard_install.cpp
index 1f38509..dde289f 100644
--- a/fuse_sdcard_install.cpp
+++ b/install/fuse_sdcard_install.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "fuse_sdcard_install.h"
+#include "install/fuse_sdcard_install.h"
 
 #include <dirent.h>
 #include <signal.h>
@@ -35,8 +35,8 @@
 #include "bootloader_message/bootloader_message.h"
 #include "fuse_provider.h"
 #include "fuse_sideload.h"
-#include "install.h"
-#include "roots.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
@@ -184,7 +184,7 @@
       }
     }
 
-    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/);
+    result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/, ui);
     break;
   }
 
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..dbc8245 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(bool* wipe_cache, RecoveryUI* ui);
diff --git a/fuse_sdcard_install.h b/install/include/install/fuse_sdcard_install.h
similarity index 100%
rename from fuse_sdcard_install.h
rename to install/include/install/fuse_sdcard_install.h
diff --git a/install.h b/install/include/install/install.h
similarity index 91%
rename from install.h
rename to install/include/install/install.h
index da8aa5e..74fb3d1 100644
--- a/install.h
+++ b/install/include/install/install.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef RECOVERY_INSTALL_H_
-#define RECOVERY_INSTALL_H_
+#pragma once
 
 #include <stddef.h>
 
@@ -26,6 +25,7 @@
 #include <ziparchive/zip_archive.h>
 
 #include "package.h"
+#include "recovery_ui/ui.h"
 
 enum InstallResult {
   INSTALL_SUCCESS,
@@ -45,12 +45,12 @@
 
 // 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);
+int install_package(const std::string& package, bool* wipe_cache, bool needs_mount, int retry_count,
+                    RecoveryUI* ui);
 
 // Verifies the package by ota keys. Returns true if the package is verified successfully,
 // otherwise returns false.
-bool verify_package(Package* package);
+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.
@@ -67,5 +67,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/package.h b/install/include/install/package.h
similarity index 100%
rename from package.h
rename to install/include/install/package.h
diff --git a/verifier.h b/install/include/install/verifier.h
similarity index 77%
rename from verifier.h
rename to install/include/install/verifier.h
index 106b86b..f9e9475 100644
--- a/verifier.h
+++ b/install/include/install/verifier.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef _RECOVERY_VERIFIER_H
-#define _RECOVERY_VERIFIER_H
+#pragma once
 
 #include <stdint.h>
 
@@ -44,25 +43,20 @@
 };
 
 struct Certificate {
-    typedef enum {
-        KEY_TYPE_RSA,
-        KEY_TYPE_EC,
-    } KeyType;
+  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_)) {}
+  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;
+  // 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 {
@@ -88,7 +82,8 @@
 //  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 bits long, and public exponent is 3 or 65537.
+// 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.
@@ -102,7 +97,5 @@
 // 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 */
+#define VERIFY_SUCCESS 0
+#define VERIFY_FAILURE 1
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 95%
rename from install.cpp
rename to install/install.cpp
index b7fb788..a1124c3 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,19 +46,22 @@
 #include <android-base/unique_fd.h>
 #include <vintf/VintfObjectRecovery.h>
 
-#include "common.h"
+#include "install/package.h"
+#include "install/verifier.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
 #include "otautil/thermalutil.h"
-#include "package.h"
-#include "private/install.h"
+#include "private/setup_commands.h"
 #include "recovery_ui/ui.h"
-#include "roots.h"
-#include "verifier.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;
@@ -323,7 +326,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";
@@ -342,7 +345,9 @@
 
   // The updater in child process writes to the pipe to communicate with recovery.
   android::base::unique_fd pipe_read, pipe_write;
-  if (!android::base::Pipe(&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;
   }
@@ -567,7 +572,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...
@@ -594,7 +599,7 @@
   }
 
   // Verify package.
-  if (!verify_package(package.get())) {
+  if (!verify_package(package.get(), ui)) {
     log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure));
     return INSTALL_CORRUPT;
   }
@@ -618,18 +623,19 @@
     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");
 
   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* 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();
@@ -642,7 +648,7 @@
     result = INSTALL_ERROR;
   } else {
     result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count,
-                                    &max_temperature);
+                                    &max_temperature, ui);
   }
 
   // Measure the time spent to apply OTA update in seconds.
@@ -700,7 +706,7 @@
   return result;
 }
 
-bool verify_package(Package* package) {
+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()) {
diff --git a/package.cpp b/install/package.cpp
similarity index 99%
rename from package.cpp
rename to install/package.cpp
index 6c7289f..4402f48 100644
--- a/package.cpp
+++ b/install/package.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "package.h"
+#include "install/package.h"
 
 #include <string.h>
 #include <unistd.h>
diff --git a/verifier.cpp b/install/verifier.cpp
similarity index 92%
rename from verifier.cpp
rename to install/verifier.cpp
index 68a011e..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,8 +36,8 @@
 #include <openssl/rsa.h>
 #include <ziparchive/zip_archive.h>
 
-#include "asn1_decoder.h"
 #include "otautil/print_sha1.h"
+#include "private/asn1_decoder.h"
 
 /*
  * Simple version of PKCS#7 SignedData extraction. This extracts the
@@ -82,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;
   }
@@ -96,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;
   }
 
@@ -152,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;
   }
 
@@ -189,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.
@@ -203,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;
     }
   }
 
@@ -247,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)) {
@@ -373,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/logging.cpp b/logging.cpp
index 283d115..48f9ec3 100644
--- a/logging.cpp
+++ b/logging.cpp
@@ -33,7 +33,7 @@
 #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";
diff --git a/otautil/Android.bp b/otautil/Android.bp
index 41018dd..b4936c0 100644
--- a/otautil/Android.bp
+++ b/otautil/Android.bp
@@ -42,12 +42,23 @@
                 "dirutil.cpp",
                 "mounts.cpp",
                 "parse_install_logs.cpp",
+                "roots.cpp",
                 "sysutil.cpp",
                 "thermalutil.cpp",
             ],
 
+            include_dirs: [
+                "system/vold",
+            ],
+
+            static_libs: [
+                "libfstab",
+            ],
+
             shared_libs: [
                 "libcutils",
+                "libext4_utils",
+                "libfs_mgr",
                 "libselinux",
             ],
         },
diff --git a/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/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.cpp b/recovery.cpp
index d9c1f22..0349184 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -24,7 +24,6 @@
 #include <limits.h>
 #include <linux/fs.h>
 #include <linux/input.h>
-#include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -50,20 +49,20 @@
 #include <healthhalutils/HealthHalUtils.h>
 #include <ziparchive/zip_archive.h>
 
-#include "adb_install.h"
 #include "common.h"
 #include "fsck_unshare_blocks.h"
-#include "fuse_sdcard_install.h"
-#include "install.h"
+#include "install/adb_install.h"
+#include "install/fuse_sdcard_install.h"
+#include "install/install.h"
+#include "install/package.h"
 #include "logging.h"
 #include "otautil/dirutil.h"
 #include "otautil/error_code.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
-#include "package.h"
 #include "recovery_ui/screen_ui.h"
 #include "recovery_ui/ui.h"
-#include "roots.h"
 
 static constexpr const char* CACHE_LOG_DIR = "/cache/recovery";
 static constexpr const char* COMMAND_FILE = "/cache/recovery/command";
@@ -79,7 +78,7 @@
 // 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 modified_flash = false;
 std::string stage;
 const char* reason = nullptr;
 
@@ -272,12 +271,6 @@
   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);
-}
-
 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" };
@@ -439,7 +432,7 @@
 // 1. verify the package.
 // 2. check metadata (ota-type, pre-device and serial number if having one).
 static bool CheckWipePackage(Package* wipe_package) {
-  if (!verify_package(wipe_package)) {
+  if (!verify_package(wipe_package, ui)) {
     LOG(ERROR) << "Failed to verify package";
     return false;
   }
@@ -693,7 +686,7 @@
         modified_flash = true;
         bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD);
         if (adb) {
-          status = apply_from_adb(&should_wipe_cache);
+          status = apply_from_adb(&should_wipe_cache, ui);
         } else {
           status = ApplyFromSdcard(device, &should_wipe_cache, ui);
         }
@@ -746,20 +739,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;
@@ -1030,7 +1009,8 @@
         set_retry_bootloader_message(retry_count + 1, args);
       }
 
-      status = install_package(update_package, &should_wipe_cache, true, retry_count);
+      modified_flash = true;
+      status = install_package(update_package, &should_wipe_cache, true, retry_count, ui);
       if (status == INSTALL_SUCCESS && should_wipe_cache) {
         wipe_cache(false, device);
       }
@@ -1096,7 +1076,7 @@
     if (!sideload_auto_reboot) {
       ui->ShowText(true);
     }
-    status = apply_from_adb(&should_wipe_cache);
+    status = apply_from_adb(&should_wipe_cache, ui);
     if (status == INSTALL_SUCCESS && should_wipe_cache) {
       if (!wipe_cache(false, device)) {
         status = INSTALL_ERROR;
diff --git a/recovery_main.cpp b/recovery_main.cpp
index 2f5a184..b41368d 100644
--- a/recovery_main.cpp
+++ b/recovery_main.cpp
@@ -53,12 +53,12 @@
 #include "logging.h"
 #include "minadbd/minadbd.h"
 #include "otautil/paths.h"
+#include "otautil/roots.h"
 #include "otautil/sysutil.h"
 #include "recovery.h"
 #include "recovery_ui/device.h"
 #include "recovery_ui/stub_ui.h"
 #include "recovery_ui/ui.h"
-#include "roots.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) {
diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h
index cfa914e..3c44510 100644
--- a/recovery_ui/include/recovery_ui/device.h
+++ b/recovery_ui/include/recovery_ui/device.h
@@ -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/tests/Android.bp b/tests/Android.bp
index ef5919e..09ef716 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -76,9 +76,9 @@
 librecovery_static_libs = [
     "librecovery",
     "librecovery_fastboot",
+    "libinstall",
+    "librecovery_ui",
     "libminui",
-    "libpackage",
-    "libverifier",
     "libotautil",
 
     "libhealthhalutils",
@@ -116,10 +116,9 @@
     ],
 
     static_libs: libapplypatch_static_libs + [
+        "libinstall",
         "librecovery_ui",
         "libminui",
-        "libpackage",
-        "libverifier",
         "libotautil",
         "libupdater",
         "libgtest_prod",
diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp
index 969805b..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) {
diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp
index c904cd0..ded23c5 100644
--- a/tests/component/verifier_test.cpp
+++ b/tests/component/verifier_test.cpp
@@ -35,9 +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 "package.h"
-#include "verifier.h"
 
 using namespace std::string_literals;
 
@@ -158,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);
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-----
+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
+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
index fa492d3..a735a69 100644
--- a/tests/unit/package_test.cpp
+++ b/tests/unit/package_test.cpp
@@ -26,7 +26,7 @@
 #include <ziparchive/zip_writer.h>
 
 #include "common/test_constants.h"
-#include "package.h"
+#include "install/package.h"
 
 class PackageTest : public ::testing::Test {
  protected:
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";