Update the context classpath after the dex files are opened.

The classpath stored in the context maybe be out of sync with the list of
opened dex files if any of the dex file is a multidex or cannot be opened.

This CL ensures that OpenDexFiles updates the classpath with the dex file
locations that were open.

The change does not affect the current use of ClassLoaderContext. It is a
preparatory step for b/62269291

Bug: 62269291
Test: m test-art-host-gtest-class_loader_context_test
Change-Id: Ibe675df9b2c06a4df8189f0d72df8b4061603a0e
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index e97c6a0..3265b94 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -187,7 +187,10 @@
 // Opens requested class path files and appends them to opened_dex_files. If the dex files have
 // been stripped, this opens them from their oat files (which get added to opened_oat_files).
 bool ClassLoaderContext::OpenDexFiles(InstructionSet isa, const std::string& classpath_dir) {
-  CHECK(!dex_files_open_attempted_) << "OpenDexFiles should not be called twice";
+  if (dex_files_open_attempted_) {
+    // Do not attempt to re-open the files if we already tried.
+    return dex_files_open_result_;
+  }
 
   dex_files_open_attempted_ = true;
   // Assume we can open all dex files. If not, we will set this to false as we go.
@@ -203,6 +206,7 @@
   // TODO(calin): Refine the dex opening interface to be able to tell if an archive contains
   // no dex files. So that we can distinguish the real failures...
   for (ClassLoaderInfo& info : class_loader_chain_) {
+    size_t opened_dex_files_index = info.opened_dex_files.size();
     for (const std::string& cp_elem : info.classpath) {
       // If path is relative, append it to the provided base directory.
       std::string raw_location = cp_elem;
@@ -249,6 +253,23 @@
         }
       }
     }
+
+    // We finished opening the dex files from the classpath.
+    // Now update the classpath and the checksum with the locations of the dex files.
+    //
+    // We do this because initially the classpath contains the paths of the dex files; and
+    // some of them might be multi-dexes. So in order to have a consistent view we replace all the
+    // file paths with the actual dex locations being loaded.
+    // 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.classpath.clear();
+    info.checksums.clear();
+    for (size_t k = opened_dex_files_index; k < info.opened_dex_files.size(); k++) {
+      std::unique_ptr<const DexFile>& dex = info.opened_dex_files[k];
+      info.classpath.push_back(dex->GetLocation());
+      info.checksums.push_back(dex->GetLocationChecksum());
+    }
   }
 
   return dex_files_open_result_;
