recovery: allow A/B updater to downgrade

Change-Id: Iaa1fb7838fb958e69fb3104fef7743aafad12b1b
diff --git a/install/adb_install.cpp b/install/adb_install.cpp
index fd1ca1a..d82238e 100644
--- a/install/adb_install.cpp
+++ b/install/adb_install.cpp
@@ -114,11 +114,17 @@
     }
     ui->CancelWaitKey();
 
-    *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, true /* verify */, ui);
+    *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, true /* verify */,
+                              false /* allow_ab_downgrade */, ui);
     if (*result == INSTALL_UNVERIFIED &&
         ui->IsTextVisible() && ask_to_continue_unverified(device)) {
-      *result =
-          install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, false /* verify */, ui);
+      *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, false /* verify */,
+                                false /* allow_ab_downgrade */, ui);
+    }
+    if (*result == INSTALL_DOWNGRADE &&
+      ui->IsTextVisible() && ask_to_continue_downgrade(device)) {
+      *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, false /* verify */,
+                                true /* allow_ab_downgrade */, ui);
     }
     break;
   }
diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp
index e7f4c0d..cc0a3e7 100644
--- a/install/fuse_sdcard_install.cpp
+++ b/install/fuse_sdcard_install.cpp
@@ -191,11 +191,16 @@
     }
 
     result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/,
-                             true /* verify */, ui);
+                             true /* verify */, false /* allow_ab_downgrade */, ui);
     if (result == INSTALL_UNVERIFIED && ask_to_continue_unverified(device)) {
       result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/,
-                               false /* verify */, ui);
+                               false /* verify */, false /* allow_ab_downgrade */, ui);
     }
+    if (result == INSTALL_DOWNGRADE && ask_to_continue_downgrade(device)) {
+      result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/,
+                               false /* verify */, true /* allow_ab_downgrade */, ui);
+    }
+
     break;
   }
 
diff --git a/install/include/install/install.h b/install/include/install/install.h
index a0763af..85c197d 100644
--- a/install/include/install/install.h
+++ b/install/include/install/install.h
@@ -38,6 +38,7 @@
   INSTALL_KEY_INTERRUPTED,
   INSTALL_REBOOT,
   INSTALL_UNVERIFIED,
+  INSTALL_DOWNGRADE,
 };
 
 enum class OtaType {
@@ -50,7 +51,7 @@
 // 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, bool verify, RecoveryUI* ui);
+                    int retry_count, bool verify, bool allow_ab_downgrade, RecoveryUI* ui);
 
 // Verifies the package by ota keys. Returns true if the package is verified successfully,
 // otherwise returns false.
@@ -70,7 +71,9 @@
 // Checks if the the metadata in the OTA package has expected values. Returns 0 on success.
 // 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);
+int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type,
+                         bool allow_ab_downgrade = false);
 
 // Defined in recovery.cpp, just declare it and it will eventually link fine.
 bool ask_to_continue_unverified(Device* device);
+bool ask_to_continue_downgrade(Device* device);
\ No newline at end of file
diff --git a/install/install.cpp b/install/install.cpp
index d376433..80e5f1b 100644
--- a/install/install.cpp
+++ b/install/install.cpp
@@ -138,7 +138,8 @@
 // Checks the build version, fingerprint and timestamp in the metadata of the A/B package.
 // Downgrading is not allowed unless explicitly enabled in the package and only for
 // incremental packages.
