Change fetch_cvd to only fetch single host package

The previous behavior was (needlessly) fetching the host package tools
for each instance.  Now, only a single host package is fetched no
matter the number of instances.  The config processing is also updated
to have `host_package_build` as a non-instance field.  Both `fetch_cvd`
and `cvd load` config processing were updated to still handle cases
where ONLY the host package tools (no instance files) are fetched.  The
`cvd load` case for that cannot be properly tested until local launches
are supported.

In multi-fetch and when instance subdirectories are specified the host
tools will now be put into its own subdirectory, adjacent to the
instance directories.  Additionally, the filepaths from the host tools
download and extraction are no longer appended to the
`fetcher_config.json` file.  That file is used by `assemble_cvd` for
tasks like image processing, and I was not able to find or trigger a
case where any reference to the host tools in that file was required.

Bug: 296623471
Test: atest -c --host --no-bazel-mode cvd_load_test
Test: fetch_cvd --target_directory=fetch_test
--host_package_build=aosp-main
Test: # only host package is downloaded to target directory
Test: fetch_cvd --target_directory=fetch_test --default_build=aosp-main
Test: # host package and instance files are downloaded to target directory
Test: cvd load host/cvd_test_configs/main_phone.json
Test: # host package files are in new .../artifacts/host_tools directory
and device successfully launches
Test: cvd load
host/cvd_test_configs/tm_phone-tm_watch-main_host_pkg.json
Test: # host package files are in new .../artifacts/host_tools directory
and devices successfully launch

Change-Id: Id27ccbe3632241945f5c73e708298ca807d80510
diff --git a/host/commands/cvd/fetch/fetch_cvd.cc b/host/commands/cvd/fetch/fetch_cvd.cc
index da63aaf..0ac024a 100644
--- a/host/commands/cvd/fetch/fetch_cvd.cc
+++ b/host/commands/cvd/fetch/fetch_cvd.cc
@@ -82,7 +82,6 @@
   std::vector<std::optional<BuildString>> boot_build;
   std::vector<std::optional<BuildString>> bootloader_build;
   std::vector<std::optional<BuildString>> otatools_build;
-  std::vector<std::optional<BuildString>> host_package_build;
   std::vector<bool> download_img_zip;
   std::vector<bool> download_target_files_zip;
   std::vector<std::string> boot_artifact;
@@ -91,6 +90,7 @@
 struct FetchFlags {
   std::string target_directory = kDefaultTargetDirectory;
   std::vector<std::string> target_subdirectory;
+  std::optional<BuildString> host_package_build;
   bool keep_downloaded_archives = kDefaultKeepDownloadedArchives;
   android::base::LogSeverity verbosity = android::base::INFO;
   bool helpxml = false;
@@ -121,12 +121,6 @@
   std::string system_target_files;
 };
 
-struct Target {
-  BuildStrings build_strings;
-  DownloadFlags download_flags;
-  TargetDirectories directories;
-};
-
 struct Builds {
   std::optional<Build> default_build;
   std::optional<Build> system;
@@ -134,7 +128,18 @@
   std::optional<Build> boot;
   std::optional<Build> bootloader;
   std::optional<Build> otatools;
-  Build host_package;
+};
+
+struct Target {
+  BuildStrings build_strings;
+  DownloadFlags download_flags;
+  TargetDirectories directories;
+  Builds builds;
+};
+
+struct HostToolsTarget {
+  std::optional<BuildString> build_string;
+  std::string host_tools_directory;
 };
 
 Flag GflagsCompatFlagSeconds(const std::string& name,
@@ -165,13 +170,15 @@
                                       fetch_flags.keep_downloaded_archives)
                          .Help("Keep downloaded zip/tar."));
   flags.emplace_back(VerbosityFlag(fetch_flags.verbosity));
-
   flags.emplace_back(
       GflagsCompatFlag("target_subdirectory", fetch_flags.target_subdirectory)
           .Help("Target subdirectory to fetch files into.  Specifically aimed "
                 "at organizing builds when there are multiple fetches. "
                 "**Note**: directory separator automatically prepended, only "
                 "give the subdirectory name."));
