Pass the cache-info fd from odrefresh to dex2oat.

This allows dex2oat in CompOS to read the cache-info file. Otherwise,
CompOS will generate invalid artifacts that put the device into JIT
Zygote.

Bug: 286422732
Test: atest art_standalone_odrefresh_tests
Test: (on udc-dev) -
  1. adb shell device_config set_sync_disabled_for_tests persistent
  2. adb shell device_config put runtime_native_boot enable_uffd_gc true
  3. atest odsign_e2e_tests_full:CompOsSigningHostTest
Change-Id: I4c6c5fb13a93b58ce53f46cd8d6d9c1e05ecd720
diff --git a/dex2oat/dex2oat_options.cc b/dex2oat/dex2oat_options.cc
index cf60008..4da2198 100644
--- a/dex2oat/dex2oat_options.cc
+++ b/dex2oat/dex2oat_options.cc
@@ -462,7 +462,10 @@
       .Define("--force-palette-compilation-hooks")
           .WithHelp("Force PaletteNotify{Start,End}Dex2oatCompilation calls.")
           .IntoKey(M::ForcePaletteCompilationHooks)
-      .Ignore({"--comments=_"});
+      .Ignore({
+        "--comments=_",
+        "--cache-info-fd=_",  // Handled in mark_compact.cc.
+      });
   // clang-format on
 
   AddCompilerOptionsArgumentParserOptions<Dex2oatArgumentMap>(*parser_builder);
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index 608c321..7bc1a04 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -494,6 +494,19 @@
   return {};
 }
 
+Result<void> AddCacheInfoFd(/*inout*/ std::vector<std::string>& args,
+                            /*inout*/ std::vector<std::unique_ptr<File>>& readonly_files_raii,
+                            const std::string& cache_info_filename) {
+  std::unique_ptr<File> cache_info_file(OS::OpenFileForReading(cache_info_filename.c_str()));
+  if (cache_info_file == nullptr) {
+    return ErrnoErrorf("Failed to open a cache info file '{}'", cache_info_file);
+  }
+
+  args.emplace_back("--cache-info-fd=" + std::to_string(cache_info_file->Fd()));
+  readonly_files_raii.push_back(std::move(cache_info_file));
+  return {};
+}
+
 std::string GetBootImageComponentBasename(const std::string& jar_path, bool is_first_jar) {
   if (is_first_jar) {
     return kFirstBootImageBasename;
@@ -1653,6 +1666,12 @@
     return CompilationResult::Error(OdrMetrics::Status::kUnknown, result.error().message());
   }
 
+  // dex2oat reads some system properties from cache-info.xml generated by odrefresh.
+  result = AddCacheInfoFd(args, readonly_files_raii, cache_info_filename_);
+  if (!result.ok()) {
+    return CompilationResult::Error(OdrMetrics::Status::kUnknown, result.error().message());
+  }
+
   for (const std::string& dex_file : dex_files) {
     std::string actual_path = RewriteParentDirectoryIfNeeded(dex_file);
     args.emplace_back("--dex-file=" + dex_file);
diff --git a/odrefresh/odrefresh_test.cc b/odrefresh/odrefresh_test.cc
index f4a3c4c..b1d3023 100644
--- a/odrefresh/odrefresh_test.cc
+++ b/odrefresh/odrefresh_test.cc
@@ -233,8 +233,9 @@
     mock_exec_utils_ = mock_exec_utils.get();
 
     metrics_ = std::make_unique<OdrMetrics>(dalvik_cache_dir_);
+    cache_info_xml_ = dalvik_cache_dir_ + "/cache-info.xml";
     odrefresh_ = std::make_unique<OnDeviceRefresh>(config_,
-                                                   dalvik_cache_dir_ + "/cache-info.xml",
+                                                   cache_info_xml_,
                                                    std::move(mock_exec_utils),
                                                    /*check_compilation_space=*/[] { return true; });
   }
@@ -272,6 +273,7 @@
   std::string services_jar_profile_;
   std::string dirty_image_objects_file_;
   std::string preloaded_classes_file_;
+  std::string cache_info_xml_;
 };
 
 TEST_F(OdRefreshTest, PrimaryBootImage) {
@@ -290,7 +292,8 @@
                                     ElementsAre(FdOf(core_oj_jar_), FdOf(framework_jar_)))),
                   Contains(Flag("--oat-location=", dalvik_cache_dir_ + "/x86_64/boot.oat")),
                   Contains(Flag("--base=", _)),
