Add a helper class OfaContext for OatFileAssistant.

OfaContext fetches and caches information including boot image
checksums, bootclasspath checksums, and APEX versions.

This change eliminates the unnecessary reads of the art files,
oat files, and dex files in the slow path of app oat header validation
on app startup. After this change, there is no longer a "fast path" and
a "slow path". Instead, OfaContext always gets the boot image checksums
and BCP checksums from the runtime. (Trace before:
http://screen/6CPziPGmG92Mrw2, Trace after:
http://screen/gzpUEcJ3R78fWiJ)

OfaContext can be kept on the caller side so that the cache can be
reused, making callers like artd faster on repetitive calls to
OatFileAssistant. After this change, calling Artd::getOptimizationStatus
100 times takes 600ms less total time. For example, running
`adb shell pm art get-optimization-status com.google.android.youtube`
100 times took 3.2s on average on Cuttlefish before this change, but now
takes 2.6s after this change.

In addition, after this change, the responsibilities of
OatFileAssistant and ImageSpace are divided in a better way. ImageSpace
is purely responsible for loading and validating boot images. It is no
longer responsible for validating app oat headers. Instead, this
responsibility is now taken by OatFileAssistant.

`--validate-bcp` is removed from dexoptanalyzer because no one is using
it.

Bug: 238394854
Bug: 229268202
Test: m test-art-host-gtest-art_runtime_tests
Test: atest ArtGtestsTargetChroot
Test: art/test.py --host -r
Test: atest art_standalone_dexpreopt_tests
Test: atest odsign_e2e_tests_full
Ignore-AOSP-First: Contains internal only changes. Will cherry-pick the
  AOSP part later.
Change-Id: I83b43684d78c91e649300929ca2f348c626e1cab
diff --git a/artd/artd.cc b/artd/artd.cc
index d0a72e4..87d3858 100644
--- a/artd/artd.cc
+++ b/artd/artd.cc
@@ -36,11 +36,10 @@
 #include "android/binder_auto_utils.h"
 #include "android/binder_manager.h"
 #include "android/binder_process.h"
-#include "base/array_ref.h"
 #include "base/file_utils.h"
 #include "oat_file_assistant.h"
+#include "oat_file_assistant_context.h"
 #include "path_utils.h"
-#include "runtime.h"
 #include "tools/tools.h"
 
 namespace art {
@@ -106,33 +105,32 @@
                                           const std::string& in_instructionSet,
                                           const std::string& in_classLoaderContext,
                                           GetOptimizationStatusResult* _aidl_return) {
-  Result<OatFileAssistant::RuntimeOptions> runtime_options = GetRuntimeOptions();
-  if (!runtime_options.ok()) {
+  Result<OatFileAssistantContext*> ofa_context = GetOatFileAssistantContext();
+  if (!ofa_context.ok()) {
     return ScopedAStatus::fromExceptionCodeWithMessage(
         EX_ILLEGAL_STATE,
-        ("Failed to get runtime options: " + runtime_options.error().message()).c_str());
+        ("Failed to get OatFileAssistantContext: " + ofa_context.error().message()).c_str());
   }
 
   std::unique_ptr<ClassLoaderContext> context;
   std::string error_msg;
-  auto oat_file_assistant = OatFileAssistant::Create(
-      in_dexFile.c_str(),
-      in_instructionSet.c_str(),
-      in_classLoaderContext.c_str(),
-      /*load_executable=*/false,
-      /*only_load_trusted_executable=*/true,
-      std::make_unique<OatFileAssistant::RuntimeOptions>(std::move(*runtime_options)),
-      &context,
-      &error_msg);
+  auto oat_file_assistant = OatFileAssistant::Create(in_dexFile.c_str(),
+                                                     in_instructionSet.c_str(),
+                                                     in_classLoaderContext.c_str(),
+                                                     /*load_executable=*/false,
+                                                     /*only_load_trusted_executable=*/true,
+                                                     ofa_context.value(),
+                                                     &context,
+                                                     &error_msg);
   if (oat_file_assistant == nullptr) {
     return ScopedAStatus::fromExceptionCodeWithMessage(
         EX_ILLEGAL_STATE, ("Failed to create OatFileAssistant: " + error_msg).c_str());
   }
 
   std::string ignored_odex_status;
-  oat_file_assistant->GetOptimizationStatus(&_aidl_return->compilerFilter,
+  oat_file_assistant->GetOptimizationStatus(&_aidl_return->locationDebugString,
+                                            &_aidl_return->compilerFilter,
                                             &_aidl_return->compilationReason,
-                                            &_aidl_return->locationDebugString,
                                             &ignored_odex_status);
 
   // We ignore odex_status because it is not meaningful. It can only be either "up-to-date",
@@ -157,14 +155,19 @@
   return {};
 }
 
-Result<OatFileAssistant::RuntimeOptions> Artd::GetRuntimeOptions() {
-  return OatFileAssistant::RuntimeOptions{
-      .image_locations = *OR_RETURN(GetBootImageLocations()),
-      .boot_class_path = *OR_RETURN(GetBootClassPath()),
-      .boot_class_path_locations = *OR_RETURN(GetBootClassPath()),
-      .deny_art_apex_data_files = DenyArtApexDataFiles(),
-      .apex_versions = *OR_RETURN(GetApexVersions()),
-  };
+Result<OatFileAssistantContext*> Artd::GetOatFileAssistantContext() {
+  if (ofa_context_ == nullptr) {
+    ofa_context_ = std::make_unique<OatFileAssistantContext>(
+        std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+            OatFileAssistantContext::RuntimeOptions{
+                .image_locations = *OR_RETURN(GetBootImageLocations()),
+                .boot_class_path = *OR_RETURN(GetBootClassPath()),
+                .boot_class_path_locations = *OR_RETURN(GetBootClassPath()),
+                .deny_art_apex_data_files = DenyArtApexDataFiles(),
+            }));
+  }
+
+  return ofa_context_.get();
 }
 
 Result<const std::vector<std::string>*> Artd::GetBootImageLocations() {
@@ -202,15 +205,6 @@
   return &cached_boot_class_path_.value();
 }
 
-android::base::Result<const std::string*> Artd::GetApexVersions() {
-  if (!cached_apex_versions_.has_value()) {
-    cached_apex_versions_ =
-        Runtime::GetApexVersions(ArrayRef<const std::string>(*OR_RETURN(GetBootClassPath())));
-  }
-
-  return &cached_apex_versions_.value();
-}
-
 bool Artd::UseJitZygote() {
   if (!cached_use_jit_zygote_.has_value()) {
     cached_use_jit_zygote_ =
diff --git a/artd/artd.h b/artd/artd.h
index 7beef7f..25a4c86 100644
--- a/artd/artd.h
+++ b/artd/artd.h
@@ -17,13 +17,14 @@
 #ifndef ART_ARTD_ARTD_H_
 #define ART_ARTD_ARTD_H_
 
+#include <memory>
 #include <string>
 #include <vector>
 
 #include "aidl/com/android/server/art/BnArtd.h"
 #include "android-base/result.h"
 #include "android/binder_auto_utils.h"
-#include "oat_file_assistant.h"
+#include "oat_file_assistant_context.h"
 #include "tools/system_properties.h"
 
 namespace art {
@@ -50,14 +51,12 @@
   android::base::Result<void> Start();
 
  private:
-  android::base::Result<OatFileAssistant::RuntimeOptions> GetRuntimeOptions();
+  android::base::Result<OatFileAssistantContext*> GetOatFileAssistantContext();
 
   android::base::Result<const std::vector<std::string>*> GetBootImageLocations();
 
   android::base::Result<const std::vector<std::string>*> GetBootClassPath();
 
-  android::base::Result<const std::string*> GetApexVersions();
-
   bool UseJitZygote();
 
   bool DenyArtApexDataFiles();
@@ -68,6 +67,8 @@
   std::optional<bool> cached_use_jit_zygote_;
   std::optional<bool> cached_deny_art_apex_data_files_;
 
+  std::unique_ptr<OatFileAssistantContext> ofa_context_;
+
   std::unique_ptr<art::tools::SystemProperties> props_;
 };
 
diff --git a/dexoptanalyzer/dexoptanalyzer.cc b/dexoptanalyzer/dexoptanalyzer.cc
index 6137766..ef43157 100644
--- a/dexoptanalyzer/dexoptanalyzer.cc
+++ b/dexoptanalyzer/dexoptanalyzer.cc
@@ -122,9 +122,6 @@
   UsageError("      print a colon-separated list of its dex files to standard output. Dexopt");
   UsageError("      needed analysis is not performed when this option is set.");
   UsageError("");
-  UsageError("  --validate-bcp: validates the boot class path files (.art, .oat, .vdex).");
-  UsageError("      Requires --isa and --image options to locate artifacts.");
-  UsageError("");
   UsageError("Return code:");
   UsageError("  To make it easier to integrate with the internal tools this command will make");
   UsageError("    available its result (dexoptNeeded) as the exit/return code. i.e. it will not");
@@ -147,10 +144,7 @@
 
 class DexoptAnalyzer final {
  public:
-  DexoptAnalyzer() :
-      only_flatten_context_(false),
-      only_validate_bcp_(false),
-      downgrade_(false) {}
+  DexoptAnalyzer() : only_flatten_context_(false), downgrade_(false) {}
 
   void ParseArgs(int argc, char **argv) {
     original_argc = argc;
@@ -236,8 +230,6 @@
         }
       } else if (option == "--flatten-class-loader-context") {
         only_flatten_context_ = true;
-      } else if (option == "--validate-bcp") {
-        only_validate_bcp_ = true;
       } else {
         Usage("Unknown argument '%s'", raw_option);
       }
@@ -363,83 +355,6 @@
     }
   }
 
-  // Validates the boot classpath and boot classpath extensions by checking the image checksums,
-  // the oat files and the vdex files.
-  //
-  // Returns `ReturnCode::kNoDexOptNeeded` when all the files are up-to-date,
-  // `ReturnCode::kDex2OatFromScratch` if any of the files are missing or out-of-date, and
-  // `ReturnCode::kErrorCannotCreateRuntime` if the files could not be tested due to problem
-  // creating a runtime.
-  ReturnCode ValidateBcp() const {
-    using ImageSpace = gc::space::ImageSpace;
-
-    if (!CreateRuntime()) {
-      return ReturnCode::kErrorCannotCreateRuntime;
-    }
-    std::unique_ptr<Runtime> runtime(Runtime::Current());
-
-    auto dex_files = ArrayRef<const DexFile* const>(runtime->GetClassLinker()->GetBootClassPath());
-    auto boot_image_spaces = ArrayRef<ImageSpace* const>(runtime->GetHeap()->GetBootImageSpaces());
-    const std::string checksums = ImageSpace::GetBootClassPathChecksums(boot_image_spaces,
-                                                                        dex_files);
-
-    std::string error_msg;
-    const std::vector<std::string>& bcp = runtime->GetBootClassPath();
-    const std::vector<std::string>& bcp_locations = runtime->GetBootClassPathLocations();
-    const std::vector<int>& bcp_fds = runtime->GetBootClassPathFds();
-    const std::vector<std::string>& image_locations = runtime->GetImageLocations();
-    const std::string bcp_locations_path = android::base::Join(bcp_locations, ':');
-    if (!ImageSpace::VerifyBootClassPathChecksums(checksums,
-                                                  bcp_locations_path,
-                                                  ArrayRef<const std::string>(image_locations),
-                                                  ArrayRef<const std::string>(bcp_locations),
-                                                  ArrayRef<const std::string>(bcp),
-                                                  ArrayRef<const int>(bcp_fds),
-                                                  runtime->GetInstructionSet(),
-                                                  runtime->GetApexVersions(),
-                                                  &error_msg)) {
-      LOG(INFO) << "Failed to verify boot class path checksums: " << error_msg;
-      return ReturnCode::kDex2OatFromScratch;
-    }
-
-    const auto& image_spaces = runtime->GetHeap()->GetBootImageSpaces();
-    size_t bcp_component_count = 0;
-    for (const auto& image_space : image_spaces) {
-      if (!image_space->GetImageHeader().IsValid()) {
-        LOG(INFO) << "Image header is not valid: " << image_space->GetImageFilename();
-        return ReturnCode::kDex2OatFromScratch;
-      }
-      const OatFile* oat_file = image_space->GetOatFile();
-      if (oat_file == nullptr) {
-        const std::string oat_path = ReplaceFileExtension(image_space->GetImageFilename(), "oat");
-        LOG(INFO) << "Oat file missing: " << oat_path;
-        return ReturnCode::kDex2OatFromScratch;
-      }
-      if (!oat_file->GetOatHeader().IsValid() ||
-          !ImageSpace::ValidateOatFile(*oat_file, &error_msg)) {
-        LOG(INFO) << "Oat file is not valid: " << oat_file->GetLocation() << " " << error_msg;
-        return ReturnCode::kDex2OatFromScratch;
-      }
-      const VdexFile* vdex_file = oat_file->GetVdexFile();
-      if (vdex_file == nullptr || !vdex_file->IsValid()) {
-        LOG(INFO) << "Vdex file is not valid : " << oat_file->GetLocation();
-        return ReturnCode::kDex2OatFromScratch;
-      }
-      bcp_component_count += image_space->GetComponentCount();
-    }
-
-    // If the number of components encountered in the image spaces does not match the number
-    // of components expected from the boot classpath locations then something is missing.
-    if (bcp_component_count != bcp_locations.size()) {
-      for (size_t i = bcp_component_count; i < bcp_locations.size(); ++i) {
-        LOG(INFO) << "Missing image file for " << bcp_locations[i];
-      }
-      return ReturnCode::kDex2OatFromScratch;
-    }
-
-    return ReturnCode::kNoDexOptNeeded;
-  }
-
   ReturnCode FlattenClassLoaderContext() const {
     DCHECK(only_flatten_context_);
     if (context_str_.empty()) {
@@ -458,8 +373,6 @@
   ReturnCode Run() const {
     if (only_flatten_context_) {
       return FlattenClassLoaderContext();
-    } else if (only_validate_bcp_) {
-      return ValidateBcp();
     } else {
       return GetDexOptNeeded();
     }
@@ -471,7 +384,6 @@
   CompilerFilter::Filter compiler_filter_;
   std::string context_str_;
   bool only_flatten_context_;
-  bool only_validate_bcp_;
   ProfileAnalysisResult profile_analysis_result_;
   bool downgrade_;
   std::string image_;
diff --git a/openjdkjvmti/ti_search.cc b/openjdkjvmti/ti_search.cc
index 526836e..3ddf4ab 100644
--- a/openjdkjvmti/ti_search.cc
+++ b/openjdkjvmti/ti_search.cc
@@ -247,9 +247,9 @@
     return ERR(ILLEGAL_ARGUMENT);
   }
 
-  art::ScopedObjectAccess soa(art::Thread::Current());
+  current->AppendToBootClassPath(segment, segment, dex_files);
   for (std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
-    current->GetClassLinker()->AppendToBootClassPath(art::Thread::Current(), dex_file.release());
+    dex_file.release();
   }
 
   return ERR(NONE);
diff --git a/runtime/Android.bp b/runtime/Android.bp
index f8b8968..d42848f 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -256,6 +256,7 @@
         "oat.cc",
         "oat_file.cc",
         "oat_file_assistant.cc",
+        "oat_file_assistant_context.cc",
         "oat_file_manager.cc",
         "oat_quick_method_header.cc",
         "object_lock.cc",
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 0c02ead..1f44a67 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
-#include <string>
-#include <vector>
+#include "dexopt_test.h"
 
 #include <gtest/gtest.h>
 #include <procinfo/process_map.h>
 
+#include <string>
+#include <vector>
+
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
+#include "arch/instruction_set.h"
 #include "base/file_utils.h"
 #include "base/mem_map.h"
 #include "common_runtime_test.h"
@@ -29,10 +32,10 @@
 #include "dex/art_dex_file_loader.h"
 #include "dex/dex_file_loader.h"
 #include "dex2oat_environment_test.h"
-#include "dexopt_test.h"
 #include "gc/space/image_space.h"
 #include "hidden_api.h"
 #include "oat.h"
+#include "oat_file_assistant.h"
 #include "profile/profile_compilation_info.h"
 
 namespace art {
@@ -163,23 +166,13 @@
   EXPECT_EQ(filter, odex_file->GetCompilerFilter());
 
   if (CompilerFilter::DependsOnImageChecksum(filter)) {
-    const OatHeader& oat_header = odex_file->GetOatHeader();
-    const char* oat_bcp = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathKey);
-    ASSERT_TRUE(oat_bcp != nullptr);
-    ASSERT_EQ(oat_bcp, android::base::Join(Runtime::Current()->GetBootClassPathLocations(), ':'));
-    const char* checksums = oat_header.GetStoreValueByKey(OatHeader::kBootClassPathChecksumsKey);
-    ASSERT_TRUE(checksums != nullptr);
+    std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(/*spec=*/"");
+    OatFileAssistant oat_file_assistant(dex_location.c_str(),
+                                        kRuntimeISA,
+                                        context.get(),
+                                        /*load_executable=*/false);
 
-    bool match = gc::space::ImageSpace::VerifyBootClassPathChecksums(
-        checksums,
-        oat_bcp,
-        ArrayRef<const std::string>(&image_location, 1),
-        ArrayRef<const std::string>(Runtime::Current()->GetBootClassPathLocations()),
-        ArrayRef<const std::string>(Runtime::Current()->GetBootClassPath()),
-        ArrayRef<const int>(Runtime::Current()->GetBootClassPathFds()),
-        kRuntimeISA,
-        Runtime::Current()->GetApexVersions(),
-        &error_msg);
+    bool match = oat_file_assistant.ValidateBootClassPathChecksums(*odex_file);
     ASSERT_EQ(!with_alternate_image, match) << error_msg;
   }
 }
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index b1ab960..373dd280 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1368,9 +1368,9 @@
   }
 };
 
-static void AppendImageChecksum(uint32_t component_count,
-                                uint32_t checksum,
-                                /*inout*/std::string* checksums) {
+void ImageSpace::AppendImageChecksum(uint32_t component_count,
+                                     uint32_t checksum,
+                                     /*inout*/ std::string* checksums) {
   static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
   StringAppendF(checksums, "i;%u/%08x", component_count, checksum);
 }
@@ -1380,7 +1380,7 @@
                                         /*inout*/std::string_view* oat_checksums,
                                         /*out*/std::string* error_msg) {
   std::string image_checksum;
-  AppendImageChecksum(component_count, checksum, &image_checksum);
+  ImageSpace::AppendImageChecksum(component_count, checksum, &image_checksum);
   if (!StartsWith(*oat_checksums, image_checksum)) {
     *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
                               std::string(*oat_checksums).c_str(),
@@ -1391,196 +1391,6 @@
   return true;
 }
 
-// Helper class to find the primary boot image and boot image extensions
-// and determine the boot image layout.
-class ImageSpace::BootImageLayout {
- public:
-  // Description of a "chunk" of the boot image, i.e. either primary boot image
-  // or a boot image extension, used in conjunction with the boot class path to
-  // load boot image components.
-  struct ImageChunk {
-    std::string base_location;
-    std::string base_filename;
-    std::vector<std::string> profile_files;
-    size_t start_index;
-    uint32_t component_count;
-    uint32_t image_space_count;
-    uint32_t reservation_size;
-    uint32_t checksum;
-    uint32_t boot_image_component_count;
-    uint32_t boot_image_checksum;
-    uint32_t boot_image_size;
-
-    // The following file descriptors hold the memfd files for extensions compiled
-    // in memory and described by the above fields. We want to use them to mmap()
-    // the contents and then close them while treating the ImageChunk description
-    // as immutable (const), so make these fields explicitly mutable.
-    mutable android::base::unique_fd art_fd;
-    mutable android::base::unique_fd vdex_fd;
-    mutable android::base::unique_fd oat_fd;
-  };
-
-  BootImageLayout(ArrayRef<const std::string> image_locations,
-                  ArrayRef<const std::string> boot_class_path,
-                  ArrayRef<const std::string> boot_class_path_locations,
-                  ArrayRef<const int> boot_class_path_fds,
-                  ArrayRef<const int> boot_class_path_image_fds,
-                  ArrayRef<const int> boot_class_path_vdex_fds,
-                  ArrayRef<const int> boot_class_path_oat_fds,
-                  const std::string* apex_versions = nullptr)
-      : image_locations_(image_locations),
-        boot_class_path_(boot_class_path),
-        boot_class_path_locations_(boot_class_path_locations),
-        boot_class_path_fds_(boot_class_path_fds),
-        boot_class_path_image_fds_(boot_class_path_image_fds),
-        boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
-        boot_class_path_oat_fds_(boot_class_path_oat_fds),
-        apex_versions_(GetApexVersions(apex_versions)) {}
-
-  std::string GetPrimaryImageLocation();
-
-  bool LoadFromSystem(InstructionSet image_isa, /*out*/std::string* error_msg) {
-    return LoadOrValidateFromSystem(image_isa, /*oat_checksums=*/ nullptr, error_msg);
-  }
-
-  bool ValidateFromSystem(InstructionSet image_isa,
-                          /*inout*/std::string_view* oat_checksums,
-                          /*out*/std::string* error_msg) {
-    DCHECK(oat_checksums != nullptr);
-    return LoadOrValidateFromSystem(image_isa, oat_checksums, error_msg);
-  }
-
-  ArrayRef<const ImageChunk> GetChunks() const {
-    return ArrayRef<const ImageChunk>(chunks_);
-  }
-
-  uint32_t GetBaseAddress() const {
-    return base_address_;
-  }
-
-  size_t GetNextBcpIndex() const {
-    return next_bcp_index_;
-  }
-
-  size_t GetTotalComponentCount() const {
-    return total_component_count_;
-  }
-
-  size_t GetTotalReservationSize() const {
-    return total_reservation_size_;
-  }
-
- private:
-  struct NamedComponentLocation {
-    std::string base_location;
-    size_t bcp_index;
-    std::vector<std::string> profile_filenames;
-  };
-
-  std::string ExpandLocationImpl(const std::string& location,
-                                 size_t bcp_index,
-                                 bool boot_image_extension) {
-    std::vector<std::string> expanded = ExpandMultiImageLocations(
-        ArrayRef<const std::string>(boot_class_path_).SubArray(bcp_index, 1u),
-        location,
-        boot_image_extension);
-    DCHECK_EQ(expanded.size(), 1u);
-    return expanded[0];
-  }
-
-  std::string ExpandLocation(const std::string& location, size_t bcp_index) {
-    if (bcp_index == 0u) {
-      DCHECK_EQ(location, ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ false));
-      return location;
-    } else {
-      return ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/ true);
-    }
-  }
-
-  std::string GetBcpComponentPath(size_t bcp_index) {
-    DCHECK_LE(bcp_index, boot_class_path_.size());
-    size_t bcp_slash_pos = boot_class_path_[bcp_index].rfind('/');
-    DCHECK_NE(bcp_slash_pos, std::string::npos);
-    return boot_class_path_[bcp_index].substr(0u, bcp_slash_pos + 1u);
-  }
-
-  bool VerifyImageLocation(ArrayRef<const std::string> components,
-                           /*out*/size_t* named_components_count,
-                           /*out*/std::string* error_msg);
-
-  bool MatchNamedComponents(
-      ArrayRef<const std::string> named_components,
-      /*out*/std::vector<NamedComponentLocation>* named_component_locations,
-      /*out*/std::string* error_msg);
-
-  bool ValidateBootImageChecksum(const char* file_description,
-                                 const ImageHeader& header,
-                                 /*out*/std::string* error_msg);
-
-  bool ValidateHeader(const ImageHeader& header,
-                      size_t bcp_index,
-                      const char* file_description,
-                      /*out*/std::string* error_msg);
-
-  bool ValidateOatFile(const std::string& base_location,
-                       const std::string& base_filename,
-                       size_t bcp_index,
-                       size_t component_count,
-                       /*out*/std::string* error_msg);
-
-  bool ReadHeader(const std::string& base_location,
-                  const std::string& base_filename,
-                  size_t bcp_index,
-                  /*out*/std::string* error_msg);
-
-  // Compiles a consecutive subsequence of bootclasspath dex files, whose contents are included in
-  // the profiles specified by `profile_filenames`, starting from `bcp_index`.
-  bool CompileBootclasspathElements(const std::string& base_location,
-                                    const std::string& base_filename,
-                                    size_t bcp_index,
-                                    const std::vector<std::string>& profile_filenames,
-                                    ArrayRef<const std::string> dependencies,
-                                    /*out*/std::string* error_msg);
-
-  bool CheckAndRemoveLastChunkChecksum(/*inout*/std::string_view* oat_checksums,
-                                       /*out*/std::string* error_msg);
-
-  template <typename FilenameFn>
-  bool LoadOrValidate(FilenameFn&& filename_fn,
-                      /*inout*/std::string_view* oat_checksums,
-                      /*out*/std::string* error_msg);
-
-  bool LoadOrValidateFromSystem(InstructionSet image_isa,
-                                /*inout*/std::string_view* oat_checksums,
-                                /*out*/std::string* error_msg);
-
-  // This function prefers taking APEX versions from the input instead of from the runtime if
-  // possible. If the input is present, `ValidateFromSystem` can work without an active runtime.
-  static const std::string& GetApexVersions(const std::string* apex_versions) {
-    if (apex_versions == nullptr) {
-      DCHECK(Runtime::Current() != nullptr);
-      return Runtime::Current()->GetApexVersions();
-    } else {
-      return *apex_versions;
-    }
-  }
-
-  ArrayRef<const std::string> image_locations_;
-  ArrayRef<const std::string> boot_class_path_;
-  ArrayRef<const std::string> boot_class_path_locations_;
-  ArrayRef<const int> boot_class_path_fds_;
-  ArrayRef<const int> boot_class_path_image_fds_;
-  ArrayRef<const int> boot_class_path_vdex_fds_;
-  ArrayRef<const int> boot_class_path_oat_fds_;
-
-  std::vector<ImageChunk> chunks_;
-  uint32_t base_address_ = 0u;
-  size_t next_bcp_index_ = 0u;
-  size_t total_component_count_ = 0u;
-  size_t total_reservation_size_ = 0u;
-  const std::string& apex_versions_;
-};
-
 std::string ImageSpace::BootImageLayout::GetPrimaryImageLocation() {
   DCHECK(!image_locations_.empty());
   std::string location = image_locations_[0];
@@ -2167,48 +1977,12 @@
   return true;
 }
 
