DeltaPerformer: add static PreparePartitionsForUpdate

Expose a static PreparePartitionsForUpdate for
implementation of AllocateSpaceForPayload.

- If this function is called multiple times with the same
'update_check_response_hash', calls after the first call
has no effect.

- If this function is called again with a different
'update_check_response_hash', space is re-allocated.

- DeltaPerformer::ResetUpdateProgress deletes the stored hash
and cause the next PreparePartitionsForUpdate to always re-allocate
space.

- DeltaPerformer::ParseManifestPartitions now set error code to
kNotEnoughSpace when appropriate.

Test: apply an OTA manually
Bug: 138808058
Change-Id: I6fb60016088a3133af3fc961196f63e7d079ae93
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 8cec076..bb7c98c 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -925,8 +925,13 @@
   }
 
   if (install_plan_->target_slot != BootControlInterface::kInvalidSlot) {
-    if (!PreparePartitionsForUpdate()) {
-      *error = ErrorCode::kInstallDeviceOpenError;
+    uint64_t required_size = 0;
+    if (!PreparePartitionsForUpdate(&required_size)) {
+      if (required_size > 0) {
+        *error = ErrorCode::kNotEnoughSpace;
+      } else {
+        *error = ErrorCode::kInstallDeviceOpenError;
+      }
       return false;
     }
   }
@@ -944,21 +949,56 @@
   return true;
 }
 
-bool DeltaPerformer::PreparePartitionsForUpdate() {
-  bool metadata_updated = false;
-  prefs_->GetBoolean(kPrefsDynamicPartitionMetadataUpdated, &metadata_updated);
-  if (!boot_control_->GetDynamicPartitionControl()->PreparePartitionsForUpdate(
-          boot_control_->GetCurrentSlot(),
-          install_plan_->target_slot,
-          manifest_,
-          !metadata_updated,
-          nullptr /* required_size */)) {
+bool DeltaPerformer::PreparePartitionsForUpdate(uint64_t* required_size) {
+  // Call static PreparePartitionsForUpdate with hash from
+  // kPrefsUpdateCheckResponseHash to ensure hash of payload that space is
+  // preallocated for is the same as the hash of payload being applied.
+  string update_check_response_hash;
+  ignore_result(prefs_->GetString(kPrefsUpdateCheckResponseHash,
+                                  &update_check_response_hash));
+  return PreparePartitionsForUpdate(prefs_,
+                                    boot_control_,
+                                    install_plan_->target_slot,
+                                    manifest_,
+                                    update_check_response_hash,
+                                    required_size);
+}
+
+bool DeltaPerformer::PreparePartitionsForUpdate(
+    PrefsInterface* prefs,
+    BootControlInterface* boot_control,
+    BootControlInterface::Slot target_slot,
+    const DeltaArchiveManifest& manifest,
+    const std::string& update_check_response_hash,
+    uint64_t* required_size) {
+  string last_hash;
+  ignore_result(
+      prefs->GetString(kPrefsDynamicPartitionMetadataUpdated, &last_hash));
+
+  bool is_resume = !update_check_response_hash.empty() &&
+                   last_hash == update_check_response_hash;
+
+  if (is_resume) {
+    LOG(INFO) << "Using previously prepared partitions for update. hash = "
+              << last_hash;
+  } else {
+    LOG(INFO) << "Preparing partitions for new update. last hash = "
+              << last_hash << ", new hash = " << update_check_response_hash;
+  }
+
+  if (!boot_control->GetDynamicPartitionControl()->PreparePartitionsForUpdate(
+          boot_control->GetCurrentSlot(),
+          target_slot,
+          manifest,
+          !is_resume /* should update */,
+          required_size)) {
     LOG(ERROR) << "Unable to initialize partition metadata for slot "
-               << BootControlInterface::SlotName(install_plan_->target_slot);
+               << BootControlInterface::SlotName(target_slot);
     return false;
   }
-  TEST_AND_RETURN_FALSE(
-      prefs_->SetBoolean(kPrefsDynamicPartitionMetadataUpdated, true));
+
+  TEST_AND_RETURN_FALSE(prefs->SetString(kPrefsDynamicPartitionMetadataUpdated,
+                                         update_check_response_hash));
   LOG(INFO) << "PreparePartitionsForUpdate done.";
 
   return true;
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 4c64dfa..6dbd3b8 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -179,6 +179,24 @@
                                  const FileDescriptorPtr source_fd,
                                  ErrorCode* error);
 
+  // Initialize partitions and allocate required space for an update with the
+  // given |manifest|. |update_check_response_hash| is used to check if the
+  // previous call to this function corresponds to the same payload.
+  // - Same payload: not make any persistent modifications (not write to disk)
+  // - Different payload: make persistent modifications (write to disk)
+  // In both cases, in-memory flags are updated. This function must be called
+  // on the payload at least once (to update in-memory flags) before writing
+  // (applying) the payload.
+  // If error due to insufficient space, |required_size| is set to the required
+  // size on the device to apply the payload.
+  static bool PreparePartitionsForUpdate(
+      PrefsInterface* prefs,
+      BootControlInterface* boot_control,
+      BootControlInterface::Slot target_slot,
+      const DeltaArchiveManifest& manifest,
+      const std::string& update_check_response_hash,
+      uint64_t* required_size);
+
  private:
   friend class DeltaPerformerTest;
   friend class DeltaPerformerIntegrationTest;
@@ -289,7 +307,8 @@
 
   // After install_plan_ is filled with partition names and sizes, initialize
   // metadata of partitions and map necessary devices before opening devices.
-  bool PreparePartitionsForUpdate();
+  // Also see comment for the static PreparePartitionsForUpdate().
+  bool PreparePartitionsForUpdate(uint64_t* required_size);
 
   // Update Engine preference store.
   PrefsInterface* prefs_;
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index a2ad77b..4797137 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -728,7 +728,7 @@
       .WillRepeatedly(Return(true));
   EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignedSHA256Context, _))
       .WillRepeatedly(Return(true));
-  EXPECT_CALL(prefs, SetBoolean(kPrefsDynamicPartitionMetadataUpdated, _))
+  EXPECT_CALL(prefs, SetString(kPrefsDynamicPartitionMetadataUpdated, _))
       .WillRepeatedly(Return(true));
   if (op_hash_test == kValidOperationData && signature_test != kSignatureNone) {
     EXPECT_CALL(prefs, SetString(kPrefsUpdateStateSignatureBlob, _))