Add arg for overwriting class loader class path

Added stored_context srgument to EncodeContextForOatFile that
overwrites the class path when non-null.

This is used by the --stored-class-loader-context argument. Fixed
the test.

Bug: 70934104
Bug: 67345922
Test: test-art-host-gtest

Change-Id: If877d8cfe9d34eeaa941e9f6df2e12539d9c4a6f
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index 216ad8f..4afc44c 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -254,6 +254,7 @@
     // This will allow the context to VerifyClassLoaderContextMatch which expects or multidex
     // location in the class paths.
     // Note that this will also remove the paths that could not be opened.
+    info.original_classpath = std::move(info.classpath);
     info.classpath.clear();
     info.checksums.clear();
     for (size_t k = opened_dex_files_index; k < info.opened_dex_files.size(); k++) {
@@ -294,20 +295,26 @@
 }
 
 std::string ClassLoaderContext::EncodeContextForDex2oat(const std::string& base_dir) const {
-  return EncodeContext(base_dir, /*for_dex2oat*/ true);
+  return EncodeContext(base_dir, /*for_dex2oat*/ true, /*stored_context*/ nullptr);
 }
 
-std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir) const {
-  return EncodeContext(base_dir, /*for_dex2oat*/ false);
+std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_dir,
+                                                        ClassLoaderContext* stored_context) const {
+  return EncodeContext(base_dir, /*for_dex2oat*/ false, stored_context);
 }
 
 std::string ClassLoaderContext::EncodeContext(const std::string& base_dir,
-                                              bool for_dex2oat) const {
+                                              bool for_dex2oat,
+                                              ClassLoaderContext* stored_context) const {
   CheckDexFilesOpened("EncodeContextForOatFile");
   if (special_shared_library_) {
     return OatFile::kSpecialSharedLibrary;
   }
 
+  if (stored_context != nullptr) {
+    DCHECK_EQ(class_loader_chain_.size(), stored_context->class_loader_chain_.size());
+  }
+
   std::ostringstream out;
   if (class_loader_chain_.empty()) {
     // We can get in this situation if the context was created with a class path containing the
@@ -326,6 +333,15 @@
     out << GetClassLoaderTypeName(info.type);
     out << kClassLoaderOpeningMark;
     std::set<std::string> seen_locations;
+    SafeMap<std::string, std::string> remap;
+    if (stored_context != nullptr) {
+      DCHECK_EQ(info.original_classpath.size(),
+                stored_context->class_loader_chain_[i].classpath.size());
+      for (size_t k = 0; k < info.original_classpath.size(); ++k) {
+        // Note that we don't care if the same name appears twice.
+        remap.Put(info.original_classpath[k], stored_context->class_loader_chain_[i].classpath[k]);
+      }
+    }
     for (size_t k = 0; k < info.opened_dex_files.size(); k++) {
       const std::unique_ptr<const DexFile>& dex_file = info.opened_dex_files[k];
       if (for_dex2oat) {
@@ -337,7 +353,14 @@
           continue;
         }
       }
-      const std::string& location = dex_file->GetLocation();
+      std::string location = dex_file->GetLocation();
+      // If there is a stored class loader remap, fix up the multidex strings.
+      if (!remap.empty()) {
+        std::string base_dex_location = DexFileLoader::GetBaseLocation(location);
+        auto it = remap.find(base_dex_location);
+        CHECK(it != remap.end()) << base_dex_location;
+        location = it->second + DexFileLoader::GetMultiDexSuffix(location);
+      }
       if (k > 0) {
         out << kClasspathSeparator;
       }
@@ -345,7 +368,7 @@
       if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) {
         out << location.substr(base_dir.length() + 1).c_str();
       } else {
-        out << dex_file->GetLocation().c_str();
+        out << location.c_str();
       }
       // dex2oat does not need the checksums.
       if (!for_dex2oat) {
@@ -776,4 +799,3 @@
 }
 
 }  // namespace art
-
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 231acc4..1c83007 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -84,9 +84,12 @@
   // (so that it can be read and verified at runtime against the actual class
   // loader hierarchy).
   // Should only be called if OpenDexFiles() returned true.
+  // If stored context is non-null, the stored names are overwritten by the class path from the
+  // stored context.
   // E.g. if the context is PCL[a.dex:b.dex] this will return
   // "PCL[a.dex*a_checksum*b.dex*a_checksum]".
-  std::string EncodeContextForOatFile(const std::string& base_dir) const;
+  std::string EncodeContextForOatFile(const std::string& base_dir,
+                                      ClassLoaderContext* stored_context = nullptr) const;
 
   // Encodes the context as a string suitable to be passed to dex2oat.
   // This is the same as EncodeContextForOatFile but without adding the checksums
@@ -150,6 +153,8 @@
     // The list of class path elements that this loader loads.
     // Note that this list may contain relative paths.
     std::vector<std::string> classpath;
+    // Original opened class path (ignoring multidex).
+    std::vector<std::string> original_classpath;
     // The list of class path elements checksums.
     // May be empty if the checksums are not given when the context is created.
     std::vector<uint32_t> checksums;
@@ -202,7 +207,9 @@
   // location). Otherwise, for oat files, the encoding adds all the dex files (including multidex)
   // together with their checksums.
   // Should only be called if OpenDexFiles() returned true.
-  std::string EncodeContext(const std::string& base_dir, bool for_dex2oat) const;
+  std::string EncodeContext(const std::string& base_dir,
+                            bool for_dex2oat,
+                            ClassLoaderContext* stored_context) const;
 
   // Extracts the class loader type from the given spec.
   // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not