-                  Not(Contains(Flag("--boot-image=", _))))))
+                  Not(Contains(Flag("--boot-image=", _))),
+                  Contains(Flag("--cache-info-fd=", FdOf(cache_info_xml_))))))
       .WillOnce(Return(0));
 
   // Ignore the invocation for the mainline extension.
@@ -329,7 +332,8 @@
                                         FdOf(framework_wifi_jar_)))),
           Contains(Flag("--oat-location=", dalvik_cache_dir_ + "/x86_64/boot-conscrypt.oat")),
           Not(Contains(Flag("--base=", _))),
-          Contains(Flag("--boot-image=", _)))))
+          Contains(Flag("--boot-image=", _)),
+          Contains(Flag("--cache-info-fd=", FdOf(cache_info_xml_))))))
       .WillOnce(Return(0));
 
   EXPECT_EQ(odrefresh_->Compile(
@@ -428,14 +432,16 @@
   EXPECT_CALL(*mock_exec_utils_,
               DoExecAndReturnCode(AllOf(Contains(Flag("--dex-file=", location_provider_jar_)),
                                         Contains("--class-loader-context=PCL[]"),
-                                        Not(Contains(Flag("--class-loader-context-fds=", _))))))
+                                        Not(Contains(Flag("--class-loader-context-fds=", _))),
+                                        Contains(Flag("--cache-info-fd=", FdOf(cache_info_xml_))))))
       .WillOnce(Return(0));
   EXPECT_CALL(
       *mock_exec_utils_,
       DoExecAndReturnCode(AllOf(
           Contains(Flag("--dex-file=", services_jar_)),
           Contains(Flag("--class-loader-context=", ART_FORMAT("PCL[{}]", location_provider_jar_))),
-          Contains(Flag("--class-loader-context-fds=", FdOf(location_provider_jar_))))))
+          Contains(Flag("--class-loader-context-fds=", FdOf(location_provider_jar_))),
+          Contains(Flag("--cache-info-fd=", FdOf(cache_info_xml_))))))
       .WillOnce(Return(0));
   EXPECT_CALL(
       *mock_exec_utils_,
@@ -444,7 +450,8 @@
           Contains(Flag("--class-loader-context=",
                         ART_FORMAT("PCL[];PCL[{}:{}]", location_provider_jar_, services_jar_))),
           Contains(ListFlag("--class-loader-context-fds=",
-                            ElementsAre(FdOf(location_provider_jar_), FdOf(services_jar_)))))))
+                            ElementsAre(FdOf(location_provider_jar_), FdOf(services_jar_)))),
+          Contains(Flag("--cache-info-fd=", FdOf(cache_info_xml_))))))
       .WillOnce(Return(0));
   EXPECT_CALL(
       *mock_exec_utils_,
@@ -453,7 +460,8 @@
           Contains(Flag("--class-loader-context=",
                         ART_FORMAT("PCL[];PCL[{}:{}]", location_provider_jar_, services_jar_))),
           Contains(ListFlag("--class-loader-context-fds=",
-                            ElementsAre(FdOf(location_provider_jar_), FdOf(services_jar_)))))))
+                            ElementsAre(FdOf(location_provider_jar_), FdOf(services_jar_)))),
+          Contains(Flag("--cache-info-fd=", FdOf(cache_info_xml_))))))
       .WillOnce(Return(0));
 
   EXPECT_EQ(
diff --git a/runtime/gc/collector/mark_compact.cc b/runtime/gc/collector/mark_compact.cc
index fd641f8..cd5a45b 100644
--- a/runtime/gc/collector/mark_compact.cc
+++ b/runtime/gc/collector/mark_compact.cc
@@ -29,10 +29,15 @@
 
 #include <fstream>
 #include <numeric>
+#include <string>
+#include <string_view>
+#include <vector>
 
 #include "android-base/file.h"
 #include "android-base/parsebool.h"
+#include "android-base/parseint.h"
 #include "android-base/properties.h"
+#include "android-base/strings.h"
 #include "base/file_utils.h"
 #include "base/memfd.h"
 #include "base/quasi_atomic.h"
@@ -164,18 +169,64 @@
 }
 
 #ifdef ART_TARGET_ANDROID