-bool ImageSpace::BootImageLayout::CheckAndRemoveLastChunkChecksum(
-    /*inout*/std::string_view* oat_checksums,
-    /*out*/std::string* error_msg) {
-  DCHECK(oat_checksums != nullptr);
-  DCHECK(!chunks_.empty());
-  const ImageChunk& chunk = chunks_.back();
-  size_t component_count = chunk.component_count;
-  size_t checksum = chunk.checksum;
-  if (!CheckAndRemoveImageChecksum(component_count, checksum, oat_checksums, error_msg)) {
-    DCHECK(!error_msg->empty());
-    return false;
-  }
-  if (oat_checksums->empty()) {
-    if (next_bcp_index_ != boot_class_path_.size()) {
-      *error_msg = StringPrintf("Checksum too short, missing %zu components.",
-                                boot_class_path_.size() - next_bcp_index_);
-      return false;
-    }
-    return true;
-  }
-  if (!StartsWith(*oat_checksums, ":")) {
-    *error_msg = StringPrintf("Missing ':' separator at start of %s",
-                              std::string(*oat_checksums).c_str());
-    return false;
-  }
-  oat_checksums->remove_prefix(1u);
-  if (oat_checksums->empty()) {
-    *error_msg = "Missing checksums after the ':' separator.";
-    return false;
-  }
-  return true;
-}
-
 template <typename FilenameFn>
