Compile the entire bootclasspath on device.

Most of the changes in this CL are renaming and comment changes. The
main behavior changes are:
1. Adding all bootclasspath jars (including the ones in the ART APEX) to
   `--dex-file` and `--dex-fd`.
2. Adding both the ART boot profile and the framework profile to
   `--profile-fd`.
3. Replacing the `--boot-image` flag with `--base` for compiling boot
   classpath jars.
4. Renaming the boot image on /data from `boot-framework.art` to
   `boot.art`.
5. Updating the `--boot-image` flag for compiling system_server jars.
6. Updating the default boot image location that the runtime uses.

Bug: 203492478
Test: atest art_standalone_odrefresh_tests
Test: atest art_standalone_artd_tests
Test: atest odsign_e2e_tests
Change-Id: If56bc96ec6f38335f3e8e1ad3085e231baacb6fc
diff --git a/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl b/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
index 43fd922..8253ead 100644
--- a/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
+++ b/artd/binder/private/com/android/art/DexoptBcpExtArgs.aidl
@@ -44,7 +44,7 @@
     // manually.
     String[] bootClasspaths;
     int[] bootClasspathFds;
-    int profileFd = -1;
+    int[] profileFds;
     int dirtyImageObjectsFd = -1;
     // Output file descriptors
     int oatFd = -1;
diff --git a/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl b/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
index bfb94e1..54aa867 100644
--- a/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
+++ b/artd/binder/private/com/android/art/DexoptSystemServerArgs.aidl
@@ -60,7 +60,7 @@
     String dexPath;
     String oatLocation;
     String[] classloaderContext;
-    boolean isBootImageOnSystem;
+    String bootImage;
     boolean classloaderContextAsParent;
 
     // SECURITY: The server may accept the request to produce code for the specified architecture,
diff --git a/artd/libdexopt.cc b/artd/libdexopt.cc
index 853f950..b6982c1 100644
--- a/artd/libdexopt.cc
+++ b/artd/libdexopt.cc
@@ -43,11 +43,6 @@
 using android::base::Error;
 using android::base::Result;
 
