Parse multiple packages from Omaha response.

The multi-payload info are stored in OmahaResponse and InstallPlan, but
we still can only apply the first payload for now.

Bug: 36252799
Test: mma -j
Test: update_engine_unittests

Change-Id: I5ca63944ae9082670d0e67888409374f140d4245
(cherry picked from commit 2aba8a87d4fac245a2e2d238b3159f8eabce630f)
diff --git a/payload_consumer/delta_performer.cc b/payload_consumer/delta_performer.cc
index 21299d7..b14a54f 100644
--- a/payload_consumer/delta_performer.cc
+++ b/payload_consumer/delta_performer.cc
@@ -187,7 +187,7 @@
   }
 
   // Format download total count and percentage.
-  size_t payload_size = install_plan_->payload_size;
+  size_t payload_size = payload_->size;
   string payload_size_str("?");
   string downloaded_percentage_str("");
   if (payload_size) {
@@ -222,7 +222,7 @@
   // eliminated once we ensure that the payload_size in the install plan is
   // always given and is non-zero. This currently isn't the case during unit
   // tests (see chromium-os:37969).
-  size_t payload_size = install_plan_->payload_size;
+  size_t payload_size = payload_->size;
   unsigned actual_operations_weight = kProgressOperationsWeight;
   if (payload_size)
     new_overall_progress += min(
@@ -518,9 +518,9 @@
     // beyond the expected metadata size.
     metadata_size_ = manifest_offset + manifest_size_;
     if (install_plan_->hash_checks_mandatory) {
-      if (install_plan_->metadata_size != metadata_size_) {
+      if (payload_->metadata_size != metadata_size_) {
         LOG(ERROR) << "Mandatory metadata size in Omaha response ("
-                   << install_plan_->metadata_size
+                   << payload_->metadata_size
                    << ") is missing/incorrect, actual = " << metadata_size_;
         *error = ErrorCode::kDownloadInvalidMetadataSize;
         return kMetadataParseError;
@@ -537,13 +537,13 @@
   // here. This is logged here (after we received the full metadata data) so
   // that we just log once (instead of logging n times) if it takes n
   // DeltaPerformer::Write calls to download the full manifest.
-  if (install_plan_->metadata_size == metadata_size_) {
+  if (payload_->metadata_size == metadata_size_) {
     LOG(INFO) << "Manifest size in payload matches expected value from Omaha";
   } else {
     // For mandatory-cases, we'd have already returned a kMetadataParseError
     // above. We'll be here only for non-mandatory cases. Just send a UMA stat.
     LOG(WARNING) << "Ignoring missing/incorrect metadata size ("
-                 << install_plan_->metadata_size
+                 << payload_->metadata_size
                  << ") in Omaha response as validation is not mandatory. "
                  << "Trusting metadata size in payload = " << metadata_size_;
   }
@@ -687,7 +687,7 @@
     // NOTE: If hash checks are mandatory and if metadata_signature is empty,
     // we would have already failed in ParsePayloadMetadata method and thus not
     // even be here. So no need to handle that case again here.
-    if (!install_plan_->metadata_signature.empty()) {
+    if (!payload_->metadata_signature.empty()) {
       // Note: Validate must be called only if CanPerformInstallOperation is
       // called. Otherwise, we might be failing operations before even if there
       // isn't sufficient data to compute the proper hash.
@@ -1323,18 +1323,18 @@
     return ErrorCode::kDownloadMetadataSignatureError;
 
   brillo::Blob metadata_signature_blob, metadata_signature_protobuf_blob;
-  if (!install_plan_->metadata_signature.empty()) {
+  if (!payload_->metadata_signature.empty()) {
     // Convert base64-encoded signature to raw bytes.
-    if (!brillo::data_encoding::Base64Decode(
-        install_plan_->metadata_signature, &metadata_signature_blob)) {
+    if (!brillo::data_encoding::Base64Decode(payload_->metadata_signature,
+                                             &metadata_signature_blob)) {
       LOG(ERROR) << "Unable to decode base64 metadata signature: "
-                 << install_plan_->metadata_signature;
+                 << payload_->metadata_signature;
       return ErrorCode::kDownloadMetadataSignatureError;
     }
   } else if (major_payload_version_ == kBrilloMajorPayloadVersion) {
-    metadata_signature_protobuf_blob.assign(payload.begin() + metadata_size_,
-                                            payload.begin() + metadata_size_ +
-                                            metadata_signature_size_);
+    metadata_signature_protobuf_blob.assign(
+        payload.begin() + metadata_size_,
+        payload.begin() + metadata_size_ + metadata_signature_size_);
   }
 
   if (metadata_signature_blob.empty() &&
diff --git a/payload_consumer/delta_performer.h b/payload_consumer/delta_performer.h
index 7fe2cd2..f363a4c 100644
--- a/payload_consumer/delta_performer.h
+++ b/payload_consumer/delta_performer.h
@@ -78,12 +78,14 @@
                  BootControlInterface* boot_control,
                  HardwareInterface* hardware,
                  DownloadActionDelegate* download_delegate,
-                 InstallPlan* install_plan)
+                 InstallPlan* install_plan,
+                 InstallPlan::Payload* payload)
       : prefs_(prefs),
         boot_control_(boot_control),
         hardware_(hardware),
         download_delegate_(download_delegate),
-        install_plan_(install_plan) {}
+        install_plan_(install_plan),
+        payload_(payload) {}
 
   // FileWriter's Write implementation where caller doesn't care about
   // error codes.
@@ -303,6 +305,9 @@
   // Install Plan based on Omaha Response.
   InstallPlan* install_plan_;
 
+  // Pointer to the current payload in install_plan_.payloads.
+  InstallPlan::Payload* payload_{nullptr};
+
   // File descriptor of the source partition. Only set while updating a
   // partition when using a delta payload.
   FileDescriptorPtr source_fd_{nullptr};
diff --git a/payload_consumer/delta_performer_integration_test.cc b/payload_consumer/delta_performer_integration_test.cc
index e87a907..80d4dc3 100644
--- a/payload_consumer/delta_performer_integration_test.cc
+++ b/payload_consumer/delta_performer_integration_test.cc
@@ -719,8 +719,9 @@
 
   // Update the A image in place.
   InstallPlan* install_plan = &state->install_plan;
+  install_plan->payloads.resize(1);
   install_plan->hash_checks_mandatory = hash_checks_mandatory;
-  install_plan->metadata_size = state->metadata_size;
+  install_plan->payloads[0].metadata_size = state->metadata_size;
   install_plan->payload_type = (full_kernel && full_rootfs)
                                    ? InstallPayloadType::kFull
                                    : InstallPayloadType::kDelta;
@@ -739,14 +740,15 @@
       state->delta.data(),
       state->metadata_size,
       GetBuildArtifactsPath(kUnittestPrivateKeyPath),
-      &install_plan->metadata_signature));
-  EXPECT_FALSE(install_plan->metadata_signature.empty());
+      &install_plan->payloads[0].metadata_signature));
+  EXPECT_FALSE(install_plan->payloads[0].metadata_signature.empty());
 
   *performer = new DeltaPerformer(&prefs,
                                   &state->fake_boot_control_,
                                   &state->fake_hardware_,
                                   &state->mock_delegate_,
-                                  install_plan);
+                                  install_plan,
+                                  &install_plan->payloads[0]);
   string public_key_path = GetBuildArtifactsPath(kUnittestPublicKeyPath);
   EXPECT_TRUE(utils::FileExists(public_key_path.c_str()));
   (*performer)->set_public_key_path(public_key_path);
diff --git a/payload_consumer/delta_performer_unittest.cc b/payload_consumer/delta_performer_unittest.cc
index 18481a7..3af13ec 100644
--- a/payload_consumer/delta_performer_unittest.cc
+++ b/payload_consumer/delta_performer_unittest.cc
@@ -164,7 +164,7 @@
     string private_key =
         sign_payload ? GetBuildArtifactsPath(kUnittestPrivateKeyPath) : "";
     EXPECT_TRUE(payload.WritePayload(
-        payload_path, blob_path, private_key, &install_plan_.metadata_size));
+        payload_path, blob_path, private_key, &payload_.metadata_size));
 
     brillo::Blob payload_data;
     EXPECT_TRUE(utils::ReadFile(payload_path, &payload_data));
@@ -231,7 +231,7 @@
     uint64_t version = htobe64(kChromeOSMajorPayloadVersion);
     EXPECT_TRUE(performer_.Write(&version, 8));
 
-    install_plan_.metadata_size = expected_metadata_size;
+    payload_.metadata_size = expected_metadata_size;
     ErrorCode error_code;
     // When filling in size in manifest, exclude the size of the 20-byte header.
     uint64_t size_in_manifest = htobe64(actual_metadata_size - 20);
@@ -268,13 +268,13 @@
     // Fill up the metadata signature in install plan according to the test.
     switch (metadata_signature_test) {
       case kEmptyMetadataSignature:
-        install_plan_.metadata_signature.clear();
+        payload_.metadata_signature.clear();
         expected_result = DeltaPerformer::kMetadataParseError;
         expected_error = ErrorCode::kDownloadMetadataSignatureMissingError;
         break;
 
       case kInvalidMetadataSignature:
-        install_plan_.metadata_signature = kBogusMetadataSignature1;
+        payload_.metadata_signature = kBogusMetadataSignature1;
         expected_result = DeltaPerformer::kMetadataParseError;
         expected_error = ErrorCode::kDownloadMetadataSignatureMismatch;
         break;
@@ -286,10 +286,10 @@
         // then we can get to manifest signature checks.
         ASSERT_TRUE(PayloadSigner::GetMetadataSignature(
             payload.data(),
-            install_plan_.metadata_size,
+            payload_.metadata_size,
             GetBuildArtifactsPath(kUnittestPrivateKeyPath),
-            &install_plan_.metadata_signature));
-        EXPECT_FALSE(install_plan_.metadata_signature.empty());
+            &payload_.metadata_signature));
+        EXPECT_FALSE(payload_.metadata_signature.empty());
         expected_result = DeltaPerformer::kMetadataParseSuccess;
         expected_error = ErrorCode::kSuccess;
         break;
@@ -317,7 +317,7 @@
 
     // Check that the parsed metadata size is what's expected. This test
     // implicitly confirms that the metadata signature is valid, if required.
-    EXPECT_EQ(install_plan_.metadata_size, performer_.GetMetadataSize());
+    EXPECT_EQ(payload_.metadata_size, performer_.GetMetadataSize());
   }
 
   void SetSupportedMajorVersion(uint64_t major_version) {
@@ -325,11 +325,16 @@
   }
   FakePrefs prefs_;
   InstallPlan install_plan_;
