Implement setShouldSwitchSlotOnReboot and resetShouldSwitchSlotOnReboot am: 20982a52a8

Original change: https://android-review.googlesource.com/c/platform/system/update_engine/+/1795810

Change-Id: I943f762c589f0a6ec06c2bf6f3d72da6c68e23b7
diff --git a/aosp/update_attempter_android.cc b/aosp/update_attempter_android.cc
index cceecd9..f2ec2a5 100644
--- a/aosp/update_attempter_android.cc
+++ b/aosp/update_attempter_android.cc
@@ -426,38 +426,11 @@
     }
 
     case UpdateStatus::UPDATED_NEED_REBOOT: {
-      bool ret_value = true;
-
-      // Update the boot flags so the current slot has higher priority.
-      if (!boot_control_->SetActiveBootSlot(GetCurrentSlot()))
-        ret_value = false;
-
-      // Mark the current slot as successful again, since marking it as active
-      // may reset the successful bit. We ignore the result of whether marking
-      // the current slot as successful worked.
-      if (!boot_control_->MarkBootSuccessfulAsync(Bind([](bool successful) {})))
-        ret_value = false;
-
-      // Resets the warm reset property since we won't switch the slot.
-      hardware_->SetWarmReset(false);
-
-      // Resets the vbmeta digest.
-      hardware_->SetVbmetaDigestForInactiveSlot(true /* reset */);
-
-      // Remove update progress for DeltaPerformer and remove snapshots.
-      if (!boot_control_->GetDynamicPartitionControl()->ResetUpdate(prefs_))
-        ret_value = false;
-
-      ClearMetricsPrefs();
-
-      if (!ret_value) {
-        return LogAndSetError(
-            error, FROM_HERE, "Failed to reset the status to ");
+      const bool ret_value = resetShouldSwitchSlotOnReboot(error);
+      if (ret_value) {
+        LOG(INFO) << "Reset status successful";
       }
-
-      SetStatusAndNotify(UpdateStatus::IDLE);
-      LOG(INFO) << "Reset status successful";
-      return true;
+      return ret_value;
     }
 
     default:
@@ -721,6 +694,7 @@
 
 void UpdateAttempterAndroid::ScheduleProcessingStart() {
   LOG(INFO) << "Scheduling an action processor start.";
+  processor_->set_delegate(this);
   brillo::MessageLoop::current()->PostTask(
       FROM_HERE,
       Bind([](ActionProcessor* processor) { processor->StartProcessing(); },
@@ -788,7 +762,6 @@
 
 void UpdateAttempterAndroid::BuildUpdateActions(HttpFetcher* fetcher) {
   CHECK(!processor_->IsRunning());
-  processor_->set_delegate(this);
 
   // Actions:
   auto update_boot_flags_action =
@@ -871,6 +844,12 @@
       payload_type = kPayloadTypeDelta;
     payload_size += p.size;
   }
+  // In some cases, e.g. after calling |setShouldSwitchSlotOnReboot()|,  this
+  // function will be triggered, but payload_size in this case might be 0, if so
+  // skip reporting any metrics.
+  if (payload_size == 0) {
+    return;
+  }
 
   metrics::AttemptResult attempt_result =
       metrics_utils::GetAttemptResult(error_code);
@@ -1169,13 +1148,67 @@
 
 bool UpdateAttempterAndroid::setShouldSwitchSlotOnReboot(
     const std::string& metadata_filename, brillo::ErrorPtr* error) {
+  LOG(INFO) << "setShouldSwitchSlotOnReboot(" << metadata_filename << ")";
   if (processor_->IsRunning()) {
     return LogAndSetError(
         error, FROM_HERE, "Already processing an update, cancel it first.");
   }
-  // TODO(187321613) Implement this
-  return LogAndSetError(
-      error, FROM_HERE, "setShouldSwitchSlotOnReboot is not implemented yet.");
+  DeltaArchiveManifest manifest;
+  TEST_AND_RETURN_FALSE(
+      VerifyPayloadParseManifest(metadata_filename, &manifest, error));
+
+  if (!boot_control_->GetDynamicPartitionControl()->PreparePartitionsForUpdate(
+          GetCurrentSlot(),
+          GetTargetSlot(),
+          manifest,
+          false /* should update */,
+          nullptr)) {
+    return LogAndSetError(
+        error, FROM_HERE, "Failed to PreparePartitionsForUpdate");
+  }
+  InstallPlan install_plan_;
+  install_plan_.source_slot = GetCurrentSlot();
+  install_plan_.target_slot = GetTargetSlot();
+  // Don't do verity computation, just hash the partitions
+  install_plan_.write_verity = false;
+  // Don't run postinstall, we just need PostinstallAction to switch the slots.
+  install_plan_.run_post_install = false;
+  install_plan_.is_resume = true;
+
+  CHECK_NE(install_plan_.source_slot, UINT32_MAX);
+  CHECK_NE(install_plan_.target_slot, UINT32_MAX);
+
+  ErrorCode error_code;
+  if (!install_plan_.ParsePartitions(manifest.partitions(),
+                                     boot_control_,
+                                     manifest.block_size(),
+                                     &error_code)) {
+    return LogAndSetError(error,
+                          FROM_HERE,
+                          "Failed to LoadPartitionsFromSlots " +
+                              utils::ErrorCodeToString(error_code));
+  }
+
+  auto install_plan_action = std::make_unique<InstallPlanAction>(install_plan_);
+  auto filesystem_verifier_action = std::make_unique<FilesystemVerifierAction>(
+      boot_control_->GetDynamicPartitionControl());
+  auto postinstall_runner_action =
+      std::make_unique<PostinstallRunnerAction>(boot_control_, hardware_);
+  SetStatusAndNotify(UpdateStatus::VERIFYING);
+  filesystem_verifier_action->set_delegate(this);
+  postinstall_runner_action->set_delegate(this);
+
+  // Bond them together. We have to use the leaf-types when calling
+  // BondActions().
+  BondActions(install_plan_action.get(), filesystem_verifier_action.get());
+  BondActions(filesystem_verifier_action.get(),
+              postinstall_runner_action.get());
+
+  processor_->EnqueueAction(std::move(install_plan_action));
+  processor_->EnqueueAction(std::move(filesystem_verifier_action));
+  processor_->EnqueueAction(std::move(postinstall_runner_action));
+  ScheduleProcessingStart();
+  return true;
 }
 
 bool UpdateAttempterAndroid::resetShouldSwitchSlotOnReboot(
@@ -1184,11 +1217,27 @@
     return LogAndSetError(
         error, FROM_HERE, "Already processing an update, cancel it first.");
   }
-  // TODO(187321613) Implement this
-  return LogAndSetError(
-      error,
-      FROM_HERE,
-      "resetShouldSwitchSlotOnReboot is not implemented yet.");
+  // Update the boot flags so the current slot has higher priority.
+  if (!boot_control_->SetActiveBootSlot(GetCurrentSlot())) {
+    return LogAndSetError(error, FROM_HERE, "Failed to SetActiveBootSlot");
+  }
+
+  // Mark the current slot as successful again, since marking it as active
+  // may reset the successful bit. We ignore the result of whether marking
+  // the current slot as successful worked.
+  if (!boot_control_->MarkBootSuccessfulAsync(Bind([](bool successful) {}))) {
+    return LogAndSetError(
+        error, FROM_HERE, "Failed to MarkBootSuccessfulAsync");
+  }
+
+  // Resets the warm reset property since we won't switch the slot.
+  hardware_->SetWarmReset(false);
+
+  // Resets the vbmeta digest.
+  hardware_->SetVbmetaDigestForInactiveSlot(true /* reset */);
+  LOG(INFO) << "Slot switch cancelled.";
+  SetStatusAndNotify(UpdateStatus::IDLE);
+  return true;
 }
 
 void UpdateAttempterAndroid::ScheduleCleanupPreviousUpdate() {
diff --git a/common/utils.cc b/common/utils.cc
index 25df540..45ad425 100644
--- a/common/utils.cc
+++ b/common/utils.cc
@@ -1047,4 +1047,8 @@
 
 }  // namespace utils
 
+std::string HexEncode(const brillo::Blob& blob) noexcept {
+  return base::HexEncode(blob.data(), blob.size());
+}
+
 }  // namespace chromeos_update_engine
diff --git a/common/utils.h b/common/utils.h
index 9ed0c7b..c1820ee 100644
--- a/common/utils.h
+++ b/common/utils.h
@@ -32,6 +32,7 @@
 
 #include <base/files/file_path.h>
 #include <base/posix/eintr_wrapper.h>
+#include <base/strings/string_number_conversions.h>
 #include <base/time/time.h>
 #include <brillo/key_value_store.h>
 #include <brillo/secure_blob.h>
@@ -479,6 +480,8 @@
   T t2_;
 };
 
+std::string HexEncode(const brillo::Blob& blob) noexcept;
+
 }  // namespace chromeos_update_engine
 
 #define TEST_AND_RETURN_FALSE_ERRNO(_x)                              \
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 31fa90e..f067a82 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -624,10 +624,8 @@
 }
 
 bool DeltaPerformer::ParseManifestPartitions(ErrorCode* error) {
-  partitions_.clear();
-  for (const PartitionUpdate& partition : manifest_.partitions()) {
-    partitions_.push_back(partition);
-  }
+  partitions_.assign(manifest_.partitions().begin(),
+                     manifest_.partitions().end());
 
   // For VAB and partial updates, the partition preparation will copy the
   // dynamic partitions metadata to the target metadata slot, and rename the
@@ -685,87 +683,11 @@
     }
   }
 