@@ -633,13 +654,20 @@
 }
 
 bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) const {
+  DCHECK(dex_files_open_attempted_);
+  DCHECK(dex_files_open_result_);
+
   ClassLoaderContext expected_context;
   if (!expected_context.Parse(context_spec, /*parse_checksums*/ true)) {
     LOG(WARNING) << "Invalid class loader context: " << context_spec;
     return false;
   }
 
-  if (expected_context.special_shared_library_) {
+  // Special shared library contexts always match. They essentially instruct the runtime
+  // to ignore the class path check because the oat file is known to be loaded in different
+  // contexts. OatFileManager will further verify if the oat file can be loaded based on the
+  // collision check.
+  if (special_shared_library_ || expected_context.special_shared_library_) {
     return true;
   }
 
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 9afa880..692a6cd 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -41,7 +41,12 @@
   // to ClassLoaderInfo::opened_oat_files. The 'classpath_dir' argument specifies the directory to
   // use for the relative class paths.
   // Returns true if all dex files where successfully opened.
-  // It may be called only once per ClassLoaderContext. The second call will abort.
+  // It may be called only once per ClassLoaderContext. Subsequent calls will return the same
+  // result without doing anything.
+  //
+  // This will replace the class path locations with the locations of the opened dex files.
+  // (Note that one dex file can contain multidexes. Each multidex will be added to the classpath
+  // separately.)
   //
   // Note that a "false" return could mean that either an apk/jar contained no dex files or
   // that we hit a I/O or checksum mismatch error.
@@ -98,6 +103,7 @@
   //    - the number and type of the class loaders from the chain matches
   //    - the class loader from the same position have the same classpath
   //      (the order and checksum of the dex files matches)
+  // This should be called after OpenDexFiles().
   bool VerifyClassLoaderContextMatch(const std::string& context_spec) const;
 
   // Creates the class loader context from the given string.
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 60b5fa4..4ad1bcd 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -74,39 +74,29 @@
   void VerifyOpenDexFiles(
       ClassLoaderContext* context,
       size_t index,
-      std::vector<std::vector<std::unique_ptr<const DexFile>>*>& all_dex_files) {
+      std::vector<std::unique_ptr<const DexFile>>* all_dex_files) {
     ASSERT_TRUE(context != nullptr);
     ASSERT_TRUE(context->dex_files_open_attempted_);
     ASSERT_TRUE(context->dex_files_open_result_);
     ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
-    ASSERT_EQ(all_dex_files.size(), info.classpath.size());
+    ASSERT_EQ(all_dex_files->size(), info.classpath.size());
+    ASSERT_EQ(all_dex_files->size(), info.opened_dex_files.size());
     size_t cur_open_dex_index = 0;
-    for (size_t k = 0; k < all_dex_files.size(); k++) {
-      std::vector<std::unique_ptr<const DexFile>>& dex_files_for_cp_elem = *(all_dex_files[k]);
-      for (size_t i = 0; i < dex_files_for_cp_elem.size(); i++) {
-        ASSERT_LT(cur_open_dex_index, info.opened_dex_files.size());
-
-        std::unique_ptr<const DexFile>& opened_dex_file =
+    for (size_t k = 0; k < all_dex_files->size(); k++) {
+      std::unique_ptr<const DexFile>& opened_dex_file =
             info.opened_dex_files[cur_open_dex_index++];
-        std::unique_ptr<const DexFile>& expected_dex_file = dex_files_for_cp_elem[i];
+      std::unique_ptr<const DexFile>& expected_dex_file = (*all_dex_files)[k];
 
-        std::string expected_location = expected_dex_file->GetBaseLocation();
-        UniqueCPtr<const char[]> expected_real_location(
-            realpath(expected_location.c_str(), nullptr));
-        ASSERT_TRUE(expected_real_location != nullptr) << expected_location;
-        expected_location.assign(expected_real_location.get());
-        expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
+      std::string expected_location = expected_dex_file->GetBaseLocation();
+      UniqueCPtr<const char[]> expected_real_location(
+          realpath(expected_location.c_str(), nullptr));
+      ASSERT_TRUE(expected_real_location != nullptr) << expected_location;
+      expected_location.assign(expected_real_location.get());
+      expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
 
-        ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
-        ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
-
-        std::string class_path_location = info.classpath[k];
-        UniqueCPtr<const char[]> class_path_location_real(
-            realpath(class_path_location.c_str(), nullptr));
-        ASSERT_TRUE(class_path_location_real != nullptr);
-        class_path_location.assign(class_path_location_real.get());
-        ASSERT_EQ(class_path_location, opened_dex_file->GetBaseLocation());
-      }
+      ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
+      ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
+      ASSERT_EQ(info.classpath[k], opened_dex_file->GetLocation());
     }
   }
 
@@ -148,6 +138,11 @@
     }
   }
 
+  void PretendContextOpenedDexFiles(ClassLoaderContext* context) {
+    context->dex_files_open_attempted_ = true;
+    context->dex_files_open_result_ = true;
+  }
+
  private:
   void VerifyClassLoaderInfo(ClassLoaderContext* context,
                              size_t index,
@@ -167,11 +162,9 @@
                                     ClassLoaderContext::ClassLoaderType type,
                                     const std::string& test_name) {
     std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(test_name.c_str());
-    std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files;
-    all_dex_files.push_back(&dex_files);
 
     VerifyClassLoaderInfo(context, index, type, GetTestDexFileName(test_name.c_str()));
-    VerifyOpenDexFiles(context, index, all_dex_files);
+    VerifyOpenDexFiles(context, index, &dex_files);
   }
 };
 
@@ -242,11 +235,8 @@
 
 TEST_F(ClassLoaderContextTest, OpenValidDexFiles) {
   std::string multidex_name = GetTestDexFileName("MultiDex");
-  std::vector<std::unique_ptr<const DexFile>> multidex_files = OpenTestDexFiles("MultiDex");
   std::string myclass_dex_name = GetTestDexFileName("MyClass");
-  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
   std::string dex_name = GetTestDexFileName("Main");
-  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
 
   std::unique_ptr<ClassLoaderContext> context =
       ClassLoaderContext::Create(
@@ -256,14 +246,16 @@
   ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
 
   VerifyContextSize(context.get(), 2);
-  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
-  all_dex_files0.push_back(&multidex_files);
-  all_dex_files0.push_back(&myclass_dex_files);
-  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files1;
-  all_dex_files1.push_back(&dex_files);
 
-  VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
-  VerifyOpenDexFiles(context.get(), 1, all_dex_files1);
+  std::vector<std::unique_ptr<const DexFile>> all_dex_files0 = OpenTestDexFiles("MultiDex");
+  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+  for (size_t i = 0; i < myclass_dex_files.size(); i++) {
+    all_dex_files0.emplace_back(myclass_dex_files[i].release());
+  }
+  VerifyOpenDexFiles(context.get(), 0, &all_dex_files0);
+
+  std::vector<std::unique_ptr<const DexFile>> all_dex_files1 = OpenTestDexFiles("Main");
+  VerifyOpenDexFiles(context.get(), 1, &all_dex_files1);
 }
 
 class ScratchSymLink {
@@ -296,11 +288,10 @@
   ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
 
   VerifyContextSize(context.get(), 1);
-  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
-  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
-  all_dex_files0.push_back(&myclass_dex_files);
 
-  VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
+  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+
+  VerifyOpenDexFiles(context.get(), 0, &myclass_dex_files);
 }
 
 TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
@@ -552,6 +543,9 @@
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatch) {
   std::string context_spec = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890]";
   std::unique_ptr<ClassLoaderContext> context = ParseContextWithChecksums(context_spec);
+  // Pretend that we successfully open the dex files to pass the DCHECKS.
+  // (as it's much easier to test all the corner cases without relying on actual dex files).
+  PretendContextOpenedDexFiles(context.get());
 
   VerifyContextSize(context.get(), 2);
   VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");