-bool ImageSpace::BootImageLayout::LoadOrValidate(FilenameFn&& filename_fn,
-                                                 /*inout*/std::string_view* oat_checksums,
-                                                 /*out*/std::string* error_msg) {
+bool ImageSpace::BootImageLayout::Load(FilenameFn&& filename_fn,
+                                       bool allow_in_memory_compilation,
+                                       /*out*/ std::string* error_msg) {
   DCHECK(GetChunks().empty());
   DCHECK_EQ(GetBaseAddress(), 0u);
-  bool validate = (oat_checksums != nullptr);
-  static_assert(ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check.");
-  DCHECK_IMPLIES(validate, StartsWith(*oat_checksums, "i"));
 
   ArrayRef<const std::string> components = image_locations_;
   size_t named_components_count = 0u;
@@ -2239,17 +2013,14 @@
       LOG(ERROR) << "Named image component already covered by previous image: " << base_location;
       continue;
     }
-    if (validate && bcp_index > bcp_pos) {
-      *error_msg = StringPrintf("End of contiguous boot class path images, remaining checksum: %s",
-                                std::string(*oat_checksums).c_str());
-      return false;
-    }
     std::string local_error_msg;
-    std::string* err_msg = validate ? error_msg : &local_error_msg;
     std::string base_filename;
-    if (!filename_fn(base_location, &base_filename, err_msg) ||
-        !ReadHeader(base_location, base_filename, bcp_index, err_msg)) {
-      if (validate) {
+    if (!filename_fn(base_location, &base_filename, &local_error_msg) ||
+        !ReadHeader(base_location, base_filename, bcp_index, &local_error_msg)) {
+      if (!allow_in_memory_compilation) {
+        // The boot image is unusable and we can't continue by generating a boot image in memory.
+        // All we can do is to return.
+        *error_msg = std::move(local_error_msg);
         return false;
       }
       LOG(ERROR) << "Error reading named image component header for " << base_location
@@ -2296,14 +2067,6 @@
         continue;
       }
     }
-    if (validate) {
-      if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
-        return false;
-      }
-      if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
-        return true;  // Let the caller deal with the dex file checksums if any.
-      }
-    }
     bcp_pos = GetNextBcpIndex();
   }
 
@@ -2336,24 +2099,10 @@
           VLOG(image) << "Found image extension for " << ExpandLocation(base_location, bcp_pos);
           bcp_pos = GetNextBcpIndex();
           found = true;
-          if (validate) {
-            if (!CheckAndRemoveLastChunkChecksum(oat_checksums, error_msg)) {
-              return false;
-            }
-            if (oat_checksums->empty() || !StartsWith(*oat_checksums, "i")) {
-              return true;  // Let the caller deal with the dex file checksums if any.
-            }
-          }
           break;
         }
       }
       if (!found) {
-        if (validate) {
-          *error_msg = StringPrintf("Missing extension for %s, remaining checksum: %s",
-                                    bcp_component.c_str(),
-                                    std::string(*oat_checksums).c_str());
-          return false;
-        }
         ++bcp_pos;
       }
     }
@@ -2362,16 +2111,16 @@
   return true;
 }
 
-bool ImageSpace::BootImageLayout::LoadOrValidateFromSystem(InstructionSet image_isa,
-                                                           /*inout*/std::string_view* oat_checksums,
-                                                           /*out*/std::string* error_msg) {
+bool ImageSpace::BootImageLayout::LoadFromSystem(InstructionSet image_isa,
+                                                 bool allow_in_memory_compilation,
+                                                 /*out*/ std::string* error_msg) {
   auto filename_fn = [image_isa](const std::string& location,
                                  /*out*/std::string* filename,
                                  /*out*/std::string* err_msg ATTRIBUTE_UNUSED) {
     *filename = GetSystemImageFilename(location.c_str(), image_isa);
     return true;
   };
-  return LoadOrValidate(filename_fn, oat_checksums, error_msg);
+  return Load(filename_fn, allow_in_memory_compilation, error_msg);
 }
 
 class ImageSpace::BootImageLoader {
@@ -3373,7 +3122,7 @@
                          boot_class_path_image_fds_,
                          boot_class_path_vdex_fds_,
                          boot_class_path_oat_fds_);
-  if (!layout.LoadFromSystem(image_isa_, error_msg)) {
+  if (!layout.LoadFromSystem(image_isa_, /*allow_in_memory_compilation=*/true, error_msg)) {
     return false;
   }
 
@@ -3719,7 +3468,7 @@
   return n;
 }
 
-static size_t CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
+size_t ImageSpace::CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
                                          ArrayRef<const std::string> boot_class_path,
                                          /*out*/std::string* error_msg) {
   // Check that the oat BCP is a prefix of current BCP locations and count components.
@@ -3751,140 +3500,6 @@
   return component_count;
 }
 