+static int GetOverrideCacheInfoFd() {
+  std::string args_str;
+  if (!android::base::ReadFileToString("/proc/self/cmdline", &args_str)) {
+    LOG(WARNING) << "Failed to load /proc/self/cmdline";
+    return -1;
+  }
+  std::vector<std::string_view> args;
+  Split(std::string_view(args_str), /*separator=*/'\0', &args);
+  for (std::string_view arg : args) {
+    if (android::base::ConsumePrefix(&arg, "--cache-info-fd=")) {  // This is a dex2oat flag.
+      int fd;
+      if (!android::base::ParseInt(std::string(arg), &fd)) {
+        LOG(ERROR) << "Failed to parse --cache-info-fd (value: '" << arg << "')";
+        return -1;
+      }
+      return fd;
+    }
+  }
+  return -1;
+}
+
 static bool GetCachedBoolProperty(const std::string& key, bool default_value) {
-  std::string path = GetApexDataDalvikCacheDirectory(InstructionSet::kNone) + "/cache-info.xml";
-  std::optional<com::android::art::CacheInfo> cache_info = com::android::art::read(path.c_str());
+  // For simplicity, we don't handle multiple calls because otherwise we would have to reset the fd.
+  static bool called = false;
+  CHECK(!called) << "GetCachedBoolProperty can be called only once";
+  called = true;
+
+  std::string cache_info_contents;
+  int fd = GetOverrideCacheInfoFd();
+  if (fd >= 0) {
+    if (!android::base::ReadFdToString(fd, &cache_info_contents)) {
+      PLOG(ERROR) << "Failed to read cache-info from fd " << fd;
+      return default_value;
+    }
+  } else {
+    std::string path = GetApexDataDalvikCacheDirectory(InstructionSet::kNone) + "/cache-info.xml";
+    if (!android::base::ReadFileToString(path, &cache_info_contents)) {
+      // If the file is not found, then we are in chroot or in a standalone runtime process (e.g.,
+      // IncidentHelper), or odsign/odrefresh failed to generate and sign the cache info. There's
+      // nothing we can do.
+      if (errno != ENOENT) {
+        PLOG(ERROR) << "Failed to read cache-info from the default path";
+      }
+      return default_value;
+    }
+  }
+
+  std::optional<com::android::art::CacheInfo> cache_info =
+      com::android::art::parse(cache_info_contents.c_str());
   if (!cache_info.has_value()) {
-    // We are in chroot or in a standalone runtime process (e.g., IncidentHelper), or
-    // odsign/odrefresh failed to generate and sign the cache info. There's nothing we can do.
+    // This should never happen.
+    LOG(ERROR) << "Failed to parse cache-info";
     return default_value;
   }
   const com::android::art::KeyValuePairList* list = cache_info->getFirstSystemProperties();
   if (list == nullptr) {
     // This should never happen.
-    LOG(ERROR) << "Missing system properties from cache-info.";
+    LOG(ERROR) << "Missing system properties from cache-info";
     return default_value;
   }
   const std::vector<com::android::art::KeyValuePair>& properties = list->getItem();