Retry the update if ApplyBSDiffPatch | ApplyImagePatch fails

We have seen one case when bspatch failed likely due to patch
corruption. Since the package has passed verification before, we want
to reboot and retry the patch command again since there's no
alternative for users.

We won't delete the stash before reboot, and the src has passed SHA1
check. If there's an error on the patch, it will fail the package
verification during retry.

Bug: 37855643
Test: angler reboots and retries the update when bspatch fails.
Change-Id: I2ebac9621bd1f0649bb301b9a28a0dd079ed4e1d
diff --git a/error_code.h b/error_code.h
index 0e79c87..9fe047c 100644
--- a/error_code.h
+++ b/error_code.h
@@ -44,6 +44,7 @@
   kTune2FsFailure,
   kRebootFailure,
   kPackageExtractFileFailure,
+  kPatchApplicationFailure,
   kVendorFailure = 200
 };
 
diff --git a/recovery.cpp b/recovery.cpp
index dfae7f0..122b89d 100644
--- a/recovery.cpp
+++ b/recovery.cpp
@@ -112,8 +112,9 @@
 static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install";
 static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg";
 static const char *LAST_LOG_FILE = "/cache/recovery/last_log";
-// We will try to apply the update package 5 times at most in case of an I/O error.
-static const int EIO_RETRY_COUNT = 4;
+// We will try to apply the update package 5 times at most in case of an I/O error or
+// bspatch | imgpatch error.
+static const int RETRY_LIMIT = 4;
 static const int BATTERY_READ_TIMEOUT_IN_SEC = 10;
 // GmsCore enters recovery mode to install package when having enough battery
 // percentage. Normally, the threshold is 40% without charger and 20% with charger.
@@ -1530,9 +1531,9 @@
             }
             if (status != INSTALL_SUCCESS) {
                 ui->Print("Installation aborted.\n");
-                // When I/O error happens, reboot and retry installation EIO_RETRY_COUNT
+                // When I/O error happens, reboot and retry installation RETRY_LIMIT
                 // times before we abandon this OTA update.
-                if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) {
+                if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) {
                     copy_logs();
                     set_retry_bootloader_message(retry_count, args);
                     // Print retry count on screen.
diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp
index d5c1704..df366b0 100644
--- a/updater/blockimg.cpp
+++ b/updater/blockimg.cpp
@@ -1213,6 +1213,7 @@
                                       std::placeholders::_2),
                             nullptr, nullptr) != 0) {
           LOG(ERROR) << "Failed to apply image patch.";
+          failure_type = kPatchApplicationFailure;
           return -1;
         }
       } else {
@@ -1221,6 +1222,7 @@
                                        std::placeholders::_2),
                              nullptr) != 0) {
           LOG(ERROR) << "Failed to apply bsdiff patch.";
+          failure_type = kPatchApplicationFailure;
           return -1;
         }
       }
diff --git a/updater/updater.cpp b/updater/updater.cpp
index 1be8b60..f5ff6df 100644
--- a/updater/updater.cpp
+++ b/updater/updater.cpp
@@ -202,6 +202,10 @@
     // Cause code should provide additional information about the abort.
     if (state.cause_code != kNoCause) {
       fprintf(cmd_pipe, "log cause: %d\n", state.cause_code);
+      if (state.cause_code == kPatchApplicationFailure) {
+        LOG(INFO) << "Patch application failed, retry update.";
+        fprintf(cmd_pipe, "retry_update\n");
+      }
     }
 
     if (updater_info.package_zip) {