-bool ImageSpace::VerifyBootClassPathChecksums(std::string_view oat_checksums,
-                                              std::string_view oat_boot_class_path,
-                                              ArrayRef<const std::string> image_locations,
-                                              ArrayRef<const std::string> boot_class_path_locations,
-                                              ArrayRef<const std::string> boot_class_path,
-                                              ArrayRef<const int> boot_class_path_fds,
-                                              InstructionSet image_isa,
-                                              const std::string& apex_versions,
-                                              /*out*/std::string* error_msg) {
-  if (oat_checksums.empty() || oat_boot_class_path.empty()) {
-    *error_msg = oat_checksums.empty() ? "Empty checksums." : "Empty boot class path.";
-    return false;
-  }
-
-  DCHECK_EQ(boot_class_path_locations.size(), boot_class_path.size());
-  size_t bcp_size =
-      CheckAndCountBCPComponents(oat_boot_class_path, boot_class_path_locations, error_msg);
-  if (bcp_size == static_cast<size_t>(-1)) {
-    DCHECK(!error_msg->empty());
-    return false;
-  }
-
-  size_t bcp_pos = 0u;
-  if (StartsWith(oat_checksums, "i")) {
-    // Use only the matching part of the BCP for validation.  FDs are optional, so only pass the
-    // sub-array if provided.
-    ArrayRef<const int> bcp_fds = boot_class_path_fds.empty()
-        ? ArrayRef<const int>()
-        : boot_class_path_fds.SubArray(/*pos=*/ 0u, bcp_size);
-    BootImageLayout layout(image_locations,
-                           boot_class_path.SubArray(/*pos=*/ 0u, bcp_size),
-                           boot_class_path_locations.SubArray(/*pos=*/ 0u, bcp_size),
-                           bcp_fds,
-                           /*boot_class_path_image_fds=*/ ArrayRef<const int>(),
-                           /*boot_class_path_vdex_fds=*/ ArrayRef<const int>(),
-                           /*boot_class_path_oat_fds=*/ ArrayRef<const int>(),
-                           &apex_versions);
-    std::string primary_image_location = layout.GetPrimaryImageLocation();
-    std::string system_filename;
-    bool has_system = false;
-    if (!FindImageFilename(primary_image_location.c_str(),
-                           image_isa,
-                           &system_filename,
-                           &has_system)) {
-      *error_msg = StringPrintf("Unable to find image file for %s and %s",
-                                android::base::Join(image_locations, kComponentSeparator).c_str(),
-                                GetInstructionSetString(image_isa));
-      return false;
-    }
-
-    DCHECK(has_system);
-    if (!layout.ValidateFromSystem(image_isa, &oat_checksums, error_msg)) {
-      return false;
-    }
-    bcp_pos = layout.GetNextBcpIndex();
-  }
-
-  for ( ; bcp_pos != bcp_size; ++bcp_pos) {
-    static_assert(ImageSpace::kDexFileChecksumPrefix == 'd', "Format prefix check.");
-    if (!StartsWith(oat_checksums, "d")) {
-      *error_msg = StringPrintf("Missing dex checksums, expected %s to start with 'd'",
-                                std::string(oat_checksums).c_str());
-      return false;
-    }
-    oat_checksums.remove_prefix(1u);
-
-    const std::string& bcp_filename = boot_class_path[bcp_pos];
-    std::vector<uint32_t> checksums;
-    std::vector<std::string> dex_locations;
-    const ArtDexFileLoader dex_file_loader;
-    if (!dex_file_loader.GetMultiDexChecksums(bcp_filename.c_str(),
-                                              &checksums,
-                                              &dex_locations,
-                                              error_msg)) {
-      return false;
-    }
-    DCHECK(!checksums.empty());
-    for (uint32_t checksum : checksums) {
-      std::string dex_file_checksum = StringPrintf("/%08x", checksum);
-      if (!StartsWith(oat_checksums, dex_file_checksum)) {
-        *error_msg = StringPrintf(
-            "Dex checksum mismatch for bootclasspath file %s, expected %s to start with %s",
-            bcp_filename.c_str(),
-            std::string(oat_checksums).c_str(),
-            dex_file_checksum.c_str());
-        return false;
-      }
-      oat_checksums.remove_prefix(dex_file_checksum.size());
-    }
-    if (bcp_pos + 1u != bcp_size) {
-      if (!StartsWith(oat_checksums, ":")) {
-        *error_msg = StringPrintf("Missing ':' separator at start of %s",
-                                  std::string(oat_checksums).c_str());
-        return false;
-      }
-      oat_checksums.remove_prefix(1u);
-    }
-  }
-  if (!oat_checksums.empty()) {
-    *error_msg = StringPrintf("Checksum too long, unexpected tail %s",
-                              std::string(oat_checksums).c_str());
-    return false;
-  }
-  return true;
-}
-
-bool ImageSpace::VerifyBootImages(ArrayRef<const std::string> image_locations,
-                                  ArrayRef<const std::string> boot_class_path_locations,
-                                  ArrayRef<const std::string> boot_class_path,
-                                  ArrayRef<const int> boot_class_path_fds,
-                                  InstructionSet image_isa,
-                                  const std::string& apex_versions,
-                                  /*out*/std::string* error_msg) {
-  // Remove profiles to prevent compilation. We only need to verify boot images.
-  std::vector<std::string> image_location_without_profiles;
-  for (std::string location : image_locations) {
-    size_t profile_separator_pos = location.find(kProfileSeparator);
-    if (profile_separator_pos != std::string::npos) {
-      location.resize(profile_separator_pos);
-    }
-    image_location_without_profiles.push_back(std::move(location));
-  }
-
-  BootImageLayout layout(ArrayRef<const std::string>(image_location_without_profiles),
-                         boot_class_path,
-                         boot_class_path_locations,
-                         boot_class_path_fds,
-                         /*boot_class_path_image_fds=*/ ArrayRef<const int>(),
-                         /*boot_class_path_vdex_fds=*/ ArrayRef<const int>(),
-                         /*boot_class_path_oat_fds=*/ ArrayRef<const int>(),
-                         &apex_versions);
-  return layout.LoadFromSystem(image_isa, error_msg);
-}
-
 bool ImageSpace::VerifyBootClassPathChecksums(
     std::string_view oat_checksums,
     std::string_view oat_boot_class_path,
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index f1adbec..bf9cda2 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -17,13 +17,15 @@
 #ifndef ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
 #define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_H_
 
+#include "android-base/unique_fd.h"
+#include "base/array_ref.h"
 #include "gc/accounting/space_bitmap.h"
 #include "image.h"
+#include "runtime.h"
 #include "space.h"
 
 namespace art {
 
-template <typename T> class ArrayRef;
 class DexFile;
 enum class InstructionSet;
 class OatFile;
@@ -239,28 +241,6 @@
   // Returns the total number of components (jar files) associated with the image spaces.
   static size_t GetNumberOfComponents(ArrayRef<gc::space::ImageSpace* const> image_spaces);
 
-  // Returns whether the checksums are valid for the given boot class path,
-  // image location and ISA (may differ from the ISA of an initialized Runtime).
-  // The boot image and dex files do not need to be loaded in memory.
-  static bool VerifyBootClassPathChecksums(std::string_view oat_checksums,
-                                           std::string_view oat_boot_class_path,
-                                           ArrayRef<const std::string> image_locations,
-                                           ArrayRef<const std::string> boot_class_path_locations,
-                                           ArrayRef<const std::string> boot_class_path,
-                                           ArrayRef<const int> boot_class_path_fds,
-                                           InstructionSet image_isa,
-                                           const std::string& apex_versions,
-                                           /*out*/std::string* error_msg);
-
-  // Returns whether all the boot images listed in `image_locations` are valid.
-  static bool VerifyBootImages(ArrayRef<const std::string> image_locations,
-                               ArrayRef<const std::string> boot_class_path_locations,
-                               ArrayRef<const std::string> boot_class_path,
-                               ArrayRef<const int> boot_class_path_fds,
-                               InstructionSet image_isa,
-                               const std::string& apex_versions,
-                               /*out*/std::string* error_msg);
-
   // Returns whether the oat checksums and boot class path description are valid
   // for the given boot image spaces and boot class path. Used for boot image extensions.
   static bool VerifyBootClassPathChecksums(
@@ -321,6 +301,181 @@
 
   void ReleaseMetadata() REQUIRES_SHARED(Locks::mutator_lock_);
 
+  static void AppendImageChecksum(uint32_t component_count,
+                                  uint32_t checksum,
+                                  /*inout*/ std::string* checksums);
+
+  static size_t CheckAndCountBCPComponents(std::string_view oat_boot_class_path,
+                                           ArrayRef<const std::string> boot_class_path,
+                                           /*out*/ std::string* error_msg);
+
+  // Helper class to find the primary boot image and boot image extensions
+  // and determine the boot image layout.
+  class BootImageLayout {
+   public:
+    // Description of a "chunk" of the boot image, i.e. either primary boot image
+    // or a boot image extension, used in conjunction with the boot class path to
+    // load boot image components.
+    struct ImageChunk {
+      std::string base_location;
+      std::string base_filename;
+      std::vector<std::string> profile_files;
+      size_t start_index;
+      uint32_t component_count;
+      uint32_t image_space_count;
+      uint32_t reservation_size;
+      uint32_t checksum;
+      uint32_t boot_image_component_count;
+      uint32_t boot_image_checksum;
+      uint32_t boot_image_size;
+
+      // The following file descriptors hold the memfd files for extensions compiled
+      // in memory and described by the above fields. We want to use them to mmap()
+      // the contents and then close them while treating the ImageChunk description
+      // as immutable (const), so make these fields explicitly mutable.
+      mutable android::base::unique_fd art_fd;
+      mutable android::base::unique_fd vdex_fd;
+      mutable android::base::unique_fd oat_fd;
+    };
+
+    BootImageLayout(ArrayRef<const std::string> image_locations,
+                    ArrayRef<const std::string> boot_class_path,
+                    ArrayRef<const std::string> boot_class_path_locations,
+                    ArrayRef<const int> boot_class_path_fds,
+                    ArrayRef<const int> boot_class_path_image_fds,
+                    ArrayRef<const int> boot_class_path_vdex_fds,
+                    ArrayRef<const int> boot_class_path_oat_fds,
+                    const std::string* apex_versions = nullptr)
+        : image_locations_(image_locations),
+          boot_class_path_(boot_class_path),
+          boot_class_path_locations_(boot_class_path_locations),
+          boot_class_path_fds_(boot_class_path_fds),
+          boot_class_path_image_fds_(boot_class_path_image_fds),
+          boot_class_path_vdex_fds_(boot_class_path_vdex_fds),
+          boot_class_path_oat_fds_(boot_class_path_oat_fds),
+          apex_versions_(GetApexVersions(apex_versions)) {}
+
+    std::string GetPrimaryImageLocation();
+
+    bool LoadFromSystem(InstructionSet image_isa,
+                        bool allow_in_memory_compilation,
+                        /*out*/ std::string* error_msg);
+
+    ArrayRef<const ImageChunk> GetChunks() const { return ArrayRef<const ImageChunk>(chunks_); }
+
+    uint32_t GetBaseAddress() const { return base_address_; }
+
+    size_t GetNextBcpIndex() const { return next_bcp_index_; }
+
+    size_t GetTotalComponentCount() const { return total_component_count_; }
+
+    size_t GetTotalReservationSize() const { return total_reservation_size_; }
+
+   private:
+    struct NamedComponentLocation {
+      std::string base_location;
+      size_t bcp_index;
+      std::vector<std::string> profile_filenames;
+    };
+
+    std::string ExpandLocationImpl(const std::string& location,
+                                   size_t bcp_index,
+                                   bool boot_image_extension) {
+      std::vector<std::string> expanded = ExpandMultiImageLocations(
+          ArrayRef<const std::string>(boot_class_path_).SubArray(bcp_index, 1u),
+          location,
+          boot_image_extension);
+      DCHECK_EQ(expanded.size(), 1u);
+      return expanded[0];
+    }
+
+    std::string ExpandLocation(const std::string& location, size_t bcp_index) {
+      if (bcp_index == 0u) {
+        DCHECK_EQ(location,
+                  ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/false));
+        return location;
+      } else {
+        return ExpandLocationImpl(location, bcp_index, /*boot_image_extension=*/true);
+      }
+    }
+
+    std::string GetBcpComponentPath(size_t bcp_index) {
+      DCHECK_LE(bcp_index, boot_class_path_.size());
+      size_t bcp_slash_pos = boot_class_path_[bcp_index].rfind('/');
+      DCHECK_NE(bcp_slash_pos, std::string::npos);
+      return boot_class_path_[bcp_index].substr(0u, bcp_slash_pos + 1u);
+    }
+
+    bool VerifyImageLocation(ArrayRef<const std::string> components,
+                             /*out*/ size_t* named_components_count,
+                             /*out*/ std::string* error_msg);
+
+    bool MatchNamedComponents(
+        ArrayRef<const std::string> named_components,
+        /*out*/ std::vector<NamedComponentLocation>* named_component_locations,
+        /*out*/ std::string* error_msg);
+
+    bool ValidateBootImageChecksum(const char* file_description,
+                                   const ImageHeader& header,
+                                   /*out*/ std::string* error_msg);
+
+    bool ValidateHeader(const ImageHeader& header,
+                        size_t bcp_index,
+                        const char* file_description,
+                        /*out*/ std::string* error_msg);
+
+    bool ValidateOatFile(const std::string& base_location,
+                         const std::string& base_filename,
+                         size_t bcp_index,
+                         size_t component_count,
+                         /*out*/ std::string* error_msg);
+
+    bool ReadHeader(const std::string& base_location,
+                    const std::string& base_filename,
+                    size_t bcp_index,
+                    /*out*/ std::string* error_msg);
+
+    // Compiles a consecutive subsequence of bootclasspath dex files, whose contents are included in
+    // the profiles specified by `profile_filenames`, starting from `bcp_index`.
+    bool CompileBootclasspathElements(const std::string& base_location,
+                                      const std::string& base_filename,
+                                      size_t bcp_index,
+                                      const std::vector<std::string>& profile_filenames,
+                                      ArrayRef<const std::string> dependencies,
+                                      /*out*/ std::string* error_msg);
+
+    template <typename FilenameFn>
+    bool Load(FilenameFn&& filename_fn,
+              bool allow_in_memory_compilation,
+              /*out*/ std::string* error_msg);
+
+    // This function prefers taking APEX versions from the input instead of from the runtime if
+    // possible. If the input is present, `ValidateFromSystem` can work without an active runtime.
+    static const std::string& GetApexVersions(const std::string* apex_versions) {
+      if (apex_versions == nullptr) {
+        DCHECK(Runtime::Current() != nullptr);
+        return Runtime::Current()->GetApexVersions();
+      } else {
+        return *apex_versions;
+      }
+    }
+
+    ArrayRef<const std::string> image_locations_;
+    ArrayRef<const std::string> boot_class_path_;
+    ArrayRef<const std::string> boot_class_path_locations_;
+    ArrayRef<const int> boot_class_path_fds_;
+    ArrayRef<const int> boot_class_path_image_fds_;
+    ArrayRef<const int> boot_class_path_vdex_fds_;
+    ArrayRef<const int> boot_class_path_oat_fds_;
+
+    std::vector<ImageChunk> chunks_;
+    uint32_t base_address_ = 0u;
+    size_t next_bcp_index_ = 0u;
+    size_t total_component_count_ = 0u;
+    size_t total_reservation_size_ = 0u;
+    const std::string& apex_versions_;
+  };
+
  protected:
   // Tries to initialize an ImageSpace from the given image path, returning null on error.
   //
@@ -360,7 +515,6 @@
   friend class Space;
 
  private:
-  class BootImageLayout;
   class BootImageLoader;
   template <typename ReferenceVisitor>
   class ClassTableVisitor;
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index fc85f15..b3a5917 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -321,57 +321,6 @@
   EXPECT_FALSE(ImageSpace::ValidateOatFile(*oat, &error_msg));
 }
 
-TEST_F(DexoptTest, Checksums) {
-  Runtime* runtime = Runtime::Current();
-  ASSERT_TRUE(runtime != nullptr);
-  ASSERT_FALSE(runtime->GetHeap()->GetBootImageSpaces().empty());
-
-  std::vector<std::string> bcp = runtime->GetBootClassPath();
-  std::vector<std::string> bcp_locations = runtime->GetBootClassPathLocations();
-  std::vector<const DexFile*> dex_files = runtime->GetClassLinker()->GetBootClassPath();
-
-  std::string error_msg;
-  auto create_and_verify = [&]() {
-    std::string checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
-        ArrayRef<gc::space::ImageSpace* const>(runtime->GetHeap()->GetBootImageSpaces()),
-        ArrayRef<const DexFile* const>(dex_files));
-    return gc::space::ImageSpace::VerifyBootClassPathChecksums(
-        checksums,
-        android::base::Join(bcp_locations, ':'),
-        ArrayRef<const std::string>(runtime->GetImageLocations()),
-        ArrayRef<const std::string>(bcp_locations),
-        ArrayRef<const std::string>(bcp),
-        /*boot_class_path_fds=*/ ArrayRef<const int>(),
-        kRuntimeISA,
-        runtime->GetApexVersions(),
-        &error_msg);
-  };
-
-  ASSERT_TRUE(create_and_verify()) << error_msg;
-
-  std::vector<std::unique_ptr<const DexFile>> opened_dex_files;
-  for (const std::string& src : { GetDexSrc1(), GetDexSrc2() }) {
-    std::vector<std::unique_ptr<const DexFile>> new_dex_files;
-    const ArtDexFileLoader dex_file_loader;
-    ASSERT_TRUE(dex_file_loader.Open(src.c_str(),
-                                     src,
-                                     /*verify=*/ true,
-                                     /*verify_checksum=*/ false,
-                                     &error_msg,
-                                     &new_dex_files))
-        << error_msg;
-
-    bcp.push_back(src);
-    bcp_locations.push_back(src);
-    for (std::unique_ptr<const DexFile>& df : new_dex_files) {
-      dex_files.push_back(df.get());
-      opened_dex_files.push_back(std::move(df));
-    }
-
-    ASSERT_TRUE(create_and_verify()) << error_msg;
-  }
-}
-
 template <bool kImage, bool kRelocate>
 class ImageSpaceLoadingTest : public CommonRuntimeTest {
  protected:
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 7c61c5a..389479c 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -20,6 +20,7 @@
 
 #include <memory>
 #include <sstream>
+#include <vector>
 
 #include "android-base/file.h"
 #include "android-base/logging.h"
@@ -46,6 +47,7 @@
 #include "gc/space/image_space.h"
 #include "image.h"
 #include "oat.h"
+#include "oat_file_assistant_context.h"
 #include "runtime.h"
 #include "scoped_thread_state_change-inl.h"
 #include "vdex_file.h"
@@ -53,7 +55,8 @@
 
 namespace art {
 
-using android::base::StringPrintf;
+using ::android::base::ConsumePrefix;
+using ::android::base::StringPrintf;
 
 static constexpr const char* kAnonymousDexPrefix = "Anonymous-DexFile@";
 static constexpr const char* kVdexExtension = ".vdex";
@@ -86,23 +89,23 @@
                                    ClassLoaderContext* context,
                                    bool load_executable,
                                    bool only_load_trusted_executable,
-                                   std::unique_ptr<RuntimeOptions> runtime_options)
+                                   OatFileAssistantContext* ofa_context)
     : OatFileAssistant(dex_location,
                        isa,
                        context,
                        load_executable,
                        only_load_trusted_executable,
-                       std::move(runtime_options),
-                       /*vdex_fd=*/ -1,
-                       /*oat_fd=*/ -1,
-                       /*zip_fd=*/ -1) {}
+                       ofa_context,
+                       /*vdex_fd=*/-1,
+                       /*oat_fd=*/-1,
+                       /*zip_fd=*/-1) {}
 
 OatFileAssistant::OatFileAssistant(const char* dex_location,
                                    const InstructionSet isa,
                                    ClassLoaderContext* context,
                                    bool load_executable,
                                    bool only_load_trusted_executable,
-                                   std::unique_ptr<RuntimeOptions> runtime_options,
+                                   OatFileAssistantContext* ofa_context,
                                    int vdex_fd,
                                    int oat_fd,
                                    int zip_fd)