-static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata) {
+static int CheckAbSpecificMetadata(const std::map<std::string, std::string>& metadata,
+                                   bool allow_ab_downgrade) {
   // Incremental updates should match the current build.
   auto device_pre_build = android::base::GetProperty("ro.build.version.incremental", "");
   auto pkg_pre_build = get_value(metadata, "pre-build-incremental");
@@ -157,32 +158,35 @@
   }
 
   // Check for downgrade version.
-  int64_t build_timestamp =
-      android::base::GetIntProperty("ro.build.date.utc", std::numeric_limits<int64_t>::max());
-  int64_t pkg_post_timestamp = 0;
-  // We allow to full update to the same version we are running, in case there
-  // is a problem with the current copy of that version.
-  auto pkg_post_timestamp_string = get_value(metadata, "post-timestamp");
-  if (pkg_post_timestamp_string.empty() ||
-      !android::base::ParseInt(pkg_post_timestamp_string, &pkg_post_timestamp) ||
-      pkg_post_timestamp < build_timestamp) {
-    if (get_value(metadata, "ota-downgrade") != "yes") {
-      LOG(ERROR) << "Update package is older than the current build, expected a build "
-                    "newer than timestamp "
-                 << build_timestamp << " but package has timestamp " << pkg_post_timestamp
-                 << " and downgrade not allowed.";
-      return INSTALL_ERROR;
-    }
-    if (pkg_pre_build_fingerprint.empty()) {
-      LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
-      return INSTALL_ERROR;
+  if (!allow_ab_downgrade) {
+    int64_t build_timestamp =
+        android::base::GetIntProperty("ro.build.date.utc", std::numeric_limits<int64_t>::max());
+    int64_t pkg_post_timestamp = 0;
+    // We allow to full update to the same version we are running, in case there
+    // is a problem with the current copy of that version.
+    auto pkg_post_timestamp_string = get_value(metadata, "post-timestamp");
+    if (pkg_post_timestamp_string.empty() ||
+        !android::base::ParseInt(pkg_post_timestamp_string, &pkg_post_timestamp) ||
+        pkg_post_timestamp < build_timestamp) {
+      if (get_value(metadata, "ota-downgrade") != "yes") {
+        LOG(ERROR) << "Update package is older than the current build, expected a build "
+                      "newer than timestamp "
+                   << build_timestamp << " but package has timestamp " << pkg_post_timestamp
+                   << " and downgrade not allowed.";
+        return INSTALL_DOWNGRADE;
+      }
+      if (pkg_pre_build_fingerprint.empty()) {
+        LOG(ERROR) << "Downgrade package must have a pre-build version set, not allowed.";
+        return INSTALL_DOWNGRADE;
+      }
     }
   }
 
   return 0;
 }
 
-int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type) {
+int CheckPackageMetadata(const std::map<std::string, std::string>& metadata, OtaType ota_type,
+                         bool allow_ab_downgrade) {
   auto device = android::base::GetProperty("ro.product.device", "");
   auto pkg_device = get_value(metadata, "pre-device");
   if (pkg_device != device || pkg_device.empty()) {
@@ -209,7 +213,7 @@
   }
 
   if (ota_type == OtaType::AB) {
-    return CheckAbSpecificMetadata(metadata);
+    return CheckAbSpecificMetadata(metadata, allow_ab_downgrade);
   }
 
   return 0;
@@ -313,14 +317,14 @@
 // 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, RecoveryUI* ui) {
+                             bool allow_ab_downgrade, int* max_temperature, RecoveryUI* ui) {
   std::map<std::string, std::string> metadata;
   bool is_ab_ota = false;
   if (ReadMetadataFromPackage(zip, &metadata)) {
     ReadSourceTargetBuild(metadata, log_buffer);
     if (get_value(metadata, "ota-type") == OtaTypeToString(OtaType::AB)) {
         is_ab_ota = true;
-        int check_status = CheckPackageMetadata(metadata, OtaType::AB);
+        int check_status = CheckPackageMetadata(metadata, OtaType::AB, allow_ab_downgrade);
         if (check_status != 0) {
             return check_status;
         }
@@ -556,7 +560,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,
-                                  bool verify, int* max_temperature, RecoveryUI* ui) {
+                                  bool verify, bool allow_ab_downgrade, int* max_temperature, RecoveryUI* ui) {
   ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
   ui->Print("Finding update package...\n");
   // Give verification half the progress bar...
@@ -607,8 +611,8 @@
     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, ui);
+  int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, allow_ab_downgrade,
+                                 max_temperature, ui);
   ui->SetEnableReboot(true);
   ui->Print("\n");
 
@@ -616,7 +620,7 @@
 }
 
 int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount,
-                    int retry_count, bool verify, RecoveryUI* ui) {
+                    int retry_count, bool verify, bool allow_ab_downgrade, RecoveryUI* ui) {
   CHECK(!path.empty());
 
   auto start = std::chrono::system_clock::now();
@@ -632,7 +636,7 @@
   } else {
     bool updater_wipe_cache = false;
     result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer,
-                                    retry_count, verify, &max_temperature, ui);
+                                    retry_count, verify, allow_ab_downgrade, &max_temperature, ui);
     should_wipe_cache = should_wipe_cache || updater_wipe_cache;
   }