+  InstallPlan::Payload payload_;
   FakeBootControl fake_boot_control_;
   FakeHardware fake_hardware_;
   MockDownloadActionDelegate mock_delegate_;
-  DeltaPerformer performer_{
-      &prefs_, &fake_boot_control_, &fake_hardware_, &mock_delegate_, &install_plan_};
+  DeltaPerformer performer_{&prefs_,
+                            &fake_boot_control_,
+                            &fake_hardware_,
+                            &mock_delegate_,
+                            &install_plan_,
+                            &payload_};
 };
 
 TEST_F(DeltaPerformerTest, FullPayloadWriteTest) {
@@ -666,7 +671,7 @@
   install_plan_.hash_checks_mandatory = true;
   // Just set these value so that we can use ValidateMetadataSignature directly.
   performer_.major_payload_version_ = kBrilloMajorPayloadVersion;
-  performer_.metadata_size_ = install_plan_.metadata_size;
+  performer_.metadata_size_ = payload_.metadata_size;
   uint64_t signature_length;
   EXPECT_TRUE(PayloadSigner::SignatureBlobLength(
       {GetBuildArtifactsPath(kUnittestPrivateKeyPath)}, &signature_length));
diff --git a/payload_consumer/download_action.cc b/payload_consumer/download_action.cc
index 65ae1ab..a8c987e 100644
--- a/payload_consumer/download_action.cc
+++ b/payload_consumer/download_action.cc
@@ -83,7 +83,7 @@
 bool DownloadAction::SetupP2PSharingFd() {
   P2PManager *p2p_manager = system_state_->p2p_manager();
 
-  if (!p2p_manager->FileShare(p2p_file_id_, install_plan_.payload_size)) {
+  if (!p2p_manager->FileShare(p2p_file_id_, payload_->size)) {
     LOG(ERROR) << "Unable to share file via p2p";
     CloseP2PSharingFd(true);  // delete p2p file
     return false;
@@ -174,6 +174,9 @@
   bytes_received_ = 0;
 
   install_plan_.Dump();
+  // TODO(senj): check that install plan has at least one payload.
+  if (!payload_)
+    payload_ = &install_plan_.payloads[0];
 
   LOG(INFO) << "Marking new slot as unbootable";
   if (!boot_control_->MarkSlotUnbootable(install_plan_.target_slot)) {
@@ -186,15 +189,14 @@
     LOG(INFO) << "Using writer for test.";
   } else {
     delta_performer_.reset(new DeltaPerformer(
-        prefs_, boot_control_, hardware_, delegate_, &install_plan_));
+        prefs_, boot_control_, hardware_, delegate_, &install_plan_, payload_));
     writer_ = delta_performer_.get();
   }
   download_active_ = true;
 
   if (system_state_ != nullptr) {
     const PayloadStateInterface* payload_state = system_state_->payload_state();
-    string file_id = utils::CalculateP2PFileId(install_plan_.payload_hash,
-                                               install_plan_.payload_size);
+    string file_id = utils::CalculateP2PFileId(payload_->hash, payload_->size);
     if (payload_state->GetUsingP2PForSharing()) {
       // If we're sharing the update, store the file_id to convey
       // that we should write to the file.
@@ -266,8 +268,7 @@
 
   bytes_received_ += length;
   if (delegate_ && download_active_) {
-    delegate_->BytesReceived(
-        length, bytes_received_, install_plan_.payload_size);
+    delegate_->BytesReceived(length, bytes_received_, payload_->size);
   }
   if (writer_ && !writer_->Write(bytes, length, &code_)) {
     LOG(ERROR) << "Error " << utils::ErrorCodeToString(code_) << " (" << code_
@@ -302,8 +303,7 @@
   ErrorCode code =
       successful ? ErrorCode::kSuccess : ErrorCode::kDownloadTransferError;
   if (code == ErrorCode::kSuccess && delta_performer_.get()) {
-    code = delta_performer_->VerifyPayload(install_plan_.payload_hash,
-                                           install_plan_.payload_size);
+    code = delta_performer_->VerifyPayload(payload_->hash, payload_->size);
     if (code != ErrorCode::kSuccess) {
       LOG(ERROR) << "Download of " << install_plan_.download_url
                  << " failed due to payload verification error.";
diff --git a/payload_consumer/download_action.h b/payload_consumer/download_action.h
index 285930a..0bd0d88 100644
--- a/payload_consumer/download_action.h
+++ b/payload_consumer/download_action.h
@@ -134,6 +134,9 @@
   // The InstallPlan passed in
   InstallPlan install_plan_;
 
+  // Pointer to the current payload in install_plan_.payloads.
+  InstallPlan::Payload* payload_{nullptr};
+
   // SystemState required pointers.
   PrefsInterface* prefs_;
   BootControlInterface* boot_control_;
diff --git a/payload_consumer/download_action_unittest.cc b/payload_consumer/download_action_unittest.cc
index 4392b74..57910cc 100644
--- a/payload_consumer/download_action_unittest.cc
+++ b/payload_consumer/download_action_unittest.cc
@@ -142,10 +142,10 @@
   uint64_t size = data.size();
   InstallPlan install_plan;
   install_plan.payload_type = InstallPayloadType::kDelta;
-  install_plan.payload_size = size;
+  install_plan.payloads.push_back({.size = size});
   // We pull off the first byte from data and seek past it.
   EXPECT_TRUE(HashCalculator::RawHashOfBytes(
-      &data[1], data.size() - 1, &install_plan.payload_hash));
+      &data[1], data.size() - 1, &install_plan.payloads[0].hash));
   install_plan.source_slot = 0;
   install_plan.target_slot = 1;
   // We mark both slots as bootable. Only the target slot should be unbootable
@@ -272,6 +272,7 @@
     // takes ownership of passed in HttpFetcher
     ObjectFeederAction<InstallPlan> feeder_action;
     InstallPlan install_plan;
+    install_plan.payloads.resize(1);
     feeder_action.set_obj(install_plan);
     FakeSystemState fake_system_state_;
     MockPrefs prefs;
@@ -370,8 +371,9 @@
 
   // takes ownership of passed in HttpFetcher
   InstallPlan install_plan;
-  install_plan.payload_size = 1;
-  EXPECT_TRUE(HashCalculator::RawHashOfData({'x'}, &install_plan.payload_hash));
+  install_plan.payloads.push_back({.size = 1});
+  EXPECT_TRUE(
+      HashCalculator::RawHashOfData({'x'}, &install_plan.payloads[0].hash));
   ObjectFeederAction<InstallPlan> feeder_action;
   feeder_action.set_obj(install_plan);
   MockPrefs prefs;
@@ -455,8 +457,9 @@
     EXPECT_EQ(
         0, writer.Open(output_temp_file.path().c_str(), O_WRONLY | O_CREAT, 0));
     InstallPlan install_plan;
-    install_plan.payload_size = data_.length();
-    install_plan.payload_hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'};
+    install_plan.payloads.push_back(
+        {.size = data_.length(),
+         .hash = {'1', '2', '3', '4', 'h', 'a', 's', 'h'}});
     ObjectFeederAction<InstallPlan> feeder_action;
     feeder_action.set_obj(install_plan);
     MockPrefs prefs;
diff --git a/payload_consumer/install_plan.cc b/payload_consumer/install_plan.cc
index fff0ac2..5f004bf 100644
--- a/payload_consumer/install_plan.cc
+++ b/payload_consumer/install_plan.cc
@@ -43,14 +43,9 @@
 bool InstallPlan::operator==(const InstallPlan& that) const {
   return ((is_resume == that.is_resume) &&
           (payload_type == that.payload_type) &&
-          (download_url == that.download_url) &&
-          (payload_size == that.payload_size) &&
-          (payload_hash == that.payload_hash) &&
-          (metadata_size == that.metadata_size) &&
-          (metadata_signature == that.metadata_signature) &&
+          (download_url == that.download_url) && (payloads == that.payloads) &&
           (source_slot == that.source_slot) &&
-          (target_slot == that.target_slot) &&
-          (partitions == that.partitions));
+          (target_slot == that.target_slot) && (partitions == that.partitions));
 }
 
 bool InstallPlan::operator!=(const InstallPlan& that) const {
@@ -68,16 +63,22 @@
                            partition.target_size,
                            utils::ToString(partition.run_postinstall).c_str());
   }
+  string payloads_str;
+  for (const auto& payload : payloads) {
+    payloads_str += base::StringPrintf(
+        ", payload: (size: %" PRIu64 ", metadata_size: %" PRIu64
+        ", metadata signature: %s, hash: %s)",
+        payload.size,
+        payload.metadata_size,
+        payload.metadata_signature.c_str(),
+        base::HexEncode(payload.hash.data(), payload.hash.size()).c_str());
+  }
 
   LOG(INFO) << "InstallPlan: " << (is_resume ? "resume" : "new_update")
             << ", payload type: " << InstallPayloadTypeToString(payload_type)
             << ", source_slot: " << BootControlInterface::SlotName(source_slot)
             << ", target_slot: " << BootControlInterface::SlotName(target_slot)
-            << ", url: " << download_url << ", payload size: " << payload_size
-            << ", payload hash: "
-            << base::HexEncode(payload_hash.data(), payload_hash.size())
-            << ", metadata size: " << metadata_size
-            << ", metadata signature: " << metadata_signature << partitions_str
+            << ", url: " << download_url << payloads_str << partitions_str
             << ", hash_checks_mandatory: "
             << utils::ToString(hash_checks_mandatory)
             << ", powerwash_required: " << utils::ToString(powerwash_required);
diff --git a/payload_consumer/install_plan.h b/payload_consumer/install_plan.h
index 0e25cc3..db471da 100644
--- a/payload_consumer/install_plan.h
+++ b/payload_consumer/install_plan.h
@@ -56,10 +56,18 @@
   std::string download_url;  // url to download from
   std::string version;       // version we are installing.
 
-  uint64_t payload_size{0};              // size of the payload
-  brillo::Blob payload_hash;             // SHA256 hash of the payload
-  uint64_t metadata_size{0};             // size of the metadata
-  std::string metadata_signature;        // signature of the  metadata
+  struct Payload {
+    uint64_t size = 0;               // size of the payload
+    uint64_t metadata_size = 0;      // size of the metadata
+    std::string metadata_signature;  // signature of the metadata in base64
+    brillo::Blob hash;               // SHA256 hash of the payload
+
+    bool operator==(const Payload& that) const {
+      return size == that.size && metadata_size == that.metadata_size &&
+             metadata_signature == that.metadata_signature && hash == that.hash;
+    }
+  };
+  std::vector<Payload> payloads;
 
   // The partition slots used for the update.
   BootControlInterface::Slot source_slot{BootControlInterface::kInvalidSlot};