+  flags.emplace_back(
+      GflagsCompatFlag("host_package_build", fetch_flags.host_package_build)
+          .Help("source for the host cvd tools"));
 
   flags.emplace_back(GflagsCompatFlag("api_key", build_api_flags.api_key)
                          .Help("API key ofr the Android Build API"));
@@ -205,9 +212,6 @@
   flags.emplace_back(
       GflagsCompatFlag("otatools_build", vector_flags.otatools_build)
           .Help("source for the host ota tools"));
-  flags.emplace_back(
-      GflagsCompatFlag("host_package_build", vector_flags.host_package_build)
-          .Help("source for the host cvd tools"));
 
   flags.emplace_back(
       GflagsCompatFlag("boot_artifact", vector_flags.boot_artifact)
@@ -240,9 +244,8 @@
        {flags.default_build.size(), flags.system_build.size(),
         flags.kernel_build.size(), flags.boot_build.size(),
         flags.bootloader_build.size(), flags.otatools_build.size(),
-        flags.host_package_build.size(), flags.boot_artifact.size(),
-        flags.download_img_zip.size(), flags.download_target_files_zip.size(),
-        subdirectory_flag.size()}) {
+        flags.boot_artifact.size(), flags.download_img_zip.size(),
+        flags.download_target_files_zip.size(), subdirectory_flag.size()}) {
     if (flag_size == 0) {
       // a size zero flag vector means the flag was not given
       continue;
@@ -301,6 +304,10 @@
   return {fetch_flags};
 }
 
+bool ShouldAppendSubdirectory(const FetchFlags& flags) {
+  return flags.number_of_builds > 1 || !flags.target_subdirectory.empty();
+}
+
 template <typename T>
 T AccessOrDefault(const std::vector<T>& vector, const int i,
                   const T& default_value) {
@@ -325,8 +332,6 @@
           flags.bootloader_build, index, std::nullopt),
       .otatools_build = AccessOrDefault<std::optional<BuildString>>(
           flags.otatools_build, index, std::nullopt),
-      .host_package_build = AccessOrDefault<std::optional<BuildString>>(
-          flags.host_package_build, index, std::nullopt),
   };
   auto possible_boot_artifact =
       AccessOrDefault<std::string>(flags.boot_artifact, index, "");
@@ -362,11 +367,9 @@
                            .system_target_files = base_directory + "/system"};
 }
 