-  // Fill in the InstallPlan::partitions based on the partitions from the
-  // payload.
-  for (const auto& partition : partitions_) {
-    InstallPlan::Partition install_part;
-    install_part.name = partition.partition_name();
-    install_part.run_postinstall =
-        partition.has_run_postinstall() && partition.run_postinstall();
-    if (install_part.run_postinstall) {
-      install_part.postinstall_path =
-          (partition.has_postinstall_path() ? partition.postinstall_path()
-                                            : kPostinstallDefaultScript);
-      install_part.filesystem_type = partition.filesystem_type();
-      install_part.postinstall_optional = partition.postinstall_optional();
-    }
-
-    if (partition.has_old_partition_info()) {
-      const PartitionInfo& info = partition.old_partition_info();
-      install_part.source_size = info.size();
-      install_part.source_hash.assign(info.hash().begin(), info.hash().end());
-    }
-
-    if (!partition.has_new_partition_info()) {
-      LOG(ERROR) << "Unable to get new partition hash info on partition "
-                 << install_part.name << ".";
-      *error = ErrorCode::kDownloadNewPartitionInfoError;
-      return false;
-    }
-    const PartitionInfo& info = partition.new_partition_info();
-    install_part.target_size = info.size();
-    install_part.target_hash.assign(info.hash().begin(), info.hash().end());
-
-    install_part.block_size = block_size_;
-    if (partition.has_hash_tree_extent()) {
-      Extent extent = partition.hash_tree_data_extent();
-      install_part.hash_tree_data_offset = extent.start_block() * block_size_;
-      install_part.hash_tree_data_size = extent.num_blocks() * block_size_;
-      extent = partition.hash_tree_extent();
-      install_part.hash_tree_offset = extent.start_block() * block_size_;
-      install_part.hash_tree_size = extent.num_blocks() * block_size_;
-      uint64_t hash_tree_data_end =
-          install_part.hash_tree_data_offset + install_part.hash_tree_data_size;
-      if (install_part.hash_tree_offset < hash_tree_data_end) {
-        LOG(ERROR) << "Invalid hash tree extents, hash tree data ends at "
-                   << hash_tree_data_end << ", but hash tree starts at "
-                   << install_part.hash_tree_offset;
-        *error = ErrorCode::kDownloadNewPartitionInfoError;
-        return false;
-      }
-      install_part.hash_tree_algorithm = partition.hash_tree_algorithm();
-      install_part.hash_tree_salt.assign(partition.hash_tree_salt().begin(),
-                                         partition.hash_tree_salt().end());
-    }
-    if (partition.has_fec_extent()) {
-      Extent extent = partition.fec_data_extent();
-      install_part.fec_data_offset = extent.start_block() * block_size_;
-      install_part.fec_data_size = extent.num_blocks() * block_size_;
-      extent = partition.fec_extent();
-      install_part.fec_offset = extent.start_block() * block_size_;
-      install_part.fec_size = extent.num_blocks() * block_size_;
-      uint64_t fec_data_end =
-          install_part.fec_data_offset + install_part.fec_data_size;
-      if (install_part.fec_offset < fec_data_end) {
-        LOG(ERROR) << "Invalid fec extents, fec data ends at " << fec_data_end
-                   << ", but fec starts at " << install_part.fec_offset;
-        *error = ErrorCode::kDownloadNewPartitionInfoError;
-        return false;
-      }
-      install_part.fec_roots = partition.fec_roots();
-    }
-
-    install_plan_->partitions.push_back(install_part);
-  }
-
-  // TODO(xunchang) only need to load the partitions for those in payload.
-  // Because we have already loaded the other once when generating SOURCE_COPY
-  // operations.
-  if (!install_plan_->LoadPartitionsFromSlots(boot_control_)) {
-    LOG(ERROR) << "Unable to determine all the partition devices.";
-    *error = ErrorCode::kInstallDeviceOpenError;
+  if (!install_plan_->ParsePartitions(
+          partitions_, boot_control_, block_size_, error)) {
     return false;
   }
+
   LogPartitionInfo(partitions_);
   return true;
 }
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index 06b7dd8..2bd81ae 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -26,6 +26,7 @@
 #include <base/strings/stringprintf.h>
 
 #include "update_engine/common/utils.h"
