Rewrite misc_info.txt to include only system-known partitions.

This handles the GSI case where product and partitions invented later
are all collapsed into the system partition.

Test: Combine with a later system image
Bug: 150097754
Change-Id: I871342f6df0dba0cf5c35931e8e9c5867a770cf0
diff --git a/common/libs/utils/archive.cpp b/common/libs/utils/archive.cpp
index c1c496d..36e6576 100644
--- a/common/libs/utils/archive.cpp
+++ b/common/libs/utils/archive.cpp
@@ -73,4 +73,21 @@
   return bsdtar_ret == 0;
 }
 
+std::string Archive::ExtractToMemory(const std::string& path) {
+  cvd::Command bsdtar_cmd("/usr/bin/bsdtar");
+  bsdtar_cmd.AddParameter("-xf");
+  bsdtar_cmd.AddParameter(file);
+  bsdtar_cmd.AddParameter("-O");
+  bsdtar_cmd.AddParameter(path);
+  std::string stdout, stderr;
+  auto ret = RunWithManagedStdio(std::move(bsdtar_cmd), nullptr, &stdout,
+                                 nullptr);
+  if (ret != 0) {
+    LOG(ERROR) << "Could not extract \"" << path << "\" from \"" << file
+               << "\" to memory.";
+    return "";
+  }
+  return stdout;
+}
+
 } // namespace cvd
diff --git a/common/libs/utils/archive.h b/common/libs/utils/archive.h
index fae9288..7563ca0 100644
--- a/common/libs/utils/archive.h
+++ b/common/libs/utils/archive.h
@@ -31,6 +31,7 @@
   bool ExtractAll(const std::string& target_directory = ".");
   bool ExtractFiles(const std::vector<std::string>& files,
                     const std::string& target_directory = ".");
+  std::string ExtractToMemory(const std::string& path);
 };
 
 } // namespace cvd
diff --git a/host/commands/assemble_cvd/Android.bp b/host/commands/assemble_cvd/Android.bp
index 807f18c..ef77942 100644
--- a/host/commands/assemble_cvd/Android.bp
+++ b/host/commands/assemble_cvd/Android.bp
@@ -36,6 +36,7 @@
         "data_image.cc",
         "flags.cc",
         "image_aggregator.cc",