-std::string GetBootImage() {
-  // Typically "/apex/com.android.art/javalib/boot.art".
-  return art::GetArtRoot() + "/javalib/boot.art";
-}
-
 std::string GetEnvironmentVariableOrDie(const char* name) {
   const char* value = getenv(name);
   LOG_ALWAYS_FATAL_IF(value == nullptr, "%s is not defined.", name);
@@ -206,8 +201,10 @@
 
   cmdline.emplace_back("--instruction-set=" + ToInstructionSetString(args.isa));
 
-  if (args.profileFd >= 0) {
-    cmdline.emplace_back(android::base::StringPrintf("--profile-file-fd=%d", args.profileFd));
+  if (!args.profileFds.empty()) {
+    for (int fd : args.profileFds) {
+      cmdline.emplace_back(android::base::StringPrintf("--profile-file-fd=%d", fd));
+    }
     cmdline.emplace_back("--compiler-filter=speed-profile");
   } else {
     cmdline.emplace_back("--compiler-filter=speed");
@@ -216,8 +213,7 @@
   // Compile as a single image for fewer files and slightly less memory overhead.
   cmdline.emplace_back("--single-image");
 
-  // Set boot-image and expectation of compiling boot classpath extensions.
-  cmdline.emplace_back("--boot-image=" + GetBootImage());
+  cmdline.emplace_back(android::base::StringPrintf("--base=0x%08x", ART_BASE_ADDRESS));
 
   if (args.dirtyImageObjectsFd >= 0) {
     cmdline.emplace_back(android::base::StringPrintf("--dirty-image-objects-fd=%d",
@@ -321,23 +317,7 @@
                          android::base::Join(args.classloaderFds, ':'));
   }
 
-  // Derive boot image
-  // b/197176583
-  // If the boot extension artifacts are not on /data, then boot extensions are not re-compiled
-  // and the artifacts must exist on /system.
-  std::vector<std::string> jar_paths = android::base::Split(GetDex2oatBootClasspath(), ":");
-  auto iter = std::find_if_not(jar_paths.begin(), jar_paths.end(), &LocationIsOnArtModule);
-  if (iter == jar_paths.end()) {
-    return Error() << "Missing BCP extension compatible JAR";
-  }
-  const std::string& first_boot_extension_compatible_jars = *iter;
-  // TODO(197176583): Support compiling against BCP extension in /system.
-  const std::string extension_image = GetBootImagePath(args.isBootImageOnSystem,
-                                                       first_boot_extension_compatible_jars);
-  if (extension_image.empty()) {
-    return Error() << "Can't identify the first boot extension compatible jar";
-  }
-  cmdline.emplace_back("--boot-image=" + GetBootImage() + ":" + extension_image);
+  cmdline.emplace_back("--boot-image=" + args.bootImage);
 
   AddDex2OatConcurrencyArguments(cmdline, args.threads, args.cpuSet);
 
diff --git a/artd/libdexopt_test.cc b/artd/libdexopt_test.cc
index e73c5d6..3cc340b 100644
--- a/artd/libdexopt_test.cc
+++ b/artd/libdexopt_test.cc
@@ -58,7 +58,7 @@
     default_bcp_ext_args_.bootClasspaths = android::base::Split(
         GetEnvironmentVariableOrDie("DEX2OATBOOTCLASSPATH"), ":");  // from art_artd_tests.xml
     default_bcp_ext_args_.bootClasspathFds = {21, 22};
-    default_bcp_ext_args_.profileFd = 30;
+    default_bcp_ext_args_.profileFds = {30, 31};
     default_bcp_ext_args_.dirtyImageObjectsFd = 31;
     default_bcp_ext_args_.imageFd = 90;
     default_bcp_ext_args_.vdexFd = 91;
@@ -90,7 +90,7 @@
     default_system_server_args_.compilerFilter = CompilerFilter::SPEED_PROFILE;
     default_system_server_args_.cpuSet = {0, 1};
     default_system_server_args_.threads = 42;
-    default_system_server_args_.isBootImageOnSystem = true;
+    default_system_server_args_.bootImage = "/path/to/boot.art";
     ASSERT_EQ(default_system_server_args_.bootClasspaths.size(),
               default_system_server_args_.bootClasspathFds.size());
   }
@@ -134,32 +134,35 @@
   {
     std::vector<std::string> cmdline = Dex2oatArgsFromBcpExtensionArgs(default_bcp_ext_args_);
 
-    EXPECT_THAT(cmdline, AllOf(
-        Contains("--dex-fd=10"),
-        Contains("--dex-fd=11"),
-        Contains("--dex-file=/path/to/foo.jar"),
-        Contains("--dex-file=/path/to/bar.jar"),
-        Contains(HasSubstr("-Xbootclasspath:")),
-        Contains("-Xbootclasspathfds:21:22"),
+    EXPECT_THAT(cmdline,
+                AllOf(Contains("--dex-fd=10"),
+                      Contains("--dex-fd=11"),
+                      Contains("--dex-file=/path/to/foo.jar"),
+                      Contains("--dex-file=/path/to/bar.jar"),
+                      Contains(HasSubstr("-Xbootclasspath:")),
+                      Contains("-Xbootclasspathfds:21:22"),
 
-        Contains("--profile-file-fd=30"),
-        Contains("--compiler-filter=speed-profile"),
+                      Contains("--profile-file-fd=30"),
+                      Contains("--profile-file-fd=31"),
+                      Contains("--compiler-filter=speed-profile"),
 
-        Contains("--image-fd=90"),
-        Contains("--output-vdex-fd=91"),
-        Contains("--oat-fd=92"),
-        Contains("--oat-location=/oat/location/bar.odex"),
+                      Contains("--image-fd=90"),
+                      Contains("--output-vdex-fd=91"),
+                      Contains("--oat-fd=92"),
+                      Contains("--oat-location=/oat/location/bar.odex"),
 
-        Contains("--dirty-image-objects-fd=31"),
-        Contains("--instruction-set=x86_64"),
-        Contains("--cpu-set=0,1"),
-        Contains("-j42")));
+                      Contains("--dirty-image-objects-fd=31"),
+                      Contains("--instruction-set=x86_64"),
+                      Contains("--cpu-set=0,1"),
+                      Contains("-j42"),
+
+                      Contains(HasSubstr("--base="))));
   }
 
   // No profile
   {
     auto args = default_bcp_ext_args_;
-    args.profileFd = -1;
+    args.profileFds = {};
     std::vector<std::string> cmdline = Dex2oatArgsFromBcpExtensionArgs(args);
 
     EXPECT_THAT(cmdline, AllOf(
@@ -224,29 +227,31 @@
   {
     std::vector<std::string> cmdline = Dex2oatArgsFromSystemServerArgs(default_system_server_args_);
 
-    EXPECT_THAT(cmdline, AllOf(
-        Contains("--dex-fd=10"),
-        Contains("--dex-file=/path/to/foo.jar"),
-        Contains(HasSubstr("-Xbootclasspath:")),
-        Contains("-Xbootclasspathfds:21:22:23"),
-        Contains("-Xbootclasspathimagefds:-1:31:-1"),
-        Contains("-Xbootclasspathvdexfds:-1:32:-1"),
-        Contains("-Xbootclasspathoatfds:-1:33:-1"),
+    EXPECT_THAT(cmdline,
+                AllOf(Contains("--dex-fd=10"),
+                      Contains("--dex-file=/path/to/foo.jar"),
+                      Contains(HasSubstr("-Xbootclasspath:")),
+                      Contains("-Xbootclasspathfds:21:22:23"),
+                      Contains("-Xbootclasspathimagefds:-1:31:-1"),
+                      Contains("-Xbootclasspathvdexfds:-1:32:-1"),
+                      Contains("-Xbootclasspathoatfds:-1:33:-1"),
 
-        Contains("--profile-file-fd=11"),
-        Contains("--compiler-filter=speed-profile"),
+                      Contains("--profile-file-fd=11"),
+                      Contains("--compiler-filter=speed-profile"),
 
-        Contains("--app-image-fd=90"),
-        Contains("--output-vdex-fd=91"),
-        Contains("--oat-fd=92"),
-        Contains("--oat-location=/oat/location/bar.odex"),
+                      Contains("--app-image-fd=90"),
+                      Contains("--output-vdex-fd=91"),
+                      Contains("--oat-fd=92"),
+                      Contains("--oat-location=/oat/location/bar.odex"),
 
-        Contains("--class-loader-context-fds=40:41"),
-        Contains("--class-loader-context=PCL[/cl/abc.jar:/cl/def.jar]"),
+                      Contains("--class-loader-context-fds=40:41"),
+                      Contains("--class-loader-context=PCL[/cl/abc.jar:/cl/def.jar]"),
 
-        Contains("--instruction-set=x86_64"),
-        Contains("--cpu-set=0,1"),
-        Contains("-j42")));
+                      Contains("--instruction-set=x86_64"),
+                      Contains("--cpu-set=0,1"),
+                      Contains("-j42"),
+
+                      Contains("--boot-image=/path/to/boot.art")));
   }
 
   // Test different compiler filters
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index bc08f30..07402a3 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -283,65 +283,33 @@
   return GetAndroidDir(kArtApexDataEnvVar, kArtApexDataDefaultPath, /*must_exist=*/false);
 }
 
-static std::string GetFirstBootClasspathExtensionJar(const std::string& android_root) {
-  DCHECK(kIsTargetBuild);
-
-  // This method finds the first non-APEX DEX file in the boot class path as defined by the
-  // DEX2OATBOOTCLASSPATH environment variable. This corresponds to the first boot classpath
-  // extension (see IMAGE SECTION documentation in image.h). When on-device signing is used the
-  // boot class extensions are compiled together as a single image with a name derived from the
-  // first extension. This first boot classpath extension is usually
-  // '/system/framework/framework.jar'.
-  //
-  // DEX2OATBOOTCLASSPATH is generated at build time by in the init.environ.rc.in:
-  //   ${ANDROID_BUILD_TOP}/system/core/rootdir/Android.mk
-  // and initialized on Android by init in init.environ.rc:
-  //   ${ANDROID_BUILD_TOP}/system/core/rootdir/init.environ.rc.in.
-  // It is used by installd too.
-  const char* bcp = getenv("DEX2OATBOOTCLASSPATH");
-  const std::string kDefaultBcpExtensionJar = android_root + "/framework/framework.jar";
-  if (bcp != nullptr) {
-    for (std::string_view component : SplitString(bcp, ':')) {
-      if (component.empty()) {
-        continue;
-      }
-      if (!LocationIsOnApex(component)) {
-        return std::string{component};
-      }
-    }
-  }
-  return kDefaultBcpExtensionJar;
-}
-
 std::string GetDefaultBootImageLocation(const std::string& android_root,
                                         bool deny_art_apex_data_files) {
   constexpr static const char* kJavalibBootArt = "javalib/boot.art";
   constexpr static const char* kEtcBootImageProf = "etc/boot-image.prof";
 
-  // Boot image consists of two parts:
-  //  - the primary boot image in the ART APEX (contains the Core Libraries)
-  //  - the boot image extensions (contains framework libraries) on the system partition, or
-  //    in the ART APEX data directory, if an update for the ART module has been been installed.
+  // If an update for the ART module has been been installed, a single boot image for the entire
+  // bootclasspath is in the ART APEX data directory.
   if (kIsTargetBuild && !deny_art_apex_data_files) {
-    // If the ART APEX has been updated, the compiled boot image extension will be in the ART APEX
-    // data directory (assuming there is space and we trust the artifacts there). Otherwise, for a factory installed ART APEX it is
-    // under $ANDROID_ROOT/framework/.
-    const std::string first_extension_jar{GetFirstBootClasspathExtensionJar(android_root)};
-    const std::string boot_extension_image = GetApexDataBootImage(first_extension_jar);
-    const std::string boot_extension_filename =
-        GetSystemImageFilename(boot_extension_image.c_str(), kRuntimeISA);
-    if (OS::FileExists(boot_extension_filename.c_str(), /*check_file_type=*/true)) {
-      return StringPrintf("%s/%s:%s!%s/%s",
+    const std::string boot_image =
+        GetApexDataDalvikCacheDirectory(InstructionSet::kNone) + "/boot.art";
+    const std::string boot_image_filename = GetSystemImageFilename(boot_image.c_str(), kRuntimeISA);
+    if (OS::FileExists(boot_image_filename.c_str(), /*check_file_type=*/true)) {
+      return StringPrintf("%s!%s/%s!%s/%s",
+                          boot_image.c_str(),
                           kAndroidArtApexDefaultPath,
-                          kJavalibBootArt,
-                          boot_extension_image.c_str(),
+                          kEtcBootImageProf,
                           android_root.c_str(),
                           kEtcBootImageProf);
     } else if (errno == EACCES) {
       // Additional warning for potential SELinux misconfiguration.
-      PLOG(ERROR) << "Default boot image check failed, could not stat: " << boot_extension_image;
+      PLOG(ERROR) << "Default boot image check failed, could not stat: " << boot_image_filename;
     }
   }
+  // Boot image consists of two parts:
+  //  - the primary boot image in the ART APEX (contains the Core Libraries)
+  //  - the boot image extensions (contains framework libraries) on the system partition
+  // TODO(b/211973309): Update this once the primary boot image is moved.
   return StringPrintf("%s/%s:%s/framework/boot-framework.art!%s/%s",
                       kAndroidArtApexDefaultPath,
                       kJavalibBootArt,
@@ -358,18 +326,6 @@
   return GetDefaultBootImageLocation(android_root, /*deny_art_apex_data_files=*/false);
 }
 
-std::string GetBootImagePath(bool on_system, const std::string& jar_path) {
-  if (on_system) {
-    const std::string jar_name = android::base::Basename(jar_path);
-    const std::string image_name = ReplaceFileExtension(jar_name, "art");
-    // Typically "/system/framework/boot-framework.art".
-    return StringPrintf("%s/framework/boot-%s", GetAndroidRoot().c_str(), image_name.c_str());
-  } else {
-    // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot-framework.art".
-    return GetApexDataBootImage(jar_path);
-  }
-}
-
 static /*constinit*/ std::string_view dalvik_cache_sub_dir = "dalvik-cache";
 
 void OverrideDalvikCacheSubDirectory(std::string sub_dir) {
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index 709ac12..97abc73 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -77,9 +77,6 @@
 std::string GetDefaultBootImageLocation(const std::string& android_root,
                                         bool deny_art_apex_data_files);
 
-// Returns the boot image path of the provided jar, on /system or /data.
-std::string GetBootImagePath(bool on_system, const std::string& jar_path);
-
 // Allows the name to be used for the dalvik cache directory (normally "dalvik-cache") to be
 // overridden with a new value.
 void OverrideDalvikCacheSubDirectory(std::string sub_dir);
diff --git a/odrefresh/odr_artifacts.h b/odrefresh/odr_artifacts.h
index 66d76f0..30932e2 100644
--- a/odrefresh/odr_artifacts.h
+++ b/odrefresh/odr_artifacts.h
@@ -28,7 +28,7 @@
 // A grouping of odrefresh generated artifacts.
 class OdrArtifacts {
  public:
-  static OdrArtifacts ForBootImageExtension(const std::string& image_path) {
+  static OdrArtifacts ForBootImage(const std::string& image_path) {
     return OdrArtifacts(image_path, "oat");
   }
 
diff --git a/odrefresh/odr_artifacts_test.cc b/odrefresh/odr_artifacts_test.cc
index 76f2c28..b47f2a7 100644
--- a/odrefresh/odr_artifacts_test.cc
+++ b/odrefresh/odr_artifacts_test.cc
@@ -27,7 +27,7 @@
 
 static constexpr const char* kOdrefreshArtifactDirectory = "/test/dir";
 
-TEST(OdrArtifactsTest, ForBootImageExtension) {
+TEST(OdrArtifactsTest, ForBootImage) {
   ScopedUnsetEnvironmentVariable no_env("ART_APEX_DATA");
   setenv("ART_APEX_DATA", kOdrefreshArtifactDirectory, /* overwrite */ 1);
 
@@ -37,7 +37,7 @@
   const std::string image_filename =
       GetSystemImageFilename(image_location.c_str(), InstructionSet::kArm64);
 
-  const auto artifacts = OdrArtifacts::ForBootImageExtension(image_filename);
+  const auto artifacts = OdrArtifacts::ForBootImage(image_filename);
   CHECK_EQ(std::string(kOdrefreshArtifactDirectory) + "/dalvik-cache/arm64/boot-framework.art",
            artifacts.ImagePath());
   CHECK_EQ(std::string(kOdrefreshArtifactDirectory) + "/dalvik-cache/arm64/boot-framework.oat",
diff --git a/odrefresh/odr_config.h b/odrefresh/odr_config.h
index f5676e1..7b89a83 100644
--- a/odrefresh/odr_config.h
+++ b/odrefresh/odr_config.h
@@ -87,7 +87,7 @@
 
   const std::string& GetApexInfoListFile() const { return apex_info_list_file_; }
 
-  std::vector<InstructionSet> GetBootExtensionIsas() const {
+  std::vector<InstructionSet> GetBootClasspathIsas() const {
     const auto [isa32, isa64] = GetPotentialInstructionSets();
     switch (zygote_kind_) {
       case ZygoteKind::kZygote32:
diff --git a/odrefresh/odrefresh.cc b/odrefresh/odrefresh.cc
index d3cffc6..857e8ba 100644
--- a/odrefresh/odrefresh.cc
+++ b/odrefresh/odrefresh.cc
@@ -112,6 +112,8 @@
 
 constexpr mode_t kFileMode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
 
+constexpr const char* kFirstBootImageBasename = "boot.art";
+
 void EraseFiles(const std::vector<std::unique_ptr<File>>& files) {
   for (auto& file : files) {
     file->Erase(/*unlink=*/true);
@@ -208,7 +210,7 @@
 }
 
 // Returns a rewritten path based on ANDROID_ROOT if the path starts with "/system/".
-std::string AndroidRootRewrite(const std::string &path) {
+std::string AndroidRootRewrite(const std::string& path) {
   if (StartsWith(path, "/system/")) {
     return Concatenate({GetAndroidRoot(), path.substr(7)});
   } else {
@@ -414,20 +416,30 @@
   return true;
 }
 
+std::string GetBootImageComponentBasename(const std::string& jar_path, bool is_first_jar) {
+  if (is_first_jar) {
+    return kFirstBootImageBasename;
+  }
+  const std::string jar_name = android::base::Basename(jar_path);
+  return "boot-" + ReplaceFileExtension(jar_name, "art");
+}
+
 void PrepareCompiledBootClasspathFdsIfAny(
     /*inout*/ DexoptSystemServerArgs& dexopt_args,
     /*inout*/ std::vector<std::unique_ptr<File>>& output_files,
     const std::vector<std::string>& bcp_jars,
     const InstructionSet isa,
-    bool on_system) {
+    const std::string& artifact_dir) {
   std::vector<int> bcp_image_fds;
   std::vector<int> bcp_oat_fds;
   std::vector<int> bcp_vdex_fds;
   std::vector<std::unique_ptr<File>> opened_files;
   bool added_any = false;
-  for (const std::string& jar : bcp_jars) {
-    std::string image_path = GetBootImagePath(on_system, jar);
-    image_path = image_path.empty() ? "" : GetSystemImageFilename(image_path.c_str(), isa);
+  for (size_t i = 0; i < bcp_jars.size(); i++) {
+    const std::string& jar = bcp_jars[i];
+    std::string image_path =
+        artifact_dir + "/" + GetBootImageComponentBasename(jar, /*is_first_jar=*/i == 0);
+    image_path = GetSystemImageFilename(image_path.c_str(), isa);
     std::unique_ptr<File> image_file(OS::OpenFileForReading(image_path.c_str()));
     if (image_file && image_file->IsValid()) {
       bcp_image_fds.push_back(image_file->Fd());
@@ -527,6 +539,10 @@
   }
 }
 
+std::string GetArtBootImageDir() { return GetArtRoot() + "/javalib"; }
+
+std::string GetSystemBootImageDir() { return GetAndroidRoot() + "/framework"; }
+
 }  // namespace
 
 OnDeviceRefresh::OnDeviceRefresh(const OdrConfig& config)
@@ -545,12 +561,9 @@
       exec_utils_{std::move(exec_utils)},
       odr_dexopt_{std::move(odr_dexopt)} {
   for (const std::string& jar : android::base::Split(config_.GetDex2oatBootClasspath(), ":")) {
-    // Boot class path extensions are those not in the ART APEX. Updatable APEXes should not
-    // have DEX files in the DEX2OATBOOTCLASSPATH. At the time of writing i18n is a non-updatable
-    // APEX and so does appear in the DEX2OATBOOTCLASSPATH.
-    if (!LocationIsOnArtModule(jar)) {
-      boot_extension_compilable_jars_.emplace_back(jar);
-    }
+    // Updatable APEXes should not have DEX files in the DEX2OATBOOTCLASSPATH. At the time of
+    // writing i18n is a non-updatable APEX and so does appear in the DEX2OATBOOTCLASSPATH.
+    boot_classpath_compilable_jars_.emplace_back(jar);
   }
 
   all_systemserver_jars_ = android::base::Split(config_.GetSystemServerClasspath(), ":");
@@ -566,9 +579,7 @@
   }
 }
 
-time_t OnDeviceRefresh::GetExecutionTimeUsed() const {
-  return time(nullptr) - start_time_;
-}
+time_t OnDeviceRefresh::GetExecutionTimeUsed() const { return time(nullptr) - start_time_; }
 
 time_t OnDeviceRefresh::GetExecutionTimeRemaining() const {
   return std::max(static_cast<time_t>(0),
@@ -589,8 +600,8 @@
   // We are only interested in active APEXes that contain compilable JARs.
   std::unordered_set<std::string_view> relevant_apexes;
   relevant_apexes.reserve(info_list->getApexInfo().size());
-  for (const std::vector<std::string>* jar_list : { &boot_extension_compilable_jars_,
-      &all_systemserver_jars_, &boot_classpath_jars_}) {
+  for (const std::vector<std::string>* jar_list :
+       {&boot_classpath_compilable_jars_, &all_systemserver_jars_, &boot_classpath_jars_}) {
     for (auto& jar : *jar_list) {
       std::string_view apex = ApexNameFromLocation(jar);
       if (!apex.empty()) {
@@ -606,8 +617,9 @@
   std::copy_if(info_list->getApexInfo().begin(),
                info_list->getApexInfo().end(),
                std::back_inserter(filtered_info_list),
-               [&](const apex::ApexInfo& info) { return info.getIsActive()
-                     && relevant_apexes.count(info.getModuleName()) != 0; });
+               [&](const apex::ApexInfo& info) {
+                 return info.getIsActive() && relevant_apexes.count(info.getModuleName()) != 0;
+               });
   return filtered_info_list;
 }
 
@@ -652,15 +664,15 @@
   }
 
   std::optional<std::vector<art_apex::Component>> bcp_compilable_components =
-      GenerateBootExtensionCompilableComponents();
+      GenerateBootClasspathCompilableComponents();
   if (!bcp_compilable_components.has_value()) {
-    return Errorf("No boot classpath extension compilable components.");
+    return Errorf("No boot classpath compilable components.");
   }
 
   std::optional<std::vector<art_apex::SystemServerComponent>> system_server_components =
       GenerateSystemServerComponents();
   if (!system_server_components.has_value()) {
-    return Errorf("No system_server extension components.");
+    return Errorf("No system_server components.");
   }
 
   std::ofstream out(cache_info_filename_.c_str());
@@ -697,9 +709,9 @@
   return GenerateComponents(boot_classpath_jars_);
 }
 
-std::vector<art_apex::Component> OnDeviceRefresh::GenerateBootExtensionCompilableComponents()
+std::vector<art_apex::Component> OnDeviceRefresh::GenerateBootClasspathCompilableComponents()
     const {
-  return GenerateComponents(boot_extension_compilable_jars_);
+  return GenerateComponents(boot_classpath_compilable_jars_);
 }
 
 std::vector<art_apex::SystemServerComponent> OnDeviceRefresh::GenerateSystemServerComponents()
@@ -712,16 +724,37 @@
       });
 }
 
-std::string OnDeviceRefresh::GetBootImageExtensionImage(bool on_system) const {
-  CHECK(!boot_extension_compilable_jars_.empty());
-  const std::string leading_jar = boot_extension_compilable_jars_[0];
-  return GetBootImagePath(on_system, leading_jar);
+std::string OnDeviceRefresh::GetBootImage(bool on_system) const {
+  if (on_system) {
+    // Typically "/apex/com.android.art/javalib/boot.art".
+    // TODO(b/211973309): Update this once the primary boot image is moved.
+    return GetArtBootImageDir() + "/" + kFirstBootImageBasename;
+  } else {
+    // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/boot.art".
+    return config_.GetArtifactDirectory() + "/" + kFirstBootImageBasename;
+  }
 }
 
-std::string OnDeviceRefresh::GetBootImageExtensionImagePath(bool on_system,
-                                                            const InstructionSet isa) const {
-  // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot-framework.art".
-  return GetSystemImageFilename(GetBootImageExtensionImage(on_system).c_str(), isa);
+std::string OnDeviceRefresh::GetBootImagePath(bool on_system, const InstructionSet isa) const {
+  // Typically "/data/misc/apexdata/com.android.art/dalvik-cache/<isa>/boot.art".
+  return GetSystemImageFilename(GetBootImage(on_system).c_str(), isa);
+}
+
+std::string OnDeviceRefresh::GetSystemBootImageExtension() const {
+  std::string art_root = GetArtRoot() + "/";
+  // Find the first boot extension jar.
+  auto it = std::find_if_not(
+      boot_classpath_compilable_jars_.begin(),
+      boot_classpath_compilable_jars_.end(),
+      [&](const std::string& jar) { return android::base::StartsWith(jar, art_root); });
+  CHECK(it != boot_classpath_compilable_jars_.end());
+  // Typically "/system/framework/boot-framework.art".
+  return GetSystemBootImageDir() + "/" + GetBootImageComponentBasename(*it, /*is_first_jar=*/false);
+}
+
+std::string OnDeviceRefresh::GetSystemBootImageExtensionPath(const InstructionSet isa) const {
+  // Typically "/system/framework/<isa>/boot-framework.art".
+  return GetSystemImageFilename(GetSystemBootImageExtension().c_str(), isa);
 }
 
 std::string OnDeviceRefresh::GetSystemServerImagePath(bool on_system,
@@ -752,14 +785,27 @@
   return RemoveDirectory(config_.GetArtifactDirectory());
 }
 
-WARN_UNUSED bool OnDeviceRefresh::BootExtensionArtifactsExist(
+WARN_UNUSED bool OnDeviceRefresh::BootClasspathArtifactsExist(
     bool on_system,
     const InstructionSet isa,
     /*out*/ std::string* error_msg,
     /*out*/ std::vector<std::string>* checked_artifacts) const {
-  const std::string apexdata_image_location = GetBootImageExtensionImagePath(on_system, isa);
-  const OdrArtifacts artifacts = OdrArtifacts::ForBootImageExtension(apexdata_image_location);
-  return ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg, checked_artifacts);
+  std::string path = GetBootImagePath(on_system, isa);
+  OdrArtifacts artifacts = OdrArtifacts::ForBootImage(path);
+  if (!ArtifactsExist(artifacts, /*check_art_file=*/true, error_msg, checked_artifacts)) {
+    return false;
+  }
+  // There is a split between the primary boot image and the extension on /system, so they need to
+  // be checked separately. This does not apply to the boot image on /data.
+  if (on_system) {
+    std::string extension_path = GetSystemBootImageExtensionPath(isa);
+    OdrArtifacts extension_artifacts = OdrArtifacts::ForBootImage(extension_path);
+    if (!ArtifactsExist(
+            extension_artifacts, /*check_art_file=*/true, error_msg, checked_artifacts)) {
+      return false;
+    }
+  }
+  return true;
 }
 
 WARN_UNUSED bool OnDeviceRefresh::SystemServerArtifactsExist(
@@ -781,7 +827,7 @@
   return jars_missing_artifacts->empty();
 }
 
-WARN_UNUSED bool OnDeviceRefresh::CheckBootExtensionArtifactsAreUpToDate(
+WARN_UNUSED bool OnDeviceRefresh::CheckBootClasspathArtifactsAreUpToDate(
     OdrMetrics& metrics,
     const InstructionSet isa,
     const apex::ApexInfo& art_apex_info,
@@ -792,11 +838,11 @@
 
     // ART is not updated, so we can use the artifacts on /system. Check if they exist.
     std::string error_msg;
-    if (BootExtensionArtifactsExist(/*on_system=*/true, isa, &error_msg)) {
+    if (BootClasspathArtifactsExist(/*on_system=*/true, isa, &error_msg)) {
       return true;
     }
 
-    LOG(INFO) << "Incomplete boot extension artifacts on /system. " << error_msg;
+    LOG(INFO) << "Incomplete boot classpath artifacts on /system. " << error_msg;
     LOG(INFO) << "Checking cache.";
   }
 
@@ -857,7 +903,7 @@
   // The boot class components may change unexpectedly, for example an OTA could update
   // framework.jar.
   const std::vector<art_apex::Component> expected_bcp_compilable_components =
-      GenerateBootExtensionCompilableComponents();
+      GenerateBootClasspathCompilableComponents();
   if (expected_bcp_compilable_components.size() != 0 &&
       (!cache_info->hasDex2oatBootClasspath() ||
        !cache_info->getFirstDex2oatBootClasspath()->hasComponent())) {
@@ -878,8 +924,8 @@
 
   // Cache info looks good, check all compilation artifacts exist.
   std::string error_msg;
-  if (!BootExtensionArtifactsExist(/*on_system=*/false, isa, &error_msg, checked_artifacts)) {
-    LOG(INFO) << "Incomplete boot extension artifacts. " << error_msg;
+  if (!BootClasspathArtifactsExist(/*on_system=*/false, isa, &error_msg, checked_artifacts)) {
+    LOG(INFO) << "Incomplete boot classpath artifacts. " << error_msg;
     metrics.SetTrigger(OdrMetrics::Trigger::kMissingArtifacts);
     return false;
   }
@@ -1165,7 +1211,7 @@
 
   // Clean-up helper used to simplify clean-ups and handling failures there.
   auto cleanup_and_compile_all = [&, this]() {
-    compilation_options->compile_boot_extensions_for_isas = config_.GetBootExtensionIsas();
+    compilation_options->compile_boot_classpath_for_isas = config_.GetBootClasspathIsas();
     compilation_options->system_server_jars_to_compile = AllSystemServerJars();
     return RemoveArtifactsDirectory() ? ExitCode::kCompilationRequired : ExitCode::kCleanupFailed;
   };
@@ -1215,11 +1261,11 @@
   InstructionSet system_server_isa = config_.GetSystemServerIsa();
   std::vector<std::string> checked_artifacts;
 
-  for (const InstructionSet isa : config_.GetBootExtensionIsas()) {
-    if (!CheckBootExtensionArtifactsAreUpToDate(
+  for (const InstructionSet isa : config_.GetBootClasspathIsas()) {
+    if (!CheckBootClasspathArtifactsAreUpToDate(
             metrics, isa, art_apex_info.value(), cache_info, &checked_artifacts)) {
-      compilation_options->compile_boot_extensions_for_isas.push_back(isa);
-      // system_server artifacts are invalid without valid boot extension artifacts.
+      compilation_options->compile_boot_classpath_for_isas.push_back(isa);
+      // system_server artifacts are invalid without valid boot classpath artifacts.
       if (isa == system_server_isa) {
         compilation_options->system_server_jars_to_compile = AllSystemServerJars();
       }
@@ -1234,7 +1280,7 @@
                                           &checked_artifacts);
   }
 
-  bool compilation_required = (!compilation_options->compile_boot_extensions_for_isas.empty() ||
+  bool compilation_required = (!compilation_options->compile_boot_classpath_for_isas.empty() ||
                                !compilation_options->system_server_jars_to_compile.empty());
 
   // If partial compilation is disabled, we should compile everything regardless of what's in
@@ -1269,7 +1315,7 @@
   return compilation_required ? ExitCode::kCompilationRequired : ExitCode::kOkay;
 }
 
-WARN_UNUSED bool OnDeviceRefresh::CompileBootExtensionArtifacts(
+WARN_UNUSED bool OnDeviceRefresh::CompileBootClasspathArtifacts(
     const InstructionSet isa,
     const std::string& staging_dir,
     OdrMetrics& metrics,
@@ -1281,10 +1327,17 @@
   dexopt_args.isa = InstructionSetToAidlIsa(isa);
 
   std::vector<std::unique_ptr<File>> readonly_files_raii;
-  const std::string boot_profile_file(GetAndroidRoot() + "/etc/boot-image.prof");
+  dexopt_args.profileFds.resize(2);
+  const std::string art_boot_profile_file = GetArtRoot() + "/etc/boot-image.prof";
   if (!PrepareDex2OatProfileIfExists(
-          &dexopt_args.profileFd, &readonly_files_raii, boot_profile_file)) {
-    LOG(ERROR) << "Missing expected profile for boot extension: " << boot_profile_file;
+          &dexopt_args.profileFds[0], &readonly_files_raii, art_boot_profile_file)) {
+    LOG(ERROR) << "Missing expected ART boot profile: " << art_boot_profile_file;
+    return false;
+  }
+  const std::string framework_boot_profile_file = GetAndroidRoot() + "/etc/boot-image.prof";
+  if (!PrepareDex2OatProfileIfExists(
+          &dexopt_args.profileFds[1], &readonly_files_raii, framework_boot_profile_file)) {
+    LOG(ERROR) << "Missing expected framework boot profile: " << framework_boot_profile_file;
     return false;
   }
 
@@ -1297,8 +1350,8 @@
     LOG(WARNING) << "Missing dirty objects file : " << QuotePath(dirty_image_objects_file);
   }
 
-  // Add boot extensions to compile.
-  for (const std::string& component : boot_extension_compilable_jars_) {
+  // Add boot classpath jars to compile.
+  for (const std::string& component : boot_classpath_compilable_jars_) {
     std::string actual_path = AndroidRootRewrite(component);
     std::unique_ptr<File> file(OS::OpenFileForReading(actual_path.c_str()));
     dexopt_args.dexPaths.emplace_back(component);
@@ -1312,10 +1365,8 @@
     return false;
   }
 
-  const std::string image_location = GetBootImageExtensionImagePath(/*on_system=*/false, isa);
-  const OdrArtifacts artifacts = OdrArtifacts::ForBootImageExtension(image_location);
-  CHECK_EQ(GetApexDataOatFilename(boot_extension_compilable_jars_.front().c_str(), isa),
-           artifacts.OatPath());
+  const std::string image_location = GetBootImagePath(/*on_system=*/false, isa);
+  const OdrArtifacts artifacts = OdrArtifacts::ForBootImage(image_location);
 
   dexopt_args.oatLocation = artifacts.OatPath();
   const std::pair<const std::string, int*> location_kind_pairs[] = {
@@ -1356,7 +1407,7 @@
   }
 
   const time_t timeout = GetSubprocessTimeout();
-  LOG(INFO) << "Compiling boot extensions (" << isa << "): " << dexopt_args.toString()
+  LOG(INFO) << "Compiling boot classpath (" << isa << "): " << dexopt_args.toString()
             << " [timeout " << timeout << "s]";
   if (config_.GetDryRun()) {
     LOG(INFO) << "Compilation skipped (dry-run).";
@@ -1364,6 +1415,9 @@
   }
 
   bool timed_out = false;
+  // NOTE: The method `DexoptBcpExtension` actually compiles the entire boot classpath. We don't
+  // rename this method because it will eventually go away.
+  // TODO(b/211977683): Call dex2oat directly.
   int dex2oat_exit_code =
       odr_dexopt_->DexoptBcpExtension(dexopt_args, timeout, &timed_out, error_msg);
 
@@ -1471,13 +1525,19 @@
       return false;
     }
     std::string unused_error_msg;
-    // If the boot extension artifacts are not on /data, then boot extensions are not re-compiled
+    // If the boot classpath artifacts are not on /data, then the boot classpath are not re-compiled
     // and the artifacts must exist on /system.
     bool boot_image_on_system =
-        !BootExtensionArtifactsExist(/*on_system=*/false, isa, &unused_error_msg);
+        !BootClasspathArtifactsExist(/*on_system=*/false, isa, &unused_error_msg);
     PrepareCompiledBootClasspathFdsIfAny(
-        dexopt_args, readonly_files_raii, bcp_jars, isa, boot_image_on_system);
-    dexopt_args.isBootImageOnSystem = boot_image_on_system;
+        dexopt_args,
+        readonly_files_raii,
+        bcp_jars,
+        isa,
+        boot_image_on_system ? GetSystemBootImageDir() : config_.GetArtifactDirectory());
+    dexopt_args.bootImage = boot_image_on_system ? GetBootImage(/*on_system=*/true) + ":" +
+                                                       GetSystemBootImageExtension() :
+                                                   GetBootImage(/*on_system=*/false);
 
     dexopt_args.classloaderContext = classloader_context;
     if (!classloader_context.empty()) {
@@ -1575,16 +1635,16 @@
 
   uint32_t dex2oat_invocation_count = 0;
   uint32_t total_dex2oat_invocation_count =
-      compilation_options.compile_boot_extensions_for_isas.size() +
+      compilation_options.compile_boot_classpath_for_isas.size() +
       compilation_options.system_server_jars_to_compile.size();
   ReportNextBootAnimationProgress(dex2oat_invocation_count, total_dex2oat_invocation_count);
   auto advance_animation_progress = [&]() {
     ReportNextBootAnimationProgress(++dex2oat_invocation_count, total_dex2oat_invocation_count);
   };
 
-  const auto& bcp_instruction_sets = config_.GetBootExtensionIsas();
+  const auto& bcp_instruction_sets = config_.GetBootClasspathIsas();
   DCHECK(!bcp_instruction_sets.empty() && bcp_instruction_sets.size() <= 2);
-  for (const InstructionSet isa : compilation_options.compile_boot_extensions_for_isas) {
+  for (const InstructionSet isa : compilation_options.compile_boot_classpath_for_isas) {
     auto stage = (isa == bcp_instruction_sets.front()) ? OdrMetrics::Stage::kPrimaryBootClasspath :
                                                          OdrMetrics::Stage::kSecondaryBootClasspath;
     metrics.SetStage(stage);
@@ -1595,7 +1655,7 @@
       return ExitCode::kCompilationFailed;
     }
 
-    if (!CompileBootExtensionArtifacts(
+    if (!CompileBootClasspathArtifacts(
             isa, staging_dir, metrics, advance_animation_progress, &error_msg)) {
       LOG(ERROR) << "Compilation of BCP failed: " << error_msg;
       if (!config_.GetDryRun() && !RemoveDirectory(staging_dir)) {
diff --git a/odrefresh/odrefresh.h b/odrefresh/odrefresh.h
index 88162a3..d749b2b 100644
--- a/odrefresh/odrefresh.h
+++ b/odrefresh/odrefresh.h
@@ -43,8 +43,8 @@
   // If true, update the cache info only and do not compile anything.
   bool update_cache_info_only = false;
 
-  // If not empty, compile the bootclasspath extensions for ISAs in the list.
-  std::vector<InstructionSet> compile_boot_extensions_for_isas;
+  // If not empty, compile the bootclasspath jars for ISAs in the list.
+  std::vector<InstructionSet> compile_boot_classpath_for_isas;
 
   // If not empty, compile the system server jars in the list.
   std::set<std::string> system_server_jars_to_compile;
@@ -60,8 +60,7 @@
                   std::unique_ptr<ExecUtils> exec_utils,
                   std::unique_ptr<OdrDexopt> odr_dexopt);
 
-  // Returns the exit code, a list of ISAs that boot extensions should be compiled for, and a
-  // boolean indicating whether the system server should be compiled.
+  // Returns the exit code and specifies what should be compiled in `compilation_options`.
   WARN_UNUSED ExitCode
   CheckArtifactsAreUpToDate(OdrMetrics& metrics,
                             /*out*/ CompilationOptions* compilation_options) const;
@@ -94,13 +93,23 @@
 
   std::vector<com::android::art::Component> GenerateBootClasspathComponents() const;
 
-  std::vector<com::android::art::Component> GenerateBootExtensionCompilableComponents() const;
+  std::vector<com::android::art::Component> GenerateBootClasspathCompilableComponents() const;
 
   std::vector<com::android::art::SystemServerComponent> GenerateSystemServerComponents() const;
 
-  std::string GetBootImageExtensionImage(bool on_system) const;
+  // Returns the symbolic boot image location (without ISA).
+  std::string GetBootImage(bool on_system) const;
 
-  std::string GetBootImageExtensionImagePath(bool on_system, const InstructionSet isa) const;
+  // Returns the real boot image location (with ISA).
+  std::string GetBootImagePath(bool on_system, const InstructionSet isa) const;
+
+  // Returns the symbolic boot image extension location (without ISA). Note that this only applies
+  // to boot images on /system.
+  std::string GetSystemBootImageExtension() const;
+
+  // Returns the real boot image location extension (with ISA). Note that this only applies to boot
+  // images on /system.
+  std::string GetSystemBootImageExtensionPath(const InstructionSet isa) const;
 
   std::string GetSystemServerImagePath(bool on_system, const std::string& jar_path) const;
 
@@ -113,10 +122,10 @@
   // artifacts to fs-verity.
   android::base::Result<void> RefreshExistingArtifacts() const;
 
-  // Checks whether all boot extension artifacts are present. Returns true if all are present, false
+  // Checks whether all boot classpath artifacts are present. Returns true if all are present, false
   // otherwise.
   // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
-  WARN_UNUSED bool BootExtensionArtifactsExist(
+  WARN_UNUSED bool BootClasspathArtifactsExist(
       bool on_system,
       const InstructionSet isa,
       /*out*/ std::string* error_msg,
@@ -132,10 +141,10 @@
       /*out*/ std::set<std::string>* jars_missing_artifacts,
       /*out*/ std::vector<std::string>* checked_artifacts = nullptr) const;
 
-  // Checks whether all boot extension artifacts are up to date. Returns true if all are present,
+  // Checks whether all boot classpath artifacts are up to date. Returns true if all are present,
   // false otherwise.
   // If `checked_artifacts` is present, adds checked artifacts to `checked_artifacts`.
-  WARN_UNUSED bool CheckBootExtensionArtifactsAreUpToDate(
+  WARN_UNUSED bool CheckBootClasspathArtifactsAreUpToDate(
       OdrMetrics& metrics,
       const InstructionSet isa,
       const com::android::apex::ApexInfo& art_apex_info,
@@ -153,7 +162,7 @@
       /*out*/ std::set<std::string>* jars_to_compile,
       /*out*/ std::vector<std::string>* checked_artifacts) const;
 
-  WARN_UNUSED bool CompileBootExtensionArtifacts(const InstructionSet isa,
+  WARN_UNUSED bool CompileBootClasspathArtifacts(const InstructionSet isa,
                                                  const std::string& staging_dir,
                                                  OdrMetrics& metrics,
                                                  const std::function<void()>& on_dex2oat_success,
@@ -172,8 +181,8 @@
   // Path to cache information file that is used to speed up artifact checking.
   const std::string cache_info_filename_;
 
-  // List of boot extension components that should be compiled.
-  std::vector<std::string> boot_extension_compilable_jars_;
+  // List of boot classpath components that should be compiled.
+  std::vector<std::string> boot_classpath_compilable_jars_;
 
   // Set of system_server components in SYSTEMSERVERCLASSPATH that should be compiled.
   std::unordered_set<std::string> systemserver_classpath_jars_;
diff --git a/odrefresh/odrefresh_main.cc b/odrefresh/odrefresh_main.cc
index 8262805..f93ee93 100644
--- a/odrefresh/odrefresh_main.cc
+++ b/odrefresh/odrefresh_main.cc
@@ -185,16 +185,14 @@
 NO_RETURN void UsageHelp(const char* argv0) {
   std::string name(android::base::Basename(argv0));
   UsageMsg("Usage: %s [OPTION...] ACTION", name.c_str());
-  UsageMsg("On-device refresh tool for boot class path extensions and system server");
+  UsageMsg("On-device refresh tool for boot classpath and system server");
   UsageMsg("following an update of the ART APEX.");
   UsageMsg("");
   UsageMsg("Valid ACTION choices are:");
   UsageMsg("");
   UsageMsg("--check          Check compilation artifacts are up-to-date based on metadata.");
-  UsageMsg("--compile        Compile boot class path extensions and system_server jars");
-  UsageMsg("                 when necessary.");
-  UsageMsg("--force-compile  Unconditionally compile the boot class path extensions and");
-  UsageMsg("                 system_server jars.");
+  UsageMsg("--compile        Compile boot classpath and system_server jars when necessary.");
+  UsageMsg("--force-compile  Unconditionally compile the bootclass path and system_server jars.");
   UsageMsg("--help           Display this help information.");
   UsageMsg("");
   UsageMsg("Available OPTIONs are:");
@@ -270,8 +268,8 @@
     }
     return odr.Compile(metrics,
                        CompilationOptions{
-                         .compile_boot_extensions_for_isas = config.GetBootExtensionIsas(),
-                         .system_server_jars_to_compile = odr.AllSystemServerJars(),
+                           .compile_boot_classpath_for_isas = config.GetBootClasspathIsas(),
+                           .system_server_jars_to_compile = odr.AllSystemServerJars(),
                        });
   } else if (action == "--help") {
     UsageHelp(argv[0]);
diff --git a/odrefresh/odrefresh_test.cc b/odrefresh/odrefresh_test.cc
index 7494084..370fd0d 100644
--- a/odrefresh/odrefresh_test.cc
+++ b/odrefresh/odrefresh_test.cc
@@ -159,8 +159,12 @@
 
     std::string system_etc_dir = Concatenate({android_root_path, "/etc"});
     ASSERT_TRUE(EnsureDirectoryExists(system_etc_dir));
-    boot_profile_file_ = system_etc_dir + "/boot-image.prof";
-    CreateEmptyFile(boot_profile_file_);
+    framework_profile_ = system_etc_dir + "/boot-image.prof";
+    CreateEmptyFile(framework_profile_);
+    std::string art_etc_dir = Concatenate({android_art_root_path, "/etc"});
+    ASSERT_TRUE(EnsureDirectoryExists(art_etc_dir));
+    art_profile_ = art_etc_dir + "/boot-image.prof";
+    CreateEmptyFile(art_profile_);
 
     framework_dir_ = android_root_path + "/framework";
     framework_jar_ = framework_dir_ + "/framework.jar";
@@ -169,8 +173,8 @@
     services_foo_jar_ = framework_dir_ + "/services-foo.jar";
     services_bar_jar_ = framework_dir_ + "/services-bar.jar";
     std::string services_jar_prof = framework_dir_ + "/services.jar.prof";
-    std::string javalib_dir = android_art_root_path + "/javalib";
-    std::string boot_art = javalib_dir + "/boot.art";
+    art_javalib_dir_ = android_art_root_path + "/javalib";
+    core_oj_jar_ = art_javalib_dir_ + "/core-oj.jar";
 
     // Create placeholder files.
     ASSERT_TRUE(EnsureDirectoryExists(framework_dir_ + "/x86_64"));
@@ -180,16 +184,16 @@
     CreateEmptyFile(services_foo_jar_);
     CreateEmptyFile(services_bar_jar_);
     CreateEmptyFile(services_jar_prof);
-    ASSERT_TRUE(EnsureDirectoryExists(javalib_dir));
-    CreateEmptyFile(boot_art);
+    ASSERT_TRUE(EnsureDirectoryExists(art_javalib_dir_));
+    CreateEmptyFile(core_oj_jar_);
 
     std::string apex_info_filename = Concatenate({temp_dir_path, "/apex-info-list.xml"});
     WriteFakeApexInfoList(apex_info_filename);
     config_.SetApexInfoListFile(apex_info_filename);
 
     config_.SetArtBinDir(Concatenate({temp_dir_path, "/bin"}));
-    config_.SetBootClasspath(framework_jar_);
-    config_.SetDex2oatBootclasspath(framework_jar_);
+    config_.SetBootClasspath(Concatenate({core_oj_jar_, ":", framework_jar_}));
+    config_.SetDex2oatBootclasspath(Concatenate({core_oj_jar_, ":", framework_jar_}));
     config_.SetSystemServerClasspath(Concatenate({location_provider_jar_, ":", services_jar_}));
     config_.SetStandaloneSystemServerJars(Concatenate({services_foo_jar_, ":", services_bar_jar_}));
     config_.SetIsa(InstructionSet::kX86_64);
@@ -230,6 +234,7 @@
   std::unique_ptr<ScopedUnsetEnvironmentVariable> art_apex_data_env_;
   OdrConfig config_;
   std::unique_ptr<OdrMetrics> metrics_;
+  std::string core_oj_jar_;
   std::string framework_jar_;
   std::string location_provider_jar_;
   std::string services_jar_;
@@ -237,9 +242,32 @@
   std::string services_bar_jar_;
   std::string dalvik_cache_dir_;
   std::string framework_dir_;
-  std::string boot_profile_file_;
+  std::string art_javalib_dir_;
+  std::string framework_profile_;
+  std::string art_profile_;
 };
 
+TEST_F(OdRefreshTest, BootClasspathJars) {
+  auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
+
+  EXPECT_CALL(*mock_odr_dexopt,
+              DoDexoptBcpExtension(AllOf(
+                  Field(&DexoptBcpExtArgs::dexPaths, ElementsAre(core_oj_jar_, framework_jar_)),
+                  AllOf(Field(&DexoptBcpExtArgs::dexFds,
+                              ElementsAre(FdOf(core_oj_jar_), FdOf(framework_jar_))),
+                        Field(&DexoptBcpExtArgs::profileFds,
+                              ElementsAre(FdOf(art_profile_), FdOf(framework_profile_))),
+                        Field(&DexoptBcpExtArgs::oatLocation,
+                              Eq(dalvik_cache_dir_ + "/x86_64/boot.oat"))))))
+      .WillOnce(Return(0));
+
+  EXPECT_EQ(odrefresh->Compile(*metrics_,
+                               CompilationOptions{
+                                   .compile_boot_classpath_for_isas = {InstructionSet::kX86_64},
+                               }),
+            ExitCode::kCompilationSuccess);
+}
+
 TEST_F(OdRefreshTest, AllSystemServerJars) {
   auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
 
@@ -442,7 +470,7 @@
   EXPECT_EQ(
       odrefresh->Compile(*metrics_,
                          CompilationOptions{
-                             .compile_boot_extensions_for_isas = {InstructionSet::kX86_64},
+                             .compile_boot_classpath_for_isas = {InstructionSet::kX86_64},
                              .system_server_jars_to_compile = odrefresh->AllSystemServerJars(),
                          }),
       ExitCode::kCompilationSuccess);
@@ -453,21 +481,20 @@
     auto [odrefresh, mock_odr_dexopt] = CreateOdRefresh();
 
     // Boot image is on /data.
-    OdrArtifacts artifacts =
-        OdrArtifacts::ForBootImageExtension(dalvik_cache_dir_ + "/x86_64/boot-framework.art");
+    OdrArtifacts artifacts = OdrArtifacts::ForBootImage(dalvik_cache_dir_ + "/x86_64/boot.art");
     auto file1 = ScopedCreateEmptyFile(artifacts.ImagePath());
     auto file2 = ScopedCreateEmptyFile(artifacts.VdexPath());
     auto file3 = ScopedCreateEmptyFile(artifacts.OatPath());
 
-    EXPECT_CALL(
-        *mock_odr_dexopt,
-        DoDexoptSystemServer(AllOf(Field(&DexoptSystemServerArgs::isBootImageOnSystem, Eq(false)),
-                                   Field(&DexoptSystemServerArgs::bootClasspathImageFds,
-                                         Contains(FdOf(artifacts.ImagePath()))),
-                                   Field(&DexoptSystemServerArgs::bootClasspathVdexFds,
-                                         Contains(FdOf(artifacts.VdexPath()))),
-                                   Field(&DexoptSystemServerArgs::bootClasspathOatFds,
-                                         Contains(FdOf(artifacts.OatPath()))))))
+    EXPECT_CALL(*mock_odr_dexopt,
+                DoDexoptSystemServer(AllOf(
+                    Field(&DexoptSystemServerArgs::bootImage, Eq(dalvik_cache_dir_ + "/boot.art")),
+                    Field(&DexoptSystemServerArgs::bootClasspathImageFds,
+                          Contains(FdOf(artifacts.ImagePath()))),
+                    Field(&DexoptSystemServerArgs::bootClasspathVdexFds,
+                          Contains(FdOf(artifacts.VdexPath()))),
+                    Field(&DexoptSystemServerArgs::bootClasspathOatFds,
+                          Contains(FdOf(artifacts.OatPath()))))))
         .Times(odrefresh->AllSystemServerJars().size())
         .WillRepeatedly(Return(0));
     EXPECT_EQ(
@@ -483,20 +510,23 @@
 
     // Boot image is on /system.
     OdrArtifacts artifacts =
-        OdrArtifacts::ForBootImageExtension(framework_dir_ + "/x86_64/boot-framework.art");
+        OdrArtifacts::ForBootImage(framework_dir_ + "/x86_64/boot-framework.art");
     auto file1 = ScopedCreateEmptyFile(artifacts.ImagePath());
     auto file2 = ScopedCreateEmptyFile(artifacts.VdexPath());
     auto file3 = ScopedCreateEmptyFile(artifacts.OatPath());
 
-    EXPECT_CALL(
-        *mock_odr_dexopt,
-        DoDexoptSystemServer(AllOf(Field(&DexoptSystemServerArgs::isBootImageOnSystem, Eq(true)),
-                                   Field(&DexoptSystemServerArgs::bootClasspathImageFds,
-                                         Contains(FdOf(artifacts.ImagePath()))),
-                                   Field(&DexoptSystemServerArgs::bootClasspathVdexFds,
-                                         Contains(FdOf(artifacts.VdexPath()))),
-                                   Field(&DexoptSystemServerArgs::bootClasspathOatFds,
-                                         Contains(FdOf(artifacts.OatPath()))))))
+    EXPECT_CALL(*mock_odr_dexopt,
+                DoDexoptSystemServer(
+                    AllOf(Field(&DexoptSystemServerArgs::bootImage,
+                                Eq(android::base::StringPrintf("%s/boot.art:%s/boot-framework.art",
+                                                               art_javalib_dir_.c_str(),
+                                                               framework_dir_.c_str()))),
+                          Field(&DexoptSystemServerArgs::bootClasspathImageFds,
+                                Contains(FdOf(artifacts.ImagePath()))),
+                          Field(&DexoptSystemServerArgs::bootClasspathVdexFds,
+                                Contains(FdOf(artifacts.VdexPath()))),
+                          Field(&DexoptSystemServerArgs::bootClasspathOatFds,
+                                Contains(FdOf(artifacts.OatPath()))))))
         .Times(odrefresh->AllSystemServerJars().size())
         .WillRepeatedly(Return(0));
     EXPECT_EQ(
diff --git a/test/odsign/src/com/android/tests/odsign/ArtifactsSignedTest.java b/test/odsign/src/com/android/tests/odsign/ArtifactsSignedTest.java
index 25a46e3..f29baa7 100644
--- a/test/odsign/src/com/android/tests/odsign/ArtifactsSignedTest.java
+++ b/test/odsign/src/com/android/tests/odsign/ArtifactsSignedTest.java
@@ -43,9 +43,9 @@
     // Verifying that they are generated for the correct architectures is currently out of
     // scope for this test.
     private static final String[] REQUIRED_ARTIFACT_NAMES = {
-        "boot-framework.art",
-        "boot-framework.oat",
-        "boot-framework.vdex",
+        "boot.art",
+        "boot.oat",
+        "boot.vdex",
         "system@framework@services.jar@classes.vdex",
         "system@framework@services.jar@classes.odex",
         "system@framework@services.jar@classes.art",
diff --git a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
index 101e5e2..f00ff14 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OdsignTestUtils.java
@@ -106,7 +106,7 @@
         final String zygotePid = result.getStdout().trim().split("\\s+")[0];
         assertTrue(!zygotePid.isEmpty());
 
-        final String grepPattern = ART_APEX_DALVIK_CACHE_DIRNAME + ".*boot-framework";
+        final String grepPattern = ART_APEX_DALVIK_CACHE_DIRNAME + ".*boot";
         return Optional.of(getMappedArtifacts(zygotePid, grepPattern));
     }
 
diff --git a/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java b/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
index bcce9a0..9c1949c 100644
--- a/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
+++ b/test/odsign/test-src/com/android/tests/odsign/OnDeviceSigningHostTest.java
@@ -118,8 +118,6 @@
     private String getSystemServerIsa(String mappedArtifact) {
         // Artifact path for system server artifacts has the form:
         //    ART_APEX_DALVIK_CACHE_DIRNAME + "/<arch>/system@framework@some.jar@classes.odex"
-        // `mappedArtifacts` may include other artifacts, such as boot-framework.oat that are not
-        // prefixed by the architecture.
         String[] pathComponents = mappedArtifact.split("/");
         return pathComponents[pathComponents.length - 2];
     }
@@ -161,13 +159,13 @@
 
     private void verifyZygoteLoadedArtifacts(String zygoteName, Set<String> mappedArtifacts)
             throws Exception {
-        final String bootExtensionName = "boot-framework";
+        final String bootImageStem = "boot";
 
-        assertTrue("Expect 3 boot-framework artifacts", mappedArtifacts.size() == 3);
+        assertTrue("Expect 3 bootclasspath artifacts", mappedArtifacts.size() == 3);
 
         String allArtifacts = mappedArtifacts.stream().collect(Collectors.joining(","));
         for (String extension : OdsignTestUtils.BCP_ARTIFACT_EXTENSIONS) {
-            final String artifact = bootExtensionName + extension;
+            final String artifact = bootImageStem + extension;
             final boolean found = mappedArtifacts.stream().anyMatch(a -> a.endsWith(artifact));
             assertTrue(zygoteName + " " + artifact + " not found: '" + allArtifacts + "'", found);
         }