+#include "update_engine/update_metadata.pb.h"
 #include "update_engine/payload_consumer/payload_constants.h"
 
 using std::string;
@@ -186,4 +187,113 @@
           postinstall_optional == that.postinstall_optional);
 }
 
+template <typename PartitinoUpdateArray>
+bool InstallPlan::ParseManifestToInstallPlan(
+    const PartitinoUpdateArray& partitions,
+    BootControlInterface* boot_control,
+    size_t block_size,
+    InstallPlan* install_plan,
+    ErrorCode* error) {
+  // Fill in the InstallPlan::partitions based on the partitions from the
+  // payload.
+  for (const PartitionUpdate& partition : partitions) {
+    InstallPlan::Partition install_part;
+    install_part.name = partition.partition_name();
+    install_part.run_postinstall =
+        partition.has_run_postinstall() && partition.run_postinstall();
+    if (install_part.run_postinstall) {
+      install_part.postinstall_path =
+          (partition.has_postinstall_path() ? partition.postinstall_path()
+                                            : kPostinstallDefaultScript);
+      install_part.filesystem_type = partition.filesystem_type();
+      install_part.postinstall_optional = partition.postinstall_optional();
+    }
+
+    if (partition.has_old_partition_info()) {
+      const PartitionInfo& info = partition.old_partition_info();
+      install_part.source_size = info.size();
+      install_part.source_hash.assign(info.hash().begin(), info.hash().end());
+    }
+
+    if (!partition.has_new_partition_info()) {
+      LOG(ERROR) << "Unable to get new partition hash info on partition "
+                 << install_part.name << ".";
+      *error = ErrorCode::kDownloadNewPartitionInfoError;
+      return false;
+    }
+    const PartitionInfo& info = partition.new_partition_info();
+    install_part.target_size = info.size();
+    install_part.target_hash.assign(info.hash().begin(), info.hash().end());
+
+    install_part.block_size = block_size;
+    if (partition.has_hash_tree_extent()) {
+      Extent extent = partition.hash_tree_data_extent();
+      install_part.hash_tree_data_offset = extent.start_block() * block_size;
+      install_part.hash_tree_data_size = extent.num_blocks() * block_size;
+      extent = partition.hash_tree_extent();
+      install_part.hash_tree_offset = extent.start_block() * block_size;
+      install_part.hash_tree_size = extent.num_blocks() * block_size;
+      uint64_t hash_tree_data_end =
+          install_part.hash_tree_data_offset + install_part.hash_tree_data_size;
+      if (install_part.hash_tree_offset < hash_tree_data_end) {
+        LOG(ERROR) << "Invalid hash tree extents, hash tree data ends at "
+                   << hash_tree_data_end << ", but hash tree starts at "
+                   << install_part.hash_tree_offset;
+        *error = ErrorCode::kDownloadNewPartitionInfoError;
+        return false;
+      }
+      install_part.hash_tree_algorithm = partition.hash_tree_algorithm();
+      install_part.hash_tree_salt.assign(partition.hash_tree_salt().begin(),
+                                         partition.hash_tree_salt().end());
+    }
+    if (partition.has_fec_extent()) {
+      Extent extent = partition.fec_data_extent();
+      install_part.fec_data_offset = extent.start_block() * block_size;
+      install_part.fec_data_size = extent.num_blocks() * block_size;
+      extent = partition.fec_extent();
+      install_part.fec_offset = extent.start_block() * block_size;
+      install_part.fec_size = extent.num_blocks() * block_size;
+      uint64_t fec_data_end =
+          install_part.fec_data_offset + install_part.fec_data_size;
+      if (install_part.fec_offset < fec_data_end) {
+        LOG(ERROR) << "Invalid fec extents, fec data ends at " << fec_data_end
+                   << ", but fec starts at " << install_part.fec_offset;
+        *error = ErrorCode::kDownloadNewPartitionInfoError;
+        return false;
+      }
+      install_part.fec_roots = partition.fec_roots();
+    }
+
+    install_plan->partitions.push_back(install_part);
+  }
+
+  // TODO(xunchang) only need to load the partitions for those in payload.
+  // Because we have already loaded the other once when generating SOURCE_COPY
+  // operations.
+  if (!install_plan->LoadPartitionsFromSlots(boot_control)) {
+    LOG(ERROR) << "Unable to determine all the partition devices.";
+    *error = ErrorCode::kInstallDeviceOpenError;
+    return false;
+  }
+  return true;
+}
+
+bool InstallPlan::ParsePartitions(
+    const std::vector<PartitionUpdate>& partitions,
+    BootControlInterface* boot_control,
+    size_t block_size,
+    ErrorCode* error) {
+  return ParseManifestToInstallPlan(
+      partitions, boot_control, block_size, this, error);
+}
+
+bool InstallPlan::ParsePartitions(
+    const google::protobuf::RepeatedPtrField<PartitionUpdate>& partitions,
+    BootControlInterface* boot_control,
+    size_t block_size,
+    ErrorCode* error) {
+  return ParseManifestToInstallPlan(
+      partitions, boot_control, block_size, this, error);
+}
+
 }  // namespace chromeos_update_engine
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 7c77789..0278ea5 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -47,10 +47,30 @@
   void Dump() const;
   std::string ToString() const;
 
+ private:
   // Loads the |source_path| and |target_path| of all |partitions| based on the
   // |source_slot| and |target_slot| if available. Returns whether it succeeded
   // to load all the partitions for the valid slots.
   bool LoadPartitionsFromSlots(BootControlInterface* boot_control);
+  template <typename PartitinoUpdateArray>
+  static bool ParseManifestToInstallPlan(const PartitinoUpdateArray& partitions,
+                                         BootControlInterface* boot_control,
+                                         size_t block_size,
+                                         InstallPlan* install_plan,
+                                         ErrorCode* error);
+
+ public:
+  // Load all partitions in |partitions| into this install plan, will also
+  // populate |source_path|, |target_pathh|, fec information, partition sizes.
+  bool ParsePartitions(const std::vector<PartitionUpdate>& partitions,
+                       BootControlInterface* boot_control,
+                       size_t block_size,
+                       ErrorCode* error);
+  bool ParsePartitions(
+      const google::protobuf::RepeatedPtrField<PartitionUpdate>& partitions,
+      BootControlInterface* boot_control,
+      size_t block_size,
+      ErrorCode* error);
 
   bool is_resume{false};
   std::string download_url;  // url to download from