@@ -110,13 +113,12 @@
       isa_(isa),
       load_executable_(load_executable),
       only_load_trusted_executable_(only_load_trusted_executable),
-      runtime_options_(std::move(runtime_options)),
-      odex_(this, /*is_oat_location=*/ false),
-      oat_(this, /*is_oat_location=*/ true),
-      vdex_for_odex_(this, /*is_oat_location=*/ false),
-      vdex_for_oat_(this, /*is_oat_location=*/ true),
-      dm_for_odex_(this, /*is_oat_location=*/ false),
-      dm_for_oat_(this, /*is_oat_location=*/ true),
+      odex_(this, /*is_oat_location=*/false),
+      oat_(this, /*is_oat_location=*/true),
+      vdex_for_odex_(this, /*is_oat_location=*/false),
+      vdex_for_oat_(this, /*is_oat_location=*/true),
+      dm_for_odex_(this, /*is_oat_location=*/false),
+      dm_for_oat_(this, /*is_oat_location=*/true),
       zip_fd_(zip_fd) {
   CHECK(dex_location != nullptr) << "OatFileAssistant: null dex location";
   CHECK_IMPLIES(load_executable, context != nullptr) << "Loading executable without a context";
@@ -147,24 +149,11 @@
     load_executable_ = false;
   }
 
