Merge "Create a fallback to install from fuse if mmap fails"
am: 5f408dd48a

Change-Id: Ie8a54863426bb3558600b96c2ef7338eb0171cd3
diff --git a/install/fuse_install.cpp b/install/fuse_install.cpp
index ffde4a3..8a7a278 100644
--- a/install/fuse_install.cpp
+++ b/install/fuse_install.cpp
@@ -128,11 +128,12 @@
 
   constexpr auto FUSE_BLOCK_SIZE = 65536;
   bool is_block_map = android::base::ConsumePrefix(&path, "@");
-  auto file_data_reader =
+  auto fuse_data_provider =
       is_block_map ? FuseBlockDataProvider::CreateFromBlockMap(std::string(path), FUSE_BLOCK_SIZE)
                    : FuseFileDataProvider::CreateFromFile(std::string(path), FUSE_BLOCK_SIZE);
 
-  if (!file_data_reader->Valid()) {
+  if (!fuse_data_provider || !fuse_data_provider->Valid()) {
+    LOG(ERROR) << "Failed to create fuse data provider.";
     return false;
   }
 
@@ -142,7 +143,7 @@
     umount2(SDCARD_ROOT, MNT_DETACH);
   }
 
-  return run_fuse_sideload(std::move(file_data_reader)) == 0;
+  return run_fuse_sideload(std::move(fuse_data_provider)) == 0;
 }
 
 InstallResult InstallWithFuseFromPath(std::string_view path, RecoveryUI* ui) {
diff --git a/otautil/sysutil.cpp b/otautil/sysutil.cpp
index a882985..6cd46c6 100644
--- a/otautil/sysutil.cpp
+++ b/otautil/sysutil.cpp
@@ -38,7 +38,7 @@
 BlockMapData BlockMapData::ParseBlockMapFile(const std::string& block_map_path) {
   std::string content;
   if (!android::base::ReadFileToString(block_map_path, &content)) {
-    LOG(ERROR) << "Failed to read " << block_map_path;
+    PLOG(ERROR) << "Failed to read " << block_map_path;
     return {};
   }
 
diff --git a/recovery.cpp b/recovery.cpp
index b18a8e7..97ca0a5 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -84,6 +84,8 @@
  *
  * The arguments which may be supplied in the recovery.command file:
  *   --update_package=path - verify install an OTA package file
+ *   --install_with_fuse - install the update package with FUSE. This allows installation of large
+ *       packages on LP32 builds. Since the mmap will otherwise fail due to out of memory.
  *   --wipe_data - erase user data (and cache), then reboot
  *   --prompt_and_wipe_data - prompt the user that data is corrupt, with their consent erase user
  *       data (and cache), then reboot
@@ -577,6 +579,7 @@
   static constexpr struct option OPTIONS[] = {
     { "fastboot", no_argument, nullptr, 0 },
     { "fsck_unshare_blocks", no_argument, nullptr, 0 },
+    { "install_with_fuse", no_argument, nullptr, 0 },
     { "just_exit", no_argument, nullptr, 'x' },
     { "locale", required_argument, nullptr, 0 },
     { "prompt_and_wipe_data", no_argument, nullptr, 0 },
@@ -597,6 +600,7 @@
   };
 
   const char* update_package = nullptr;
+  bool install_with_fuse = false;  // memory map the update package by default.
   bool should_wipe_data = false;
   bool should_prompt_and_wipe_data = false;
   bool should_wipe_cache = false;
@@ -632,6 +636,8 @@
         std::string option = OPTIONS[option_index].name;
         if (option == "fsck_unshare_blocks") {
           fsck_unshare_blocks = true;
+        } else if (option == "install_with_fuse") {
+          install_with_fuse = true;
         } else if (option == "locale" || option == "fastboot") {
           // Handled in recovery_main.cpp
         } else if (option == "prompt_and_wipe_data") {
@@ -737,10 +743,24 @@
       } else {
         ensure_path_mounted(update_package);
       }
-      // TODO(xunchang) install package from fuse for large packages on ILP32 builds.
-      auto package = Package::CreateMemoryPackage(
-          update_package, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
-      status = InstallPackage(package.get(), update_package, should_wipe_cache, retry_count, ui);
+
+      if (install_with_fuse) {
+        LOG(INFO) << "Installing package " << update_package << " with fuse";
+        status = InstallWithFuseFromPath(update_package, ui);
+      } else if (auto memory_package = Package::CreateMemoryPackage(
+                     update_package,
+                     std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1));
+                 memory_package != nullptr) {
+        status = InstallPackage(memory_package.get(), update_package, should_wipe_cache,
+                                retry_count, ui);
+      } else {
+        // We may fail to memory map the package on 32 bit builds for packages with 2GiB+ size.
+        // In such cases, we will try to install the package with fuse. This is not the default
+        // installation method because it introduces a layer of indirection from the kernel space.
+        LOG(WARNING) << "Failed to memory map package " << update_package
+                     << "; falling back to install with fuse";
+        status = InstallWithFuseFromPath(update_package, ui);
+      }
       if (status != INSTALL_SUCCESS) {
         ui->Print("Installation aborted.\n");