-std::vector<Target> GetFetchTargets(const FetchFlags& flags) {
-  const bool append_subdirectory =
-      flags.number_of_builds > 1 || !flags.target_subdirectory.empty();
+std::vector<Target> GetFetchTargets(const FetchFlags& flags,
+                                    const bool append_subdirectory) {
   std::vector<Target> result(flags.number_of_builds);
-
   for (int i = 0; i < result.size(); ++i) {
     result[i] = Target{
         .build_strings = GetBuildStrings(flags.vector_flags, i),
@@ -379,9 +382,23 @@
   return result;
 }
 
+HostToolsTarget GetHostToolsTarget(const FetchFlags& flags,
+                                   const bool append_subdirectory) {
+  std::string host_directory = flags.target_directory;
+  if (append_subdirectory) {
+    host_directory = host_directory + "/" + kHostToolsSubdirectory;
+  }
+  return HostToolsTarget{
+      .build_string = flags.host_package_build,
+      .host_tools_directory = host_directory,
+  };
+}
+
 Result<void> EnsureDirectoriesExist(const std::string& target_directory,
+                                    const std::string& host_tools_directory,
                                     const std::vector<Target>& targets) {
   CF_EXPECT(EnsureDirectoryExists(target_directory));
+  CF_EXPECT(EnsureDirectoryExists(host_tools_directory));
   for (const auto& target : targets) {
     CF_EXPECT(EnsureDirectoryExists(target.directories.root, kRwxAllMode));
     CF_EXPECT(EnsureDirectoryExists(target.directories.otatools, kRwxAllMode));
@@ -414,12 +431,14 @@
       new ServiceAccountOauthCredentialSource(std::move(*result)));
 }
 
-Result<std::vector<std::string>> ProcessHostPackage(
-    BuildApi& build_api, const Build& build, const std::string& target_dir,
-    const bool keep_archives) {
+Result<void> FetchHostPackage(BuildApi& build_api, const Build& build,
+                              const std::string& target_dir,
+                              const bool keep_archives) {
   std::string host_tools_filepath = CF_EXPECT(
       build_api.DownloadFile(build, target_dir, "cvd-host_package.tar.gz"));
-  return ExtractArchiveContents(host_tools_filepath, target_dir, keep_archives);
+  CF_EXPECT(
+      ExtractArchiveContents(host_tools_filepath, target_dir, keep_archives));
+  return {};
 }
 
 Result<std::unique_ptr<CredentialSource>> GetCredentialSource(
@@ -498,17 +517,9 @@
 
 Result<Builds> GetBuilds(BuildApi& build_api,
                          const BuildStrings& build_sources) {
-  auto default_build = CF_EXPECT(GetBuildHelper(
-      build_api, build_sources.default_build, kDefaultBuildTarget));
-  auto host_package_build = CF_EXPECT(GetBuildHelper(
-      build_api, build_sources.host_package_build, kDefaultBuildTarget));
-  CF_EXPECT(host_package_build.has_value() || default_build.has_value(),
-            "Either the host_package_build or default_build requires a value. "
-            "(previous default_build default was "
-            "aosp-master/aosp_cf_x86_64_phone-userdebug)");
-
   Builds result = Builds{
-      .default_build = default_build,
+      .default_build = CF_EXPECT(GetBuildHelper(
+          build_api, build_sources.default_build, kDefaultBuildTarget)),
       .system = CF_EXPECT(GetBuildHelper(build_api, build_sources.system_build,
                                          kDefaultBuildTarget)),
       .kernel = CF_EXPECT(
@@ -519,7 +530,6 @@
           build_api, build_sources.bootloader_build, "u-boot_crosvm_x86_64")),
       .otatools = CF_EXPECT(GetBuildHelper(
           build_api, build_sources.otatools_build, kDefaultBuildTarget)),
-      .host_package = host_package_build.value_or(*default_build),
   };
   if (!result.otatools) {
     if (result.system) {
@@ -531,6 +541,25 @@
   return {result};
 }
 
+Result<void> UpdateTargetsWithBuilds(BuildApi& build_api,
+                                     std::vector<Target>& targets) {
+  for (auto& target : targets) {
+    target.builds = CF_EXPECT(GetBuilds(build_api, target.build_strings));
+  }
+  return {};
+}
+
+Result<Build> GetHostBuild(BuildApi& build_api, HostToolsTarget& host_target,
+                           const std::optional<Build>& fallback_host_build) {
+  auto host_package_build = CF_EXPECT(
+      GetBuildHelper(build_api, host_target.build_string, kDefaultBuildTarget));
+  CF_EXPECT(host_package_build.has_value() || fallback_host_build.has_value(),
+            "Either the host_package_build or default_build requires a value. "
+            "(previous default_build default was "
+            "aosp-master/aosp_cf_x86_64_phone-userdebug)");
+  return host_package_build.value_or(*fallback_host_build);
+}
+
 Result<void> SaveConfig(FetcherConfig& config,
                         const std::string& target_directory) {
   // Due to constraints of the build system, artifacts intentionally cannot
@@ -552,13 +581,7 @@
                          const TargetDirectories& target_directories,
                          const DownloadFlags& flags,
                          const bool keep_downloaded_archives,
-                         const bool is_host_package_build,
                          FetcherConfig& config) {
-  auto process_pkg_ret = std::async(
-      std::launch::async, ProcessHostPackage, std::ref(build_api),
-      std::cref(builds.host_package), std::cref(target_directories.root),
-      std::cref(keep_downloaded_archives));
-
   if (builds.default_build) {
     const auto [default_build_id, default_build_target] =
         GetBuildIdAndTarget(*builds.default_build);
@@ -757,23 +780,11 @@
         FileSource::DEFAULT_BUILD, otatools_build_id, otatools_build_target,
         ota_tools_files, target_directories.root));
   }
-
-  // Wait for ProcessHostPackage to return.
-  std::vector<std::string> host_package_files =
-      CF_EXPECT(process_pkg_ret.get());
-  const auto [host_id, host_target] = GetBuildIdAndTarget(builds.host_package);
-  FileSource host_filesource = FileSource::DEFAULT_BUILD;
-  if (is_host_package_build) {
-    host_filesource = FileSource::HOST_PACKAGE_BUILD;
-  }
-  CF_EXPECT(config.AddFilesToConfig(host_filesource, host_id, host_target,
-                                    host_package_files,
-                                    target_directories.root));
   return {};
 }
 
-Result<void> Fetch(const FetchFlags& flags,
-                   const std::vector<Target>& targets) {
+Result<void> Fetch(const FetchFlags& flags, HostToolsTarget& host_target,
+                   std::vector<Target>& targets) {
 #ifdef __BIONIC__
   // TODO(schuffelen): Find a better way to deal with tzdata
   setenv("ANDROID_TZDATA_ROOT", "/", /* overwrite */ 0);
@@ -783,22 +794,32 @@
   curl_global_init(CURL_GLOBAL_DEFAULT);
   {
     BuildApi build_api = CF_EXPECT(GetBuildApi(flags.build_api_flags));
+    CF_EXPECT(UpdateTargetsWithBuilds(build_api, targets));
+    std::optional<Build> fallback_host_build = std::nullopt;
+    if (!targets.empty()) {
+      fallback_host_build = targets[0].builds.default_build;
+    }
+    const auto host_target_build =
+        CF_EXPECT(GetHostBuild(build_api, host_target, fallback_host_build));
 
+    auto host_package_future =
+        std::async(std::launch::async, FetchHostPackage, std::ref(build_api),
+                   std::cref(host_target_build),
+                   std::cref(host_target.host_tools_directory),
+                   std::cref(flags.keep_downloaded_archives));
     for (const auto& target : targets) {
       LOG(INFO) << "Starting fetch to \"" << target.directories.root << "\"";
       FetcherConfig config;
-      const Builds builds =
-          CF_EXPECT(GetBuilds(build_api, target.build_strings));
-      const bool is_host_package_build =
-          target.build_strings.host_package_build.has_value();
-      CF_EXPECT(FetchTarget(
-          build_api, builds, target.directories, target.download_flags,
-          flags.keep_downloaded_archives, is_host_package_build, config));
+      CF_EXPECT(FetchTarget(build_api, target.builds, target.directories,
+                            target.download_flags,
+                            flags.keep_downloaded_archives, config));
       CF_EXPECT(SaveConfig(config, target.directories.root));
       LOG(INFO) << "Completed fetch to \"" << target.directories.root << "\"";
     }
+    CF_EXPECT(host_package_future.get());
   }
   curl_global_cleanup();
+
   LOG(INFO) << "Completed all fetches";
   return {};
 }
@@ -808,13 +829,16 @@
 Result<void> FetchCvdMain(int argc, char** argv) {
   android::base::InitLogging(argv, android::base::StderrLogger);
   const FetchFlags flags = CF_EXPECT(GetFlagValues(argc, argv));
-  const std::vector<Target> targets = GetFetchTargets(flags);
-  CF_EXPECT(EnsureDirectoriesExist(flags.target_directory, targets));
+  const bool append_subdirectory = ShouldAppendSubdirectory(flags);
+  std::vector<Target> targets = GetFetchTargets(flags, append_subdirectory);
+  HostToolsTarget host_target = GetHostToolsTarget(flags, append_subdirectory);
+  CF_EXPECT(EnsureDirectoriesExist(flags.target_directory,
+                                   host_target.host_tools_directory, targets));
   android::base::SetLogger(
       LogToStderrAndFiles({flags.target_directory + "/fetch.log"}));
   android::base::SetMinimumLogSeverity(flags.verbosity);
 
-  auto result = Fetch(flags, targets);
+  auto result = Fetch(flags, host_target, targets);
   if (!result.ok()) {
     LOG(ERROR) << result.error().FormatForEnv();
   }
diff --git a/host/commands/cvd/fetch/fetch_cvd.h b/host/commands/cvd/fetch/fetch_cvd.h
index 9ff6c91..b622b62 100644
--- a/host/commands/cvd/fetch/fetch_cvd.h
+++ b/host/commands/cvd/fetch/fetch_cvd.h
@@ -35,6 +35,7 @@
 inline constexpr bool kDefaultDownloadImgZip = true;
 inline constexpr bool kDefaultDownloadTargetFilesZip = false;
 inline constexpr char kDefaultTargetDirectory[] = "";
+inline constexpr char kHostToolsSubdirectory[] = "host_tools";
 inline constexpr bool kDefaultKeepDownloadedArchives = false;
 
 Result<void> FetchCvdMain(int argc, char** argv);
diff --git a/host/commands/cvd/parser/cf_flags_validator.cpp b/host/commands/cvd/parser/cf_flags_validator.cpp
index 24120d5..0556cbb 100644
--- a/host/commands/cvd/parser/cf_flags_validator.cpp
+++ b/host/commands/cvd/parser/cf_flags_validator.cpp
@@ -39,7 +39,9 @@
 };
 
 static std::map<std::string, Json::ValueType> kCommonKeyMap = {
-    {"group_name", Json::ValueType::stringValue}};
+    {"group_name", Json::ValueType::stringValue},
+    {"host_package", Json::ValueType::stringValue},
+};
 
 static std::map<std::string, Json::ValueType> kFetchKeyMap = {
     {"api_key", Json::ValueType::stringValue},
@@ -124,7 +126,6 @@
     {"download_target_zip_files", Json::ValueType::booleanValue},
     {"blank_data_image_mb", Json::ValueType::uintValue},
     {"otatools", Json::ValueType::stringValue},
-    {"host_package", Json::ValueType::stringValue},
 };
 
 static std::map<std::string, Json::ValueType> kSuperKeyMap = {
diff --git a/host/commands/cvd/parser/fetch_cvd_parser.cpp b/host/commands/cvd/parser/fetch_cvd_parser.cpp
index 5bd359f..af06858 100644
--- a/host/commands/cvd/parser/fetch_cvd_parser.cpp
+++ b/host/commands/cvd/parser/fetch_cvd_parser.cpp
@@ -45,8 +45,6 @@
   CF_EXPECT(InitConfig(instance, kDefaultBuildString,
                        {"boot", "bootloader", "build"}));
   CF_EXPECT(InitConfig(instance, kDefaultBuildString, {"disk", "otatools"}));
-  CF_EXPECT(
-      InitConfig(instance, kDefaultBuildString, {"disk", "host_package"}));
   CF_EXPECT(InitConfig(instance, kDefaultDownloadImgZip,
                        {"disk", "download_img_zip"}));
   CF_EXPECT(InitConfig(instance, kDefaultDownloadTargetFilesZip,
@@ -66,6 +64,7 @@
                        {"fetch", "keep_downloaded_archives"}));
   CF_EXPECT(
       InitConfig(root, kAndroidBuildServiceUrl, {"fetch", "api_base_url"}));
+  CF_EXPECT(InitConfig(root, kDefaultBuildString, {"common", "host_package"}));
   for (auto& instance : root["instances"]) {
     CF_EXPECT(InitFetchInstanceConfigs(instance));
   }
@@ -76,8 +75,8 @@
   for (const auto& value :
        {instance["disk"]["default_build"], instance["disk"]["super"]["system"],
         instance["boot"]["kernel"]["build"], instance["boot"]["build"],
-        instance["boot"]["bootloader"]["build"], instance["disk"]["otatools"],
-        instance["disk"]["host_package"]}) {
+        instance["boot"]["bootloader"]["build"],
+        instance["disk"]["otatools"]}) {
     // expects non-prefixed build strings already converted to empty strings
     if (!value.asString().empty()) {
       return true;
@@ -114,8 +113,6 @@
       CF_EXPECT(GetFetchBuildString(result["boot"]["bootloader"]["build"]));
   result["disk"]["otatools"] =
       CF_EXPECT(GetFetchBuildString(result["disk"]["otatools"]));
-  result["disk"]["host_package"] =
-      CF_EXPECT(GetFetchBuildString(result["disk"]["host_package"]));
   return result;
 }
 
@@ -137,10 +134,13 @@
     }
   }
 
+  const std::string host_package_build =
+      CF_EXPECT(GetFetchBuildString(root["common"]["host_package"]));
   std::vector<std::string> result;
-  if (fetch_subdirectories.empty()) {
+  if (fetch_subdirectories.empty() && host_package_build.empty()) {
     return result;
   }
+
   result.emplace_back(GenerateGflag("target_directory", {target_directory}));
   result.emplace_back(GenerateGflag(
       "api_key",
@@ -162,6 +162,8 @@
   result.emplace_back(GenerateGflag(
       "api_base_url",
       {CF_EXPECT(GetValue<std::string>(root, {"fetch", "api_base_url"}))}));
+  result.emplace_back(
+      GenerateGflag("host_package_build", {host_package_build}));
 
   result.emplace_back(
       GenerateGflag("target_subdirectory", fetch_subdirectories));
@@ -178,8 +180,6 @@
   result.emplace_back(CF_EXPECT(
       GenerateGflag(fetch_instances, "otatools_build", {"disk", "otatools"})));
   result.emplace_back(CF_EXPECT(GenerateGflag(
-      fetch_instances, "host_package_build", {"disk", "host_package"})));
-  result.emplace_back(CF_EXPECT(GenerateGflag(
       fetch_instances, "download_img_zip", {"disk", "download_img_zip"})));
   result.emplace_back(
       CF_EXPECT(GenerateGflag(fetch_instances, "download_target_files_zip",
@@ -193,7 +193,8 @@
     Json::Value& root, const std::string& target_directory,
     const std::vector<std::string>& target_subdirectories) {
   CF_EXPECT(InitFetchCvdConfigs(root));
-  return GenerateFetchFlags(root, target_directory, target_subdirectories);
+  return CF_EXPECT(
+      GenerateFetchFlags(root, target_directory, target_subdirectories));
 }
 
 }  // namespace cuttlefish
diff --git a/host/commands/cvd/parser/load_configs_parser.cpp b/host/commands/cvd/parser/load_configs_parser.cpp
index 09138d9..d687316 100644
--- a/host/commands/cvd/parser/load_configs_parser.cpp
+++ b/host/commands/cvd/parser/load_configs_parser.cpp
@@ -34,6 +34,7 @@
 #include "common/libs/utils/flag_parser.h"
 #include "common/libs/utils/json.h"
 #include "common/libs/utils/result.h"
+#include "host/commands/cvd/fetch/fetch_cvd.h"
 #include "host/commands/cvd/parser/cf_configs_common.h"
 #include "host/commands/cvd/parser/cf_flags_validator.h"
 #include "host/commands/cvd/parser/fetch_cvd_parser.h"
@@ -261,8 +262,9 @@
     system_image_directories.emplace_back(result.target_directory + "/" +
                                           target_subdirectory);
   }
+
   result.host_package_directory =
-      result.target_directory + "/" + result.target_subdirectories[0];
+      result.target_directory + "/" + kHostToolsSubdirectory;
   result.system_image_directory_flag =
       "--system_image_dir=" +
       android::base::Join(system_image_directories, ',');
diff --git a/host/commands/cvd/unittests/parser/fetch_cvd_parser_tests.cpp b/host/commands/cvd/unittests/parser/fetch_cvd_parser_tests.cpp
index df8ed58..2201007 100644
--- a/host/commands/cvd/unittests/parser/fetch_cvd_parser_tests.cpp
+++ b/host/commands/cvd/unittests/parser/fetch_cvd_parser_tests.cpp
@@ -46,7 +46,8 @@
     Json::Value& root, const std::string& target_directory,
     const std::vector<std::string>& target_subdirectories) {
   CF_EXPECT(ValidateCfConfigs(root), "Loaded Json validation failed");
-  return ParseFetchCvdConfigs(root, target_directory, target_subdirectories);
+  return CF_EXPECT(
+      ParseFetchCvdConfigs(root, target_directory, target_subdirectories));
 }
 
 }  // namespace
@@ -54,6 +55,9 @@
 TEST(FetchCvdParserTests, SingleFetch) {
   const char* raw_json = R""""(
 {
+  "common" : {
+    "host_package" : "@ab/git_master/cf_x86_64_phone-userdebug"
+  },
   "instances" : [
     {
       "@import" : "phone",
@@ -65,8 +69,7 @@
       "disk" : {
         "default_build" : "@ab/git_master/cf_x86_64_phone-userdebug",
         "download_img_zip" : true,
-        "otatools" : "@ab/git_master/cf_x86_64_phone-userdebug",
-        "host_package" : "@ab/git_master/cf_x86_64_phone-userdebug"
+        "otatools" : "@ab/git_master/cf_x86_64_phone-userdebug"
       },
       "boot" : {
         "build" : "@ab/git_master/cf_x86_64_phone-userdebug",
@@ -119,9 +122,8 @@
     {
       "@import" : "phone",
       "disk" : {
-        "default_build" : "git_master/cf_x86_64_phone-userdebug",
-        "otatools" : "git_master/cf_x86_64_phone-userdebug",
-        "host_package" : "git_master/cf_x86_64_phone-userdebug"
+        "default_build" : "git_master/cf_x86_64_phone-userdebug"
+        "otatools" : "git_master/cf_x86_64_phone-userdebug"
       },
       "boot" : {
         "build" : "git_master/cf_x86_64_phone-userdebug",
@@ -145,6 +147,9 @@
 TEST(FetchCvdParserTests, MultiFetch) {
   const char* raw_json = R""""(
 {
+  "common" : {
+    "host_package" : "@ab/git_master/cf_x86_64_phone-userdebug"
+  },
   "instances" : [
     {
       "@import" : "phone",
@@ -156,8 +161,7 @@
       "disk" : {
         "default_build" : "@ab/git_master/cf_x86_64_phone-userdebug",
         "download_img_zip" : true,
-        "otatools" : "@ab/git_master/cf_x86_64_phone-userdebug",
-        "host_package" : "@ab/git_master/cf_x86_64_phone-userdebug"
+        "otatools" : "@ab/git_master/cf_x86_64_phone-userdebug"
       },
       "boot" : {
         "build" : "@ab/git_master/cf_x86_64_phone-userdebug",
@@ -209,7 +213,7 @@
       Contains("--otatools_build=git_master/cf_x86_64_phone-userdebug,"));
   EXPECT_THAT(
       flags,
-      Contains("--host_package_build=git_master/cf_x86_64_phone-userdebug,"));
+      Contains("--host_package_build=git_master/cf_x86_64_phone-userdebug"));
   EXPECT_THAT(flags,
               Contains("--boot_build=git_master/cf_x86_64_phone-userdebug,"));
   EXPECT_THAT(flags,
diff --git a/host/cvd_test_configs/main_phone.json b/host/cvd_test_configs/main_phone.json
index 9451dd2..37ca545 100644
--- a/host/cvd_test_configs/main_phone.json
+++ b/host/cvd_test_configs/main_phone.json
@@ -1,4 +1,7 @@
 {
+  "common": {
+    "host_package": "@ab/aosp-main/aosp_cf_x86_64_phone-trunk_staging-userdebug"
+  },
   "instances": [
     {
       "@import": "phone",
diff --git a/host/cvd_test_configs/tm_phone-tm_watch-main_host_pkg.json b/host/cvd_test_configs/tm_phone-tm_watch-main_host_pkg.json
index 34304db..08ba316 100644
--- a/host/cvd_test_configs/tm_phone-tm_watch-main_host_pkg.json
+++ b/host/cvd_test_configs/tm_phone-tm_watch-main_host_pkg.json
@@ -1,4 +1,7 @@
 {
+  "common": {
+    "host_package": "@ab/aosp-main/aosp_cf_x86_64_phone-trunk_staging-userdebug"
+  },
   "instances": [
     {
       "@import": "phone",
@@ -8,8 +11,7 @@
         "cpus": 4
       },
       "disk": {
-        "default_build": "@ab/git_tm-qpr-dev/cf_x86_64_phone-userdebug",
-        "host_package": "@ab/aosp-main/aosp_cf_x86_64_phone-trunk_staging-userdebug"
+        "default_build": "@ab/git_tm-qpr-dev/cf_x86_64_phone-userdebug"
       }
     },
     {
@@ -20,8 +22,7 @@
         "cpus": 4
       },
       "disk": {
-        "default_build": "@ab/git_tm-wear-dev/cf_gwear_x86-userdebug",
-        "host_package": "@ab/aosp-main/aosp_cf_x86_64_phone-trunk_staging-userdebug"
+        "default_build": "@ab/git_tm-wear-dev/cf_gwear_x86-userdebug"
       }
     }
   ]