+        "misc_info.cc",
         "super_image_mixer.cc",
     ],
     header_libs: [
diff --git a/host/commands/assemble_cvd/misc_info.cc b/host/commands/assemble_cvd/misc_info.cc
new file mode 100644
index 0000000..c708e87
--- /dev/null
+++ b/host/commands/assemble_cvd/misc_info.cc
@@ -0,0 +1,80 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "misc_info.h"
+
+#include <algorithm>
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+MiscInfo ParseMiscInfo(const std::string& misc_info_contents) {
+  auto lines = android::base::Split(misc_info_contents, "\n");
+  MiscInfo misc_info;
+  for (auto& line : lines) {
+    line = android::base::Trim(line);
+    if (line.size() == 0) {
+      continue;
+    }
+    auto eq_pos = line.find('=');
+    if (eq_pos == std::string::npos) {
+      LOG(WARNING) << "Line in unknown format: \"" << line << "\"";
+      continue;
+    }
+    // Not using android::base::Split here to only capture the first =
+    auto key = android::base::Trim(line.substr(0, eq_pos));
+    auto value = android::base::Trim(line.substr(eq_pos + 1));
+    if (misc_info.find(key) != misc_info.end()) {
+      LOG(ERROR) << "Duplicate key: \"" << key << "\". First value: \""
+                 << misc_info[key] << "\", Second value: \"" << value << "\"";
+      return {};
+    }
+    misc_info[key] = value;
+  }
+  return misc_info;
+}
+
+std::string WriteMiscInfo(const MiscInfo& misc_info) {
+  std::stringstream out;
+  for (const auto& entry : misc_info) {
+    out << entry.first << "=" << entry.second << "\n";
+  }
+  return out.str();
+}
+
+static const std::string kDynamicPartitions = "dynamic_partition_list";
+
+std::vector<std::string> SuperPartitionComponents(const MiscInfo& info) {
+  auto value_it = info.find(kDynamicPartitions);
+  if (value_it == info.end()) {
+    return {};
+  }
+  auto components = android::base::Split(value_it->second, " ");
+  for (auto& component : components) {
+    component = android::base::Trim(component);
+  }
+  components.erase(std::remove(components.begin(), components.end(), ""),
+                   components.end());
+  return components;
+}
+
+static const std::string kGoogleDynamicPartitions =
+    "super_google_dynamic_partitions_partition_list";
+
+void SetSuperPartitionComponents(const std::vector<std::string>& components,
+                                 MiscInfo* misc_info) {
+  (*misc_info)[kDynamicPartitions] = android::base::Join(components, " ");
+  (*misc_info)[kGoogleDynamicPartitions] = android::base::Join(components, " ");
+}
diff --git a/host/commands/assemble_cvd/misc_info.h b/host/commands/assemble_cvd/misc_info.h
new file mode 100644
index 0000000..7c32e3b
--- /dev/null
+++ b/host/commands/assemble_cvd/misc_info.h
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#pragma once
+
+#include <map>
+#include <string>
+
+using MiscInfo = std::map<std::string, std::string>;
+
+MiscInfo ParseMiscInfo(const std::string& file_contents);
+std::string WriteMiscInfo(const MiscInfo& info);
+
+std::vector<std::string> SuperPartitionComponents(const MiscInfo&);
+void SetSuperPartitionComponents(const std::vector<std::string>& components,
+                                 MiscInfo* misc_info);
diff --git a/host/commands/assemble_cvd/super_image_mixer.cc b/host/commands/assemble_cvd/super_image_mixer.cc
index 78d2750..ce75a67 100644
--- a/host/commands/assemble_cvd/super_image_mixer.cc
+++ b/host/commands/assemble_cvd/super_image_mixer.cc
@@ -25,9 +25,11 @@
 #include <android-base/strings.h>
 #include <glog/logging.h>
 
+#include "common/libs/fs/shared_buf.h"
 #include "common/libs/utils/archive.h"
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/subprocess.h"
+#include "host/commands/assemble_cvd/misc_info.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "host/libs/config/fetcher_config.h"
 
@@ -56,6 +58,7 @@
 const std::set<std::string> kDefaultTargetImages = {
   "IMAGES/boot.img",
   "IMAGES/cache.img",
+  "IMAGES/odm.img",
   "IMAGES/recovery.img",
   "IMAGES/userdata.img",
   "IMAGES/vendor.img",
@@ -81,14 +84,53 @@
     LOG(ERROR) << "Could not create directory " << output_path;
     return false;
   }
+  std::string output_meta = output_path + "/META";
+  if (mkdir(output_meta.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) < 0) {
+    LOG(ERROR) << "Could not create directory " << output_meta;
+    return false;
+  }
 
   if (std::find(default_target_contents.begin(), default_target_contents.end(), kMiscInfoPath)
       == default_target_contents.end()) {
     LOG(ERROR) << "Default target files zip does not have " << kMiscInfoPath;
     return false;
   }
-  if (!default_target_archive.ExtractFiles({kMiscInfoPath}, output_path)) {
-    LOG(ERROR) << "Failed to write misc info to output directory";
+  if (std::find(system_target_contents.begin(), system_target_contents.end(), kMiscInfoPath)
+      == system_target_contents.end()) {
+    LOG(ERROR) << "System target files zip does not have " << kMiscInfoPath;
+    return false;
+  }
+  const auto default_misc =
+      ParseMiscInfo(default_target_archive.ExtractToMemory(kMiscInfoPath));
+  if (default_misc.size() == 0) {
+    LOG(ERROR) << "Could not read the default misc_info.txt file.";
+    return false;
+  }
+  const auto system_misc =
+      ParseMiscInfo(system_target_archive.ExtractToMemory(kMiscInfoPath));
+  if (system_misc.size() == 0) {
+    LOG(ERROR) << "Could not read the system misc_info.txt file.";
+    return false;
+  }
+  auto output_misc = default_misc;
+  auto system_super_partitions = SuperPartitionComponents(system_misc);
+  if (std::find(system_super_partitions.begin(), system_super_partitions.end(),
+                "odm") == system_super_partitions.end()) {
+    // odm is not one of the partitions skipped by the system check
+    system_super_partitions.push_back("odm");
+  }
+  SetSuperPartitionComponents(system_super_partitions, &output_misc);
+  auto misc_output_path = output_path + "/" + kMiscInfoPath;
+  cvd::SharedFD misc_output_file =
+      cvd::SharedFD::Creat(misc_output_path.c_str(), 0644);
+  if (!misc_output_file->IsOpen()) {
+    LOG(ERROR) << "Failed to open output misc file: "
+               << misc_output_file->StrError();
+    return false;
+  }
+  if (cvd::WriteAll(misc_output_file, WriteMiscInfo(output_misc)) < 0) {
+    LOG(ERROR) << "Failed to write output misc file contents: "
+               << misc_output_file->StrError();
     return false;
   }