-  if (runtime_options_ == nullptr) {
+  if (ofa_context == nullptr) {
     CHECK(runtime != nullptr) << "runtime_options is not provided, and no active runtime is found.";
-
-    runtime_options_ = std::make_unique<RuntimeOptions>(RuntimeOptions{
-        .image_locations = runtime->GetImageLocations(),
-        .boot_class_path = runtime->GetBootClassPath(),
-        .boot_class_path_locations = runtime->GetBootClassPathLocations(),
-        .boot_class_path_fds = &runtime->GetBootClassPathFds(),
-        .deny_art_apex_data_files = runtime->DenyArtApexDataFiles(),
-        .apex_versions = runtime->GetApexVersions(),
-    });
-
-    if (isa == kRuntimeISA) {
-      // For the fast path that checks the OAT files against the runtime boot classpath checksums
-      // and boot classpath locations.
-      cached_boot_class_path_ = android::base::Join(runtime->GetBootClassPathLocations(), ":");
-      cached_boot_class_path_checksums_ = runtime->GetBootClassPathChecksums();
-    }
+    ofa_context_ = std::make_unique<OatFileAssistantContext>(runtime);
+  } else {
+    ofa_context_ = ofa_context;
   }
 
   if (runtime == nullptr) {
@@ -201,7 +190,7 @@
     std::string oat_file_name;
     if (DexLocationToOatFilename(dex_location_,
                                  isa_,
-                                 runtime_options_->deny_art_apex_data_files,
+                                 GetRuntimeOptions().deny_art_apex_data_files,
                                  &oat_file_name,
                                  &error_msg)) {
       oat_.Reset(oat_file_name, /*use_fd=*/ false);
@@ -240,7 +229,7 @@
     const std::string& context_str,
     bool load_executable,
     bool only_load_trusted_executable,
-    std::unique_ptr<RuntimeOptions> runtime_options,
+    OatFileAssistantContext* ofa_context,
     /*out*/ std::unique_ptr<ClassLoaderContext>* context,
     /*out*/ std::string* error_msg) {
   InstructionSet isa = GetInstructionSetFromString(isa_str.c_str());
@@ -270,7 +259,7 @@
                                                       tmp_context.get(),
                                                       load_executable,
                                                       only_load_trusted_executable,
-                                                      std::move(runtime_options));
+                                                      ofa_context);
 
   *context = std::move(tmp_context);
   return assistant;
@@ -285,7 +274,8 @@
   // specified by the user. This is okay, because the boot class path should
   // be the same for all ISAs.
   // TODO: Can we verify the boot class path is the same for all ISAs?
-  for (const std::string& boot_class_path_location : runtime_options_->boot_class_path_locations) {
+  for (const std::string& boot_class_path_location :
+       GetRuntimeOptions().boot_class_path_locations) {
     if (boot_class_path_location == dex_location_) {
       VLOG(oat) << "Dex location " << dex_location_ << " is in boot class path";
       return true;
@@ -567,7 +557,7 @@
       return kOatBootImageOutOfDate;
     }
     if (!gc::space::ImageSpace::ValidateApexVersions(
-            file, runtime_options_->apex_versions, &error_msg)) {
+            file, GetOatFileAssistantContext()->GetApexVersions(), &error_msg)) {
       VLOG(oat) << error_msg;
       return kOatBootImageOutOfDate;
     }
@@ -578,7 +568,8 @@
   // zip_file_only_contains_uncompressed_dex_ is only set during fetching the dex checksums.
   DCHECK(required_dex_checksums_attempted_);
   if (only_load_trusted_executable_ &&
-      !LocationIsTrusted(file.GetLocation(), !runtime_options_->deny_art_apex_data_files) &&
+      !LocationIsTrusted(file.GetLocation(),
+                         !GetRuntimeOptions().deny_art_apex_data_files) &&
       file.ContainsDexCode() && zip_file_only_contains_uncompressed_dex_) {
     LOG(ERROR) << "Not loading "
                << dex_location_
@@ -778,6 +769,105 @@
   return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr;
 }
 
+bool OatFileAssistant::ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context,
+                                                      InstructionSet isa,
+                                                      std::string_view oat_checksums,
+                                                      std::string_view oat_boot_class_path,
+                                                      /*out*/ std::string* error_msg) {
+  const std::vector<std::string>& bcp_locations =
+      ofa_context->GetRuntimeOptions().boot_class_path_locations;
+
+  if (oat_checksums.empty() || oat_boot_class_path.empty()) {
+    *error_msg = oat_checksums.empty() ? "Empty checksums" : "Empty boot class path";
+    return false;
+  }
+
+  size_t oat_bcp_size = gc::space::ImageSpace::CheckAndCountBCPComponents(
+      oat_boot_class_path, ArrayRef<const std::string>(bcp_locations), error_msg);
+  DCHECK_LE(oat_bcp_size, bcp_locations.size());
+  if (oat_bcp_size == static_cast<size_t>(-1)) {
+    DCHECK(!error_msg->empty());
+    return false;
+  }
+
+  size_t bcp_index = 0;
+  size_t boot_image_index = 0;
+  bool found_d = false;
+
+  while (bcp_index < oat_bcp_size) {
+    static_assert(gc::space::ImageSpace::kImageChecksumPrefix == 'i', "Format prefix check");
+    static_assert(gc::space::ImageSpace::kDexFileChecksumPrefix == 'd', "Format prefix check");
+    if (StartsWith(oat_checksums, "i") && !found_d) {
+      const std::vector<OatFileAssistantContext::BootImageInfo>& boot_image_info_list =
+          ofa_context->GetBootImageInfoList(isa);
+      if (boot_image_index >= boot_image_info_list.size()) {
+        *error_msg = StringPrintf("Missing boot image for %s, remaining checksums: %s",
+                                  bcp_locations[bcp_index].c_str(),
+                                  std::string(oat_checksums).c_str());
+        return false;
+      }
+
+      const OatFileAssistantContext::BootImageInfo& boot_image_info =
+          boot_image_info_list[boot_image_index];
+      if (!ConsumePrefix(&oat_checksums, boot_image_info.checksum)) {
+        *error_msg = StringPrintf("Image checksum mismatch, expected %s to start with %s",
+                                  std::string(oat_checksums).c_str(),
+                                  boot_image_info.checksum.c_str());
+        return false;
+      }
+
+      bcp_index += boot_image_info.component_count;
+      boot_image_index++;
+    } else if (StartsWith(oat_checksums, "d")) {
+      found_d = true;
+      const std::vector<std::string>* bcp_checksums =
+          ofa_context->GetBcpChecksums(bcp_index, error_msg);
+      if (bcp_checksums == nullptr) {
+        return false;
+      }
+      oat_checksums.remove_prefix(1u);
+      for (const std::string& checksum : *bcp_checksums) {
+        if (!ConsumePrefix(&oat_checksums, checksum)) {
+          *error_msg = StringPrintf(
+              "Dex checksum mismatch for bootclasspath file %s, expected %s to start with %s",
+              bcp_locations[bcp_index].c_str(),
+              std::string(oat_checksums).c_str(),
+              checksum.c_str());
+          return false;
+        }
+      }
+
+      bcp_index++;
+    } else {
+      *error_msg = StringPrintf("Unexpected checksums, expected %s to start with %s",
+                                std::string(oat_checksums).c_str(),
+                                found_d ? "'d'" : "'i' or 'd'");
+      return false;
+    }
+
+    if (bcp_index < oat_bcp_size) {
+      if (!ConsumePrefix(&oat_checksums, ":")) {
+        if (oat_checksums.empty()) {
+          *error_msg =
+              StringPrintf("Checksum too short, missing %zu components", oat_bcp_size - bcp_index);
+        } else {
+          *error_msg = StringPrintf("Missing ':' separator at start of %s",
+                                    std::string(oat_checksums).c_str());
+        }
+        return false;
+      }
+    }
+  }
+
+  if (!oat_checksums.empty()) {
+    *error_msg =
+        StringPrintf("Checksum too long, unexpected tail: %s", std::string(oat_checksums).c_str());
+    return false;
+  }
+
+  return true;
+}
+
 bool OatFileAssistant::ValidateBootClassPathChecksums(const OatFile& oat_file) {
   // Get the checksums and the BCP from the oat file.
   const char* oat_boot_class_path_checksums =
@@ -787,61 +877,24 @@
   if (oat_boot_class_path_checksums == nullptr || oat_boot_class_path == nullptr) {
     return false;
   }
-  std::string_view oat_boot_class_path_checksums_view(oat_boot_class_path_checksums);
-  std::string_view oat_boot_class_path_view(oat_boot_class_path);
-  if (oat_boot_class_path_view == cached_boot_class_path_ &&
-      oat_boot_class_path_checksums_view == cached_boot_class_path_checksums_) {
-    return true;
-  }
 
   std::string error_msg;
-  bool result = gc::space::ImageSpace::VerifyBootClassPathChecksums(
-      oat_boot_class_path_checksums_view,
-      oat_boot_class_path_view,
-      ArrayRef<const std::string>(runtime_options_->image_locations),
-      ArrayRef<const std::string>(runtime_options_->boot_class_path_locations),
-      ArrayRef<const std::string>(runtime_options_->boot_class_path),
-      runtime_options_->boot_class_path_fds != nullptr ?
-          ArrayRef<const int>(*runtime_options_->boot_class_path_fds) :
-          ArrayRef<const int>(),
-      isa_,
-      runtime_options_->apex_versions,
-      &error_msg);
+  bool result = ValidateBootClassPathChecksums(GetOatFileAssistantContext(),
+                                               isa_,
+                                               oat_boot_class_path_checksums,
+                                               oat_boot_class_path,
+                                               &error_msg);
   if (!result) {
     VLOG(oat) << "Failed to verify checksums of oat file " << oat_file.GetLocation()
         << " error: " << error_msg;
     return false;
   }
 
-  // This checksum has been validated, so save it.
-  cached_boot_class_path_ = oat_boot_class_path_view;
-  cached_boot_class_path_checksums_ = oat_boot_class_path_checksums_view;
   return true;
 }
 
 bool OatFileAssistant::IsPrimaryBootImageUsable() {
-  if (cached_is_boot_image_usable_.has_value()) {
-    return cached_is_boot_image_usable_.value();
-  }
-
-  if (runtime_options_->image_locations.empty()) {
-    return false;
-  }
-
-  std::string ignored_error_msg;
-  // Only verify the primary boot image.
-  cached_is_boot_image_usable_ = gc::space::ImageSpace::VerifyBootImages(
-      ArrayRef<const std::string>(runtime_options_->image_locations)
-          .SubArray(/*pos=*/0u, /*length=*/1u),
-      ArrayRef<const std::string>(runtime_options_->boot_class_path_locations),
-      ArrayRef<const std::string>(runtime_options_->boot_class_path),
-      runtime_options_->boot_class_path_fds != nullptr ?
-          ArrayRef<const int>(*runtime_options_->boot_class_path_fds) :
-          ArrayRef<const int>(),
-      isa_,
-      runtime_options_->apex_versions,
-      &ignored_error_msg);
-  return cached_is_boot_image_usable_.value();
+  return !GetOatFileAssistantContext()->GetBootImageInfoList(isa_).empty();
 }
 
 OatFileAssistant::OatFileInfo& OatFileAssistant::GetBestInfo() {
@@ -999,7 +1052,7 @@
   }
 
   if (LocationIsOnArtApexData(filename_) &&
-      oat_file_assistant_->runtime_options_->deny_art_apex_data_files) {
+      oat_file_assistant_->GetRuntimeOptions().deny_art_apex_data_files) {
     LOG(WARNING) << "OatFileAssistant rejected file " << filename_
                  << ": ART apexdata is untrusted.";
     return nullptr;
@@ -1206,7 +1259,7 @@
                                              InstructionSet isa,
                                              std::string* out_compilation_filter,
                                              std::string* out_compilation_reason,
-                                             std::unique_ptr<RuntimeOptions> runtime_options) {
+                                             OatFileAssistantContext* ofa_context) {
   // It may not be possible to load an oat file executable (e.g., selinux restrictions). Load
   // non-executable and check the status manually.
   OatFileAssistant oat_file_assistant(filename.c_str(),
@@ -1214,7 +1267,7 @@
                                       /*context=*/nullptr,
                                       /*load_executable=*/false,
                                       /*only_load_trusted_executable=*/false,
-                                      std::move(runtime_options));
+                                      ofa_context);
   std::string out_odex_location;  // unused
   std::string out_odex_status;  // unused
   oat_file_assistant.GetOptimizationStatus(
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 9ce215b..ce069d2 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -22,6 +22,7 @@
 #include <optional>
 #include <sstream>
 #include <string>
+#include <variant>
 
 #include "arch/instruction_set.h"
 #include "base/compiler_filter.h"
@@ -30,6 +31,7 @@
 #include "base/unix_file/fd_file.h"
 #include "class_loader_context.h"
 #include "oat_file.h"
+#include "oat_file_assistant_context.h"
 
 namespace art {
 
@@ -90,26 +92,6 @@
     kOatUpToDate,
   };
 
-  // Options that a runtime would take if the OAT file were going to be loaded by the runtime.
-  // Note that the struct only keeps references, so the caller must keep the objects alive during
-  // the lifetime of OatFileAssistant.
-  struct RuntimeOptions {
-    // Required. See `-Ximage`.
-    const std::vector<std::string>& image_locations;
-    // Required. See `-Xbootclasspath`.
-    const std::vector<std::string>& boot_class_path;
-    // Required. See `-Xbootclasspath-locations`.
-    const std::vector<std::string>& boot_class_path_locations;
-    // Optional. See `-Xbootclasspathfds`.
-    const std::vector<int>* const boot_class_path_fds = nullptr;
-    // Optional. See `-Xdeny-art-apex-data-files`.
-    const bool deny_art_apex_data_files = false;
-    // Required. A string that represents the apex versions of boot classpath jars. See
-    // `Runtime::apex_versions_` for the encoding format. Can be obtained from
-    // `Runtime::GetApexVersions(boot_class_path_locations)`.
-    const std::string& apex_versions;
-  };
-
   // A bit field to represent the conditions where dexopt should be performed.
   struct DexOptTrigger {
     // Dexopt should be performed if the target compiler filter is better than the current compiler
@@ -178,7 +160,7 @@
                    ClassLoaderContext* context,
                    bool load_executable,
                    bool only_load_trusted_executable = false,
-                   std::unique_ptr<RuntimeOptions> runtime_options = nullptr);
+                   OatFileAssistantContext* ofa_context = nullptr);
 
   // Similar to this(const char*, const InstructionSet, bool), however, if a valid zip_fd is
   // provided, vdex, oat, and zip files will be read from vdex_fd, oat_fd and zip_fd respectively.
@@ -188,7 +170,7 @@
                    ClassLoaderContext* context,
                    bool load_executable,
                    bool only_load_trusted_executable,
-                   std::unique_ptr<RuntimeOptions> runtime_options,
+                   OatFileAssistantContext* ofa_context,
                    int vdex_fd,
                    int oat_fd,
                    int zip_fd);
@@ -203,7 +185,7 @@
       const std::string& context_str,
       bool load_executable,
       bool only_load_trusted_executable,
-      std::unique_ptr<RuntimeOptions> runtime_options,
+      OatFileAssistantContext* ofa_context,
       /*out*/ std::unique_ptr<ClassLoaderContext>* context,
       /*out*/ std::string* error_msg);
 
@@ -277,7 +259,7 @@
                                     InstructionSet isa,
                                     std::string* out_compilation_filter,
                                     std::string* out_compilation_reason,
-                                    std::unique_ptr<RuntimeOptions> runtime_options = nullptr);
+                                    OatFileAssistantContext* ofa_context = nullptr);
 
   // Open and returns an image space associated with the oat file.
   static std::unique_ptr<gc::space::ImageSpace> OpenImageSpace(const OatFile* oat_file);
@@ -374,6 +356,16 @@
 
   bool ClassLoaderContextIsOkay(const OatFile& oat_file) const;
 
+  // Validates the boot class path checksum of an OatFile.
+  bool ValidateBootClassPathChecksums(const OatFile& oat_file);
+
+  // Validates the given bootclasspath and bootclasspath checksums found in an oat header.
+  static bool ValidateBootClassPathChecksums(OatFileAssistantContext* ofa_context,
+                                             InstructionSet isa,
+                                             std::string_view oat_checksums,
+                                             std::string_view oat_boot_class_path,
+                                             /*out*/ std::string* error_msg);
+
  private:
   class OatFileInfo {
    public:
@@ -502,9 +494,6 @@
   // dex_location_ dex file.
   const std::vector<uint32_t>* GetRequiredDexChecksums();
 
-  // Validates the boot class path checksum of an OatFile.
-  bool ValidateBootClassPathChecksums(const OatFile& oat_file);
-
   // Returns whether there is at least one boot image usable.
   bool IsPrimaryBootImageUsable();
 
@@ -515,6 +504,23 @@
                                  bool profile_changed,
                                  bool downgrade);
 
+  // Returns the pointer to the owned or unowned instance of OatFileAssistantContext.
+  OatFileAssistantContext* GetOatFileAssistantContext() {
+    if (std::holds_alternative<OatFileAssistantContext*>(ofa_context_)) {
+      return std::get<OatFileAssistantContext*>(ofa_context_);
+    } else {
+      return std::get<std::unique_ptr<OatFileAssistantContext>>(ofa_context_).get();
+    }
+  }
+
+  // The runtime options taken from the active runtime or the input.
+  //
+  // All member functions should get runtime options from this variable rather than referencing the
+  // active runtime. This is to allow OatFileAssistant to function without an active runtime.
+  const OatFileAssistantContext::RuntimeOptions& GetRuntimeOptions() {
+    return GetOatFileAssistantContext()->GetRuntimeOptions();
+  }
+
   std::string dex_location_;
 
   ClassLoaderContext* context_;
@@ -535,12 +541,6 @@
   // Will be set during GetRequiredDexChecksums.
   bool zip_file_only_contains_uncompressed_dex_ = true;
 
-  // The runtime options taken from the active runtime or the input.
-  //
-  // All member functions should get runtime options from this variable rather than referencing the
-  // active runtime. This is to allow OatFileAssistant to function without an active runtime.
-  std::unique_ptr<RuntimeOptions> runtime_options_;
-
   // Cached value of the required dex checksums.
   // This should be accessed only by the GetRequiredDexChecksums() method.
   std::vector<uint32_t> cached_required_dex_checksums_;
@@ -568,9 +568,8 @@
   // File descriptor corresponding to apk, dex file, or zip.
   int zip_fd_;
 
-  std::string cached_boot_class_path_;
-  std::string cached_boot_class_path_checksums_;
-  std::optional<bool> cached_is_boot_image_usable_ = std::nullopt;
+  // Owned or unowned instance of OatFileAssistantContext.
+  std::variant<std::unique_ptr<OatFileAssistantContext>, OatFileAssistantContext*> ofa_context_;
 
   friend class OatFileAssistantTest;
 
diff --git a/runtime/oat_file_assistant_context.cc b/runtime/oat_file_assistant_context.cc
new file mode 100644
index 0000000..d282d03
--- /dev/null
+++ b/runtime/oat_file_assistant_context.cc
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2022 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 <memory>
+#include <string>
+#include <vector>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+#include "arch/instruction_set.h"
+#include "base/array_ref.h"
+#include "base/logging.h"
+#include "class_linker.h"
+#include "dex/art_dex_file_loader.h"
+#include "gc/heap.h"
+#include "gc/space/image_space.h"
+#include "oat_file_assistant_context.h"
+
+namespace art {
+
+using ::android::base::StringPrintf;
+using ::art::gc::space::ImageSpace;
+
+OatFileAssistantContext::OatFileAssistantContext(
+    std::unique_ptr<OatFileAssistantContext::RuntimeOptions> runtime_options)
+    : runtime_options_(std::move(runtime_options)) {
+  DCHECK_EQ(runtime_options_->boot_class_path.size(),
+            runtime_options_->boot_class_path_locations.size());
+  DCHECK_IMPLIES(
+      runtime_options_->boot_class_path_fds != nullptr,
+      runtime_options_->boot_class_path.size() == runtime_options_->boot_class_path_fds->size());
+}
+
+OatFileAssistantContext::OatFileAssistantContext(Runtime* runtime)
+    : OatFileAssistantContext(std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+          OatFileAssistantContext::RuntimeOptions{
+              .image_locations = runtime->GetImageLocations(),
+              .boot_class_path = runtime->GetBootClassPath(),
+              .boot_class_path_locations = runtime->GetBootClassPathLocations(),
+              .boot_class_path_fds = !runtime->GetBootClassPathFds().empty() ?
+                                         &runtime->GetBootClassPathFds() :
+                                         nullptr,
+              .deny_art_apex_data_files = runtime->DenyArtApexDataFiles(),
+          })) {
+  // Fetch boot image info from the runtime.
+  std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[kRuntimeISA];
+  for (const ImageSpace* image_space : runtime->GetHeap()->GetBootImageSpaces()) {
+    // We only need the checksum of the first component for each boot image. They are in image
+    // spaces that have a non-zero component count.
+    if (image_space->GetComponentCount() > 0) {
+      BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
+      boot_image_info.component_count = image_space->GetComponentCount();
+      ImageSpace::AppendImageChecksum(image_space->GetComponentCount(),
+                                      image_space->GetImageHeader().GetImageChecksum(),
+                                      &boot_image_info.checksum);
+    }
+  }
+
+  // Fetch BCP checksums from the runtime.
+  size_t bcp_index = 0;
+  std::vector<std::string>* current_bcp_checksums = nullptr;
+  for (const DexFile* dex_file : runtime->GetClassLinker()->GetBootClassPath()) {
+    if (!DexFileLoader::IsMultiDexLocation(dex_file->GetLocation().c_str())) {
+      DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
+      current_bcp_checksums = &bcp_checksums_by_index_[bcp_index++];
+    }
+    DCHECK_NE(current_bcp_checksums, nullptr);
+    current_bcp_checksums->push_back(StringPrintf("/%08x", dex_file->GetLocationChecksum()));
+  }
+  DCHECK_EQ(bcp_index, runtime_options_->boot_class_path.size());
+
+  // Fetch APEX versions from the runtime.
+  apex_versions_ = runtime->GetApexVersions();
+}
+
+const OatFileAssistantContext::RuntimeOptions& OatFileAssistantContext::GetRuntimeOptions() const {
+  return *runtime_options_;
+}
+
+const std::vector<OatFileAssistantContext::BootImageInfo>&
+OatFileAssistantContext::GetBootImageInfoList(InstructionSet isa) {
+  if (auto it = boot_image_info_list_by_isa_.find(isa); it != boot_image_info_list_by_isa_.end()) {
+    return it->second;
+  }
+
+  ImageSpace::BootImageLayout layout(
+      ArrayRef<const std::string>(runtime_options_->image_locations),
+      ArrayRef<const std::string>(runtime_options_->boot_class_path),
+      ArrayRef<const std::string>(runtime_options_->boot_class_path_locations),
+      runtime_options_->boot_class_path_fds != nullptr ?
+          ArrayRef<const int>(*runtime_options_->boot_class_path_fds) :
+          ArrayRef<const int>(),
+      /*boot_class_path_image_fds=*/ArrayRef<const int>(),
+      /*boot_class_path_vdex_fds=*/ArrayRef<const int>(),
+      /*boot_class_path_oat_fds=*/ArrayRef<const int>(),
+      &GetApexVersions());
+
+  std::string error_msg;
+  if (!layout.LoadFromSystem(isa, /*allow_in_memory_compilation=*/false, &error_msg)) {
+    // At this point, `layout` contains a subset of boot images that can be loaded.
+    VLOG(oat) << "Some error occurred when loading boot images for oat file validation: "
+              << error_msg;
+  }
+
+  std::vector<BootImageInfo>& boot_image_info_list = boot_image_info_list_by_isa_[isa];
+  for (const ImageSpace::BootImageLayout::ImageChunk& chunk : layout.GetChunks()) {
+    BootImageInfo& boot_image_info = boot_image_info_list.emplace_back();
+    boot_image_info.component_count = chunk.component_count;
+    ImageSpace::AppendImageChecksum(
+        chunk.component_count, chunk.checksum, &boot_image_info.checksum);
+  }
+  return boot_image_info_list;
+}
+
+const std::vector<std::string>* OatFileAssistantContext::GetBcpChecksums(size_t bcp_index,
+                                                                         std::string* error_msg) {
+  DCHECK_LT(bcp_index, runtime_options_->boot_class_path.size());
+
+  if (auto it = bcp_checksums_by_index_.find(bcp_index); it != bcp_checksums_by_index_.end()) {
+    return &it->second;
+  }
+
+  std::vector<uint32_t> checksums;
+  std::vector<std::string> dex_locations;
+  ArtDexFileLoader dex_file_loader;
+  if (!dex_file_loader.GetMultiDexChecksums(
+          runtime_options_->boot_class_path[bcp_index].c_str(),
+          &checksums,
+          &dex_locations,
+          error_msg,
+          runtime_options_->boot_class_path_fds != nullptr ?
+              (*runtime_options_->boot_class_path_fds)[bcp_index] :
+              -1)) {
+    return nullptr;
+  }
+
+  DCHECK(!checksums.empty());
+  std::vector<std::string>& bcp_checksums = bcp_checksums_by_index_[bcp_index];
+  for (uint32_t checksum : checksums) {
+    bcp_checksums.push_back(StringPrintf("/%08x", checksum));
+  }
+  return &bcp_checksums;
+}
+
+const std::string& OatFileAssistantContext::GetApexVersions() {
+  if (apex_versions_.has_value()) {
+    return apex_versions_.value();
+  }
+
+  apex_versions_ = Runtime::GetApexVersions(
+      ArrayRef<const std::string>(runtime_options_->boot_class_path_locations));
+  return apex_versions_.value();
+}
+
+}  // namespace art
diff --git a/runtime/oat_file_assistant_context.h b/runtime/oat_file_assistant_context.h
new file mode 100644
index 0000000..3288dc0
--- /dev/null
+++ b/runtime/oat_file_assistant_context.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#ifndef ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
+#define ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
+
+#include <optional>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "arch/instruction_set.h"
+#include "runtime.h"
+
+namespace art {
+
+// A helper class for OatFileAssistant that fetches and caches information including boot image
+// checksums, bootclasspath checksums, and APEX versions. The same instance can be reused across
+// OatFileAssistant calls on different dex files for different instruction sets.
+class OatFileAssistantContext {
+ public:
+  // Options that a runtime would take.
+  // Note that the struct only keeps references, so the caller must keep the objects alive during
+  // the lifetime of OatFileAssistant.
+  struct RuntimeOptions {
+    // Required. See `-Ximage`.
+    const std::vector<std::string>& image_locations;
+    // Required. See `-Xbootclasspath`.
+    const std::vector<std::string>& boot_class_path;
+    // Required. See `-Xbootclasspath-locations`.
+    const std::vector<std::string>& boot_class_path_locations;
+    // Optional. See `-Xbootclasspathfds`.
+    const std::vector<int>* const boot_class_path_fds = nullptr;
+    // Optional. See `-Xdeny-art-apex-data-files`.
+    const bool deny_art_apex_data_files = false;
+  };
+
+  // Information about a boot image.
+  struct BootImageInfo {
+    // Number of BCP jars covered by the boot image.
+    size_t component_count;
+    // Checksum of the boot image. The format is "i;<component_count>/<checksum_in_8_digit_hex>"
+    std::string checksum;
+  };
+
+  // Constructs OatFileAssistantContext from runtime options. Does not fetch information on
+  // construction. Information will be fetched from disk when needed.
+  explicit OatFileAssistantContext(std::unique_ptr<RuntimeOptions> runtime_options);
+  // Constructs OatFileAssistantContext from a runtime instance. Fetches as much information as
+  // possible from the runtime. The rest information will be fetched from disk when needed.
+  explicit OatFileAssistantContext(Runtime* runtime);
+  // Returns runtime options.
+  const RuntimeOptions& GetRuntimeOptions() const;
+  // Returns information about the boot image of the given instruction set.
+  const std::vector<BootImageInfo>& GetBootImageInfoList(InstructionSet isa);
+  // Returns the checksums of the dex files in the BCP jar at the given index, or nullptr on error.
+  // The format of each checksum is "/<checksum_in_8_digit_hex>".
+  const std::vector<std::string>* GetBcpChecksums(size_t bcp_index, std::string* error_msg);
+  // Returns a string that represents the apex versions of boot classpath jars. See
+  // `Runtime::apex_versions_` for the encoding format.
+  const std::string& GetApexVersions();
+
+ private:
+  std::unique_ptr<RuntimeOptions> runtime_options_;
+  std::unordered_map<InstructionSet, std::vector<BootImageInfo>> boot_image_info_list_by_isa_;
+  std::unordered_map<size_t, std::vector<std::string>> bcp_checksums_by_index_;
+  std::optional<std::string> apex_versions_;
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_OAT_FILE_ASSISTANT_CONTEXT_H_
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 13400f4..2904ecb 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -21,6 +21,7 @@
 #include <sys/param.h>
 
 #include <functional>
+#include <iterator>
 #include <memory>
 #include <string>
 #include <type_traits>
@@ -28,19 +29,20 @@
 
 #include "android-base/scopeguard.h"
 #include "android-base/strings.h"
+#include "arch/instruction_set.h"
 #include "art_field-inl.h"
 #include "base/os.h"
 #include "base/utils.h"
-#include "class_linker-inl.h"
+#include "class_linker.h"
 #include "class_loader_context.h"
 #include "common_runtime_test.h"
 #include "dexopt_test.h"
-#include "hidden_api.h"
 #include "oat.h"
 #include "oat_file.h"
+#include "oat_file_assistant_context.h"
 #include "oat_file_manager.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread-current-inl.h"
+#include "scoped_thread_state_change.h"
+#include "thread.h"
 
 namespace art {
 
@@ -52,6 +54,7 @@
   void SetUp() override {
     DexoptTest::SetUp();
     with_runtime_ = GetParam();
+    ofa_context_ = CreateOatFileAssistantContext();
   }
 
   // Verifies all variants of `GetOptimizationStatus`.
@@ -82,7 +85,7 @@
                                               kRuntimeISA,
                                               &compilation_filter1,
                                               &compilation_reason1,
-                                              MaybeCreateRuntimeOptions());
+                                              MaybeGetOatFileAssistantContext());
 
       ASSERT_EQ(expected_filter_name, compilation_filter1);
       ASSERT_EQ(expected_reason, compilation_reason1);
@@ -104,13 +107,22 @@
     ASSERT_EQ(expected_odex_status, odex_status3);
   }
 
-  void InsertNewBootClasspathEntry() {
-    std::string extra_dex_filename = GetMultiDexSrc1();
-    Runtime* runtime = Runtime::Current();
-    runtime->boot_class_path_.push_back(extra_dex_filename);
-    if (!runtime->boot_class_path_locations_.empty()) {
-      runtime->boot_class_path_locations_.push_back(extra_dex_filename);
+  bool InsertNewBootClasspathEntry(const std::string& src, std::string* error_msg) {
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    ArtDexFileLoader dex_file_loader;
+    if (!dex_file_loader.Open(src.c_str(),
+                              src,
+                              /*verify=*/true,
+                              /*verify_checksum=*/false,
+                              error_msg,
+                              &dex_files)) {
+      return false;
     }
+
+    runtime_->AppendToBootClassPath(src, src, dex_files);
+    std::move(dex_files.begin(), dex_files.end(), std::back_inserter(opened_dex_files_));
+
+    return true;
   }
 
   // Verifies the current version of `GetDexOptNeeded` (called from artd).
@@ -165,20 +177,22 @@
         [this]() { Runtime::TestOnlySetCurrent(runtime_.get()); });
   }
 
-  // Returns runtime options if `with_runtime_` is false.
-  std::unique_ptr<OatFileAssistant::RuntimeOptions> MaybeCreateRuntimeOptions() {
-    if (with_runtime_) {
-      return nullptr;
-    } else {
-      return std::make_unique<OatFileAssistant::RuntimeOptions>(OatFileAssistant::RuntimeOptions{
-          .image_locations = runtime_->GetImageLocations(),
-          .boot_class_path = runtime_->GetBootClassPath(),
-          .boot_class_path_locations = runtime_->GetBootClassPathLocations(),
-          .boot_class_path_fds = &runtime_->GetBootClassPathFds(),
-          .deny_art_apex_data_files = runtime_->DenyArtApexDataFiles(),
-          .apex_versions = runtime_->GetApexVersions(),
-      });
-    }
+  std::unique_ptr<OatFileAssistantContext> CreateOatFileAssistantContext() {
+    return std::make_unique<OatFileAssistantContext>(
+        std::make_unique<OatFileAssistantContext::RuntimeOptions>(
+            OatFileAssistantContext::RuntimeOptions{
+                .image_locations = runtime_->GetImageLocations(),
+                .boot_class_path = runtime_->GetBootClassPath(),
+                .boot_class_path_locations = runtime_->GetBootClassPathLocations(),
+                .boot_class_path_fds = !runtime_->GetBootClassPathFds().empty() ?
+                                           &runtime_->GetBootClassPathFds() :
+                                           nullptr,
+                .deny_art_apex_data_files = runtime_->DenyArtApexDataFiles(),
+            }));
+  }
+
+  OatFileAssistantContext* MaybeGetOatFileAssistantContext() {
+    return with_runtime_ ? nullptr : ofa_context_.get();
   }
 
   // A helper function to create OatFileAssistant with some default arguments.
@@ -193,7 +207,7 @@
                             context != nullptr ? context : default_context_.get(),
                             load_executable,
                             /*only_load_trusted_executable=*/false,
-                            MaybeCreateRuntimeOptions(),
+                            MaybeGetOatFileAssistantContext(),
                             vdex_fd,
                             oat_fd,
                             zip_fd);
@@ -203,6 +217,8 @@
   bool with_runtime_;
   const OatFileAssistant::DexOptTrigger default_trigger_{.targetFilterIsBetter = true,
                                                          .primaryBootImageBecomesUsable = true};
+  std::unique_ptr<OatFileAssistantContext> ofa_context_;
+  std::vector<std::unique_ptr<const DexFile>> opened_dex_files_;
 };
 
 class ScopedNonWritable {
@@ -462,7 +478,8 @@
   GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, "install");
 
   // Insert an extra dex file to the boot class path.
-  InsertNewBootClasspathEntry();
+  std::string error_msg;
+  ASSERT_TRUE(InsertNewBootClasspathEntry(GetMultiDexSrc1(), &error_msg)) << error_msg;
 
   auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
 
@@ -2354,7 +2371,7 @@
                                default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
                                /*load_executable=*/false,
                                /*only_load_trusted_executable=*/true,
-                               MaybeCreateRuntimeOptions(),
+                               MaybeGetOatFileAssistantContext(),
                                &context,
                                &error_msg);
   ASSERT_NE(oat_file_assistant, nullptr);
@@ -2378,7 +2395,7 @@
                                      default_context_->EncodeContextForDex2oat(/*base_dir=*/""),
                                      /*load_executable=*/false,
                                      /*only_load_trusted_executable=*/true,
-                                     MaybeCreateRuntimeOptions(),
+                                     MaybeGetOatFileAssistantContext(),
                                      &context,
                                      &error_msg),
             nullptr);
@@ -2400,7 +2417,7 @@
                                      /*context_str=*/"foo",
                                      /*load_executable=*/false,
                                      /*only_load_trusted_executable=*/true,
-                                     MaybeCreateRuntimeOptions(),
+                                     MaybeGetOatFileAssistantContext(),
                                      &context,
                                      &error_msg),
             nullptr);
@@ -2427,7 +2444,7 @@
                                      /*context_str=*/"PCL[" + context_location + "]",
                                      /*load_executable=*/false,
                                      /*only_load_trusted_executable=*/true,
-                                     MaybeCreateRuntimeOptions(),
+                                     MaybeGetOatFileAssistantContext(),
                                      &context,
                                      &error_msg),
             nullptr);
@@ -2436,6 +2453,30 @@
                 "' with context 'PCL[" + context_location + "]'");
 }
 
+// Verifies that `OatFileAssistant::ValidateBootClassPathChecksums` accepts the checksum string
+// produced by `gc::space::ImageSpace::GetBootClassPathChecksums`.
+TEST_P(OatFileAssistantTest, ValidateBootClassPathChecksums) {
+  std::string error_msg;
+  auto create_and_verify = [&]() {
+    std::string checksums = gc::space::ImageSpace::GetBootClassPathChecksums(
+        ArrayRef<gc::space::ImageSpace* const>(runtime_->GetHeap()->GetBootImageSpaces()),
+        ArrayRef<const DexFile* const>(runtime_->GetClassLinker()->GetBootClassPath()));
+    std::string bcp_locations = android::base::Join(runtime_->GetBootClassPathLocations(), ':');
+
+    ofa_context_ = CreateOatFileAssistantContext();
+    auto scoped_maybe_without_runtime = ScopedMaybeWithoutRuntime();
+    return OatFileAssistant::ValidateBootClassPathChecksums(
+        ofa_context_.get(), kRuntimeISA, checksums, bcp_locations, &error_msg);
+  };
+
+  ASSERT_TRUE(create_and_verify()) << error_msg;
+
+  for (const std::string& src : {GetDexSrc1(), GetDexSrc2()}) {
+    ASSERT_TRUE(InsertNewBootClasspathEntry(src, &error_msg)) << error_msg;
+    ASSERT_TRUE(create_and_verify()) << error_msg;
+  }
+}
+
 // TODO: More Tests:
 //  * Test class linker falls back to unquickened dex for DexNoOat
 //  * Test class linker falls back to unquickened dex for MultiDexNoOat
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index f53a216..ecb5323 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -3482,4 +3482,18 @@
   return false;
 }
 
+void Runtime::AppendToBootClassPath(
+    const std::string& filename,
+    const std::string& location,
+    const std::vector<std::unique_ptr<const art::DexFile>>& dex_files) {
+  boot_class_path_.push_back(filename);
+  if (!boot_class_path_locations_.empty()) {
+    boot_class_path_locations_.push_back(location);
+  }
+  ScopedObjectAccess soa(Thread::Current());
+  for (const std::unique_ptr<const art::DexFile>& dex_file : dex_files) {
+    GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get());
+  }
+}
+
 }  // namespace art
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 8d55af9..9606194 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -315,6 +315,11 @@
     return boot_class_path_locations_.empty() ? boot_class_path_ : boot_class_path_locations_;
   }
 
+  // Dynamically add an element to boot class path.
+  void AppendToBootClassPath(const std::string& filename,
+                             const std::string& location,
+                             const std::vector<std::unique_ptr<const art::DexFile>>& dex_files);
+
   const std::vector<int>& GetBootClassPathFds() const {
     return boot_class_path_fds_;
   }
diff --git a/test/674-hiddenapi/hiddenapi.cc b/test/674-hiddenapi/hiddenapi.cc
index ebe9d10..5fa2532 100644
--- a/test/674-hiddenapi/hiddenapi.cc
+++ b/test/674-hiddenapi/hiddenapi.cc
@@ -15,13 +15,10 @@
  */
 
 #include "base/sdk_version.h"
-#include "class_linker.h"
 #include "dex/art_dex_file_loader.h"
 #include "hidden_api.h"
 #include "jni.h"
 #include "runtime.h"
-#include "scoped_thread_state_change-inl.h"
-#include "thread.h"
 #include "ti-agent/scoped_utf_chars.h"
 
 namespace art {
@@ -77,10 +74,7 @@
 
   Java_Main_setDexDomain(env, klass, int_index, is_core_platform);
 
-  ScopedObjectAccess soa(Thread::Current());
-  for (std::unique_ptr<const DexFile>& dex_file : opened_dex_files[index]) {
-    Runtime::Current()->GetClassLinker()->AppendToBootClassPath(Thread::Current(), dex_file.get());
-  }
+  Runtime::Current()->AppendToBootClassPath(path, path, opened_dex_files[index]);
 
   return int_index;
 }