update_engine: Perform artifacts meta installation action

This flow is to perform an artifacts meta based installation.
The prebuilt DLCs are the first to utilize this pipeline.

BUG=b:286327155
TEST=CQ

Change-Id: I2064820d9387b67b67c3dc29f21c79e8c8009fe6
Reviewed-on: https://chromium-review.googlesource.com/c/aosp/platform/system/update_engine/+/4824829
Reviewed-by: Yuanpeng Ni‎ <yuanpengni@chromium.org>
Commit-Queue: Jae Hoon Kim <kimjae@chromium.org>
Tested-by: Jae Hoon Kim <kimjae@chromium.org>
diff --git a/cros/install_action.cc b/cros/install_action.cc
index 423e232..6913af7 100644
--- a/cros/install_action.cc
+++ b/cros/install_action.cc
@@ -41,6 +41,9 @@
 constexpr char kBandaidUrl[] = "https://edgedl.me.gvt1.com/edgedl/dlc";
 constexpr char kLorryUrl[] = "https://dl.google.com/dlc";
 
+constexpr char kBandaidArtifactsMetaUrl[] = "https://edgedl.me.gvt1.com/edgedl";
+constexpr char kLorryArtifactsMetaUrl[] = "https://dl.google.com";
+
 constexpr char kDefaultArtifact[] = "dlc.img";
 constexpr char kDefaultPackage[] = "package";
 constexpr char kDefaultSlotting[] = "dlc-scaled";
@@ -50,9 +53,7 @@
                              const std::string& id,
                              const std::string& slotting,
                              const std::string& manifest_dir)
-    : http_fetcher_(std::move(http_fetcher)),
-      id_(id),
-      backup_urls_({kLorryUrl}) {
+    : http_fetcher_(std::move(http_fetcher)), id_(id) {
   slotting_ = slotting.empty() ? kDefaultSlotting : slotting;
   manifest_dir_ =
       manifest_dir.empty() ? imageloader::kDlcManifestRootpath : manifest_dir;
@@ -95,7 +96,32 @@
     return;
   }
   LOG(INFO) << "Installing to " << partition;
-  StartInstallation(kBandaidUrl);
+
+  std::string url_to_fetch;
+  const auto& artifacts_meta = manifest_->artifacts_meta();
+  if (artifacts_meta.valid) {
+    auto UrlToFetch = [artifacts_meta](const std::string& url) -> std::string {
+      return base::FilePath(url)
+          .Append(artifacts_meta.uri)
+          .Append(kDefaultArtifact)
+          .value();
+    };
+    url_to_fetch = UrlToFetch(kBandaidArtifactsMetaUrl);
+    backup_urls_ = {UrlToFetch(kLorryArtifactsMetaUrl)};
+  } else {
+    auto UrlToFetch = [this](const std::string& url) -> std::string {
+      return base::FilePath(url)
+          .Append(image_props_.builder_path)
+          .Append(slotting_)
+          .Append(id_)
+          .Append(kDefaultPackage)
+          .Append(kDefaultArtifact)
+          .value();
+    };
+    url_to_fetch = UrlToFetch(kBandaidUrl);
+    backup_urls_ = {UrlToFetch(kLorryUrl)};
+  }
+  StartInstallation(url_to_fetch);
 }
 
 void InstallAction::TerminateProcessing() {
@@ -184,17 +210,10 @@
   TerminateInstallation();
 }
 
-void InstallAction::StartInstallation(const std::string& url) {
+void InstallAction::StartInstallation(const std::string& url_to_fetch) {
+  LOG(INFO) << "Starting installation using URL=" << url_to_fetch;
   offset_ = 0;
   hash_.reset(crypto::SecureHash::Create(crypto::SecureHash::SHA256));
-  auto url_to_fetch = base::FilePath(url)
-                          .Append(image_props_.builder_path)
-                          .Append(slotting_)
-                          .Append(id_)
-                          .Append(kDefaultPackage)
-                          .Append(kDefaultArtifact)
-                          .value();
-  LOG(INFO) << "Starting installation using URL=" << url_to_fetch;
   http_fetcher_->SetOffset(0);
   http_fetcher_->UnsetLength();
   http_fetcher_->BeginTransfer(url_to_fetch);
diff --git a/cros/install_action.h b/cros/install_action.h
index 0529be8..3d113b2 100644
--- a/cros/install_action.h
+++ b/cros/install_action.h
@@ -96,9 +96,9 @@
   void set_delegate(InstallActionDelegate* delegate) { delegate_ = delegate; }
 
  private:
-  FRIEND_TEST(InstallActionTest, TransferFailureFetchesFromBackup);
+  FRIEND_TEST(InstallActionTestSuite, TransferFailureFetchesFromBackup);
 
-  void StartInstallation(const std::string& url);
+  void StartInstallation(const std::string& url_to_fetch);
 
   void TerminateInstallation();
 
diff --git a/cros/install_action_test.cc b/cros/install_action_test.cc
index 0df82bb..5e836a2 100644
--- a/cros/install_action_test.cc
+++ b/cros/install_action_test.cc
@@ -35,6 +35,7 @@
 constexpr char kDefaultOffset[] = "1024";
 constexpr char kDefaultSha[] =
     "5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef";
+constexpr char kArtifactsMetaSomeUri[] = "some/uri/path";
 
 constexpr char kManifestTemplate[] =
     R"({
@@ -61,6 +62,35 @@
   "used-by": "system",
   "version": "1.0.0-r1"
 })";
+constexpr char kManifestWithArtifactsMetaTemplate[] =
+    R"({
+  "critical-update": false,
+  "days-to-purge": 5,
+  "description": "A FOOBAR DLC",
+  "factory-install": false,
+  "fs-type": "squashfs",
+  "id": "sample-dlc",
+  "image-sha256-hash": )"
+    R"("5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
+  "image-type": "dlc",
+  "is-removable": true,
+  "loadpin-verity-digest": false,
+  "manifest-version": 1,
+  "mount-file-required": false,
+  "name": "Sample DLC",
+  "package": "package",
+  "pre-allocated-size": "4194304",
+  "preload-allowed": true,
+  "reserved": false,
+  "size": "1024",
+  "table-sha256-hash": )"
+    R"("44a4e688209bda4e06fd41aadc85a51de7d74a641275cb63b7caead96a9b03b7",
+  "used-by": "system",
+  "version": "1.0.0-r1",
+  "artifacts-meta": {
+    "uri": "%s"
+  }
+})";
 constexpr char kProperties[] = R"(
 CHROMEOS_RELEASE_APPID={DEB6CEFD-4EEE-462F-AC21-52DF1E17B52F}
 CHROMEOS_BOARD_APPID={DEB6CEFD-4EEE-462F-AC21-52DF1E17B52F}
@@ -155,7 +185,19 @@
   MockHttpFetcher* mock_http_fetcher_{nullptr};
 };
 
-TEST_F(InstallActionTest, ManifestReadFailure) {
+class InstallActionTestSuite : public InstallActionTest,
+                               public testing::WithParamInterface<std::string> {
+};
+
+INSTANTIATE_TEST_SUITE_P(
+    InstanceForManifests,
+    InstallActionTestSuite,
+    testing::Values(
+        base::StringPrintf(kManifestTemplate, kDefaultSha, kDefaultOffset),
+        base::StringPrintf(kManifestWithArtifactsMetaTemplate,
+                           kArtifactsMetaSomeUri)));
+
+TEST_P(InstallActionTestSuite, ManifestReadFailure) {
   processor_.set_delegate(&delegate_);
   processor_.EnqueueAction(std::move(install_action_));
 
@@ -175,12 +217,11 @@
   EXPECT_FALSE(loop_.PendingTasks());
 }
 
-TEST_F(InstallActionTest, PerformSuccessfulTest) {
+TEST_P(InstallActionTestSuite, PerformSuccessfulTest) {
   processor_.set_delegate(&delegate_);
   processor_.EnqueueAction(std::move(install_action_));
 
-  auto manifest =
-      base::StringPrintf(kManifestTemplate, kDefaultSha, kDefaultOffset);
+  auto manifest = GetParam();
   ASSERT_TRUE(test_utils::WriteFileString(
       tempdir_.GetPath()
           .Append("dlc/foobar-dlc/package/imageloader.json")
@@ -270,7 +311,7 @@
   EXPECT_FALSE(loop_.PendingTasks());
 }
 
-TEST_F(InstallActionTest, TransferFailureFetchesFromBackup) {
+TEST_P(InstallActionTestSuite, TransferFailureFetchesFromBackup) {
   ASSERT_EQ(install_action_.get()->backup_url_index_, 0);
 
   processor_.set_delegate(&delegate_);
@@ -278,8 +319,7 @@
 
   mock_http_fetcher_->FailTransfer(404);
 
-  auto manifest =
-      base::StringPrintf(kManifestTemplate, kDefaultSha, kDefaultOffset);
+  auto manifest = GetParam();
   ASSERT_TRUE(test_utils::WriteFileString(
       tempdir_.GetPath()
           .Append("dlc/foobar-dlc/package/imageloader.json")