Add support for shared libaries in class loader context.

For example:
PCL[a.dex:b.dex]{PCL[s1.dex]#PCL[s2.dex:s3.dex]};DLC[c.dex:d.dex]{DLC[s4.dex]}

Rewrite the class_loader_chain_ to be a chain instead of a vector,
to simplify processing and encoding of contexts.

bug: 111174995
Test: class_loader_context_test, test.py
Change-Id: I7c9f71b67a91b43ae534721b43dc4fdb8e0b6ec4
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index dd10f3c..de9fe22 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -42,6 +42,9 @@
 static constexpr char kDelegateLastClassLoaderString[] = "DLC";
 static constexpr char kClassLoaderOpeningMark = '[';
 static constexpr char kClassLoaderClosingMark = ']';
+static constexpr char kClassLoaderSharedLibraryOpeningMark = '{';
+static constexpr char kClassLoaderSharedLibraryClosingMark = '}';
+static constexpr char kClassLoaderSharedLibrarySeparator = '#';
 static constexpr char kClassLoaderSeparator = ';';
 static constexpr char kClasspathSeparator = ':';
 static constexpr char kDexFileChecksumSeparator = '*';
@@ -58,17 +61,35 @@
       dex_files_open_result_(true),
       owns_the_dex_files_(owns_the_dex_files) {}
 
+// Utility method to add parent and shared libraries of `info` into
+// the `work_list`.
+static void AddToWorkList(
+    ClassLoaderContext::ClassLoaderInfo* info,
+    std::vector<ClassLoaderContext::ClassLoaderInfo*>& work_list) {
+  if (info->parent != nullptr) {
+    work_list.push_back(info->parent.get());
+  }
+  for (size_t i = 0; i < info->shared_libraries.size(); ++i) {
+    work_list.push_back(info->shared_libraries[i].get());
+  }
+}
+
 ClassLoaderContext::~ClassLoaderContext() {
-  if (!owns_the_dex_files_) {
+  if (!owns_the_dex_files_ && class_loader_chain_ != nullptr) {
     // If the context does not own the dex/oat files release the unique pointers to
     // make sure we do not de-allocate them.
-    for (ClassLoaderInfo& info : class_loader_chain_) {
-      for (std::unique_ptr<OatFile>& oat_file : info.opened_oat_files) {
+    std::vector<ClassLoaderInfo*> work_list;
+    work_list.push_back(class_loader_chain_.get());
+    while (!work_list.empty()) {
+      ClassLoaderInfo* info = work_list.back();
+      work_list.pop_back();
+      for (std::unique_ptr<OatFile>& oat_file : info->opened_oat_files) {
         oat_file.release();  // NOLINT b/117926937
       }
-      for (std::unique_ptr<const DexFile>& dex_file : info.opened_dex_files) {
+      for (std::unique_ptr<const DexFile>& dex_file : info->opened_dex_files) {
         dex_file.release();  // NOLINT b/117926937
       }
+      AddToWorkList(info, work_list);
     }
   }
 }
@@ -86,11 +107,16 @@
   }
 }
 
-// The expected format is: "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]".
+// The expected format is:
+// "ClassLoaderType1[ClasspathElem1*Checksum1:ClasspathElem2*Checksum2...]{ClassLoaderType2[...]}".
 // The checksum part of the format is expected only if parse_cheksums is true.
-bool ClassLoaderContext::ParseClassLoaderSpec(const std::string& class_loader_spec,
-                                              ClassLoaderType class_loader_type,
-                                              bool parse_checksums) {
+std::unique_ptr<ClassLoaderContext::ClassLoaderInfo> ClassLoaderContext::ParseClassLoaderSpec(
+    const std::string& class_loader_spec,
+    bool parse_checksums) {
+  ClassLoaderType class_loader_type = ExtractClassLoaderType(class_loader_spec);
+  if (class_loader_type == kInvalidClassLoader) {
+    return nullptr;
+  }
   const char* class_loader_type_str = GetClassLoaderTypeName(class_loader_type);
   size_t type_str_size = strlen(class_loader_type_str);
 
@@ -98,21 +124,24 @@
 
   // Check the opening and closing markers.
   if (class_loader_spec[type_str_size] != kClassLoaderOpeningMark) {
-    return false;
+    return nullptr;
   }
-  if (class_loader_spec[class_loader_spec.length() - 1] != kClassLoaderClosingMark) {
-    return false;
+  if ((class_loader_spec[class_loader_spec.length() - 1] != kClassLoaderClosingMark) &&
+      (class_loader_spec[class_loader_spec.length() - 1] != kClassLoaderSharedLibraryClosingMark)) {
+    return nullptr;
   }
 
+  size_t closing_index = class_loader_spec.find_first_of(kClassLoaderClosingMark);
+
   // At this point we know the format is ok; continue and extract the classpath.
   // Note that class loaders with an empty class path are allowed.
   std::string classpath = class_loader_spec.substr(type_str_size + 1,
-                                                   class_loader_spec.length() - type_str_size - 2);
+                                                   closing_index - type_str_size - 1);
 
-  class_loader_chain_.push_back(ClassLoaderInfo(class_loader_type));
+  std::unique_ptr<ClassLoaderInfo> info(new ClassLoaderInfo(class_loader_type));
 
   if (!parse_checksums) {
-    Split(classpath, kClasspathSeparator, &class_loader_chain_.back().classpath);
+    Split(classpath, kClasspathSeparator, &info->classpath);
   } else {
     std::vector<std::string> classpath_elements;
     Split(classpath, kClasspathSeparator, &classpath_elements);
@@ -120,18 +149,37 @@
       std::vector<std::string> dex_file_with_checksum;
       Split(element, kDexFileChecksumSeparator, &dex_file_with_checksum);
       if (dex_file_with_checksum.size() != 2) {
-        return false;
+        return nullptr;
       }
       uint32_t checksum = 0;
       if (!android::base::ParseUint(dex_file_with_checksum[1].c_str(), &checksum)) {
-        return false;
+        return nullptr;
       }
-      class_loader_chain_.back().classpath.push_back(dex_file_with_checksum[0]);
-      class_loader_chain_.back().checksums.push_back(checksum);
+      info->classpath.push_back(dex_file_with_checksum[0]);
+      info->checksums.push_back(checksum);
     }
   }
 
-  return true;
+  if (class_loader_spec[class_loader_spec.length() - 1] == kClassLoaderSharedLibraryClosingMark) {
+    size_t start_index = class_loader_spec.find_first_of(kClassLoaderSharedLibraryOpeningMark);
+    if (start_index == std::string::npos) {
+      return nullptr;
+    }
+    std::string shared_libraries_spec =
+        class_loader_spec.substr(start_index + 1, class_loader_spec.length() - start_index - 2);
+    std::vector<std::string> shared_libraries;
+    Split(shared_libraries_spec, kClassLoaderSharedLibrarySeparator, &shared_libraries);
+    for (const std::string& shared_library_spec : shared_libraries) {
+      std::unique_ptr<ClassLoaderInfo> shared_library(
+          ParseInternal(shared_library_spec, parse_checksums));
+      if (shared_library == nullptr) {
+        return nullptr;
+      }
+      info->shared_libraries.push_back(std::move(shared_library));
+    }
+  }
+
+  return info;
 }
 
 // Extracts the class loader type from the given spec.
@@ -157,7 +205,7 @@
     // By default we load the dex files in a PathClassLoader.
     // So an empty spec is equivalent to an empty PathClassLoader (this happens when running
     // tests)
-    class_loader_chain_.push_back(ClassLoaderInfo(kPathClassLoader));
+    class_loader_chain_.reset(new ClassLoaderInfo(kPathClassLoader));
     return true;
   }
 
@@ -169,21 +217,102 @@
     return true;
   }
 
-  std::vector<std::string> class_loaders;
-  Split(spec, kClassLoaderSeparator, &class_loaders);
+  CHECK(class_loader_chain_ == nullptr);
+  class_loader_chain_.reset(ParseInternal(spec, parse_checksums));
+  return class_loader_chain_ != nullptr;
+}
 
-  for (const std::string& class_loader : class_loaders) {
-    ClassLoaderType type = ExtractClassLoaderType(class_loader);
-    if (type == kInvalidClassLoader) {
-      LOG(ERROR) << "Invalid class loader type: " << class_loader;
-      return false;
+ClassLoaderContext::ClassLoaderInfo* ClassLoaderContext::ParseInternal(
+    const std::string& spec, bool parse_checksums) {
+  CHECK(!spec.empty());
+  CHECK_NE(spec, OatFile::kSpecialSharedLibrary);
+  std::string remaining = spec;
+  std::unique_ptr<ClassLoaderInfo> first(nullptr);
+  ClassLoaderInfo* previous_iteration = nullptr;
+  while (!remaining.empty()) {
+    std::string class_loader_spec;
+    size_t first_class_loader_separator = remaining.find_first_of(kClassLoaderSeparator);
+    size_t first_shared_library_open =
+        remaining.find_first_of(kClassLoaderSharedLibraryOpeningMark);
+    if (first_class_loader_separator == std::string::npos) {
+      // Only one class loader, for example:
+      // PCL[...]
+      class_loader_spec = remaining;
+      remaining = "";
+    } else if ((first_shared_library_open == std::string::npos) ||
+               (first_shared_library_open > first_class_loader_separator)) {
+      // We found a class loader spec without shared libraries, for example:
+      // PCL[...];PCL[...]{...}
+      class_loader_spec = remaining.substr(0, first_class_loader_separator);
+      remaining = remaining.substr(first_class_loader_separator + 1,
+                                   remaining.size() - first_class_loader_separator - 1);
+    } else {
+      // The class loader spec contains shared libraries. Find the matching closing
+      // shared library marker for it.
+
+      // Counter of opened shared library marker we've encountered so far.
+      uint32_t counter = 1;
+      // The index at which we're operating in the loop.
+      uint32_t string_index = first_shared_library_open + 1;
+      while (counter != 0) {
+        size_t shared_library_close =
+            remaining.find_first_of(kClassLoaderSharedLibraryClosingMark, string_index);
+        size_t shared_library_open =
+            remaining.find_first_of(kClassLoaderSharedLibraryOpeningMark, string_index);
+        if (shared_library_close == std::string::npos) {
+          // No matching closing market. Return an error.
+          LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec;
+          return nullptr;
+        }
+
+        if ((shared_library_open == std::string::npos) ||
+            (shared_library_close < shared_library_open)) {
+          // We have seen a closing marker. Decrement the counter.
+          --counter;
+          if (counter == 0) {
+            // Found the matching closing marker.
+            class_loader_spec = remaining.substr(0, shared_library_close + 1);
+
+            // Compute the remaining string to analyze.
+            if (remaining.size() == shared_library_close + 1) {
+              remaining = "";
+            } else if ((remaining.size() == shared_library_close + 2) ||
+                       (remaining.at(shared_library_close + 1) != kClassLoaderSeparator)) {
+              LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec;
+              return nullptr;
+            } else {
+              remaining = remaining.substr(shared_library_close + 2,
+                                           remaining.size() - shared_library_close - 2);
+            }
+          } else {
+            // Move the search index forward.
+            string_index = shared_library_close + 1;
+          }
+        } else {
+          // New nested opening marker. Increment the counter and move the search
+          // index after the marker.
+          ++counter;
+          string_index = shared_library_open + 1;
+        }
+      }
     }
-    if (!ParseClassLoaderSpec(class_loader, type, parse_checksums)) {
-      LOG(ERROR) << "Invalid class loader spec: " << class_loader;
-      return false;
+
+    std::unique_ptr<ClassLoaderInfo> info =
+        ParseClassLoaderSpec(class_loader_spec, parse_checksums);
+    if (info == nullptr) {
+      LOG(ERROR) << "Invalid class loader spec: " << class_loader_spec;
+      return nullptr;
+    }
+    if (first == nullptr) {
+      first.reset(info.release());
+      previous_iteration = first.get();
+    } else {
+      CHECK(previous_iteration != nullptr);
+      previous_iteration->parent.reset(info.release());
+      previous_iteration = previous_iteration->parent.get();
     }
   }
-  return true;
+  return first.release();
 }
 
 // Opens requested class path files and appends them to opened_dex_files. If the dex files have
@@ -208,9 +337,13 @@
   // 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...
   const ArtDexFileLoader dex_file_loader;
-  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) {
+  std::vector<ClassLoaderInfo*> work_list;
+  work_list.push_back(class_loader_chain_.get());
+  while (!work_list.empty()) {
+    ClassLoaderInfo* info = work_list.back();
+    work_list.pop_back();
+    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 location = cp_elem;
       if (location[0] != '/' && !classpath_dir.empty()) {
@@ -225,7 +358,7 @@
                                 Runtime::Current()->IsVerificationEnabled(),
                                 /*verify_checksum=*/ true,
                                 &error_msg,
-                                &info.opened_dex_files)) {
+                                &info->opened_dex_files)) {
         // If we fail to open the dex file because it's been stripped, try to open the dex file
         // from its corresponding oat file.
         // This could happen when we need to recompile a pre-build whose dex code has been stripped.
@@ -237,10 +370,10 @@
         std::vector<std::unique_ptr<const DexFile>> oat_dex_files;
         if (oat_file != nullptr &&
             OatFileAssistant::LoadDexFiles(*oat_file, location, &oat_dex_files)) {
-          info.opened_oat_files.push_back(std::move(oat_file));
-          info.opened_dex_files.insert(info.opened_dex_files.end(),
-                                       std::make_move_iterator(oat_dex_files.begin()),
-                                       std::make_move_iterator(oat_dex_files.end()));
+          info->opened_oat_files.push_back(std::move(oat_file));
+          info->opened_dex_files.insert(info->opened_dex_files.end(),
+                                        std::make_move_iterator(oat_dex_files.begin()),
+                                        std::make_move_iterator(oat_dex_files.end()));
         } else {
           LOG(WARNING) << "Could not open dex files from location: " << location;
           dex_files_open_result_ = false;
@@ -257,14 +390,15 @@
     // 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++) {
-      std::unique_ptr<const DexFile>& dex = info.opened_dex_files[k];
-      info.classpath.push_back(dex->GetLocation());
-      info.checksums.push_back(dex->GetLocationChecksum());
+    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++) {
+      std::unique_ptr<const DexFile>& dex = info->opened_dex_files[k];
+      info->classpath.push_back(dex->GetLocation());
+      info->checksums.push_back(dex->GetLocationChecksum());
     }
+    AddToWorkList(info, work_list);
   }
 
   return dex_files_open_result_;
@@ -275,24 +409,33 @@
   CHECK(!dex_files_open_attempted_)
       << "RemoveLocationsFromClasspaths cannot be call after OpenDexFiles";
 
+  if (class_loader_chain_ == nullptr) {
+    return false;
+  }
+
   std::set<std::string> canonical_locations;
   for (const std::string& location : locations) {
     canonical_locations.insert(DexFileLoader::GetDexCanonicalLocation(location.c_str()));
   }
   bool removed_locations = false;
-  for (ClassLoaderInfo& info : class_loader_chain_) {
-    size_t initial_size = info.classpath.size();
+  std::vector<ClassLoaderInfo*> work_list;
+  work_list.push_back(class_loader_chain_.get());
+  while (!work_list.empty()) {
+    ClassLoaderInfo* info = work_list.back();
+    work_list.pop_back();
+    size_t initial_size = info->classpath.size();
     auto kept_it = std::remove_if(
-        info.classpath.begin(),
-        info.classpath.end(),
+        info->classpath.begin(),
+        info->classpath.end(),
         [canonical_locations](const std::string& location) {
             return ContainsElement(canonical_locations,
                                    DexFileLoader::GetDexCanonicalLocation(location.c_str()));
         });
-    info.classpath.erase(kept_it, info.classpath.end());
-    if (initial_size != info.classpath.size()) {
+    info->classpath.erase(kept_it, info->classpath.end());
+    if (initial_size != info->classpath.size()) {
       removed_locations = true;
     }
+    AddToWorkList(info, work_list);
   }
   return removed_locations;
 }
@@ -315,11 +458,11 @@
   }
 
   if (stored_context != nullptr) {
-    DCHECK_EQ(class_loader_chain_.size(), stored_context->class_loader_chain_.size());
+    DCHECK_EQ(GetParentChainSize(), stored_context->GetParentChainSize());
   }
 
   std::ostringstream out;
-  if (class_loader_chain_.empty()) {
+  if (class_loader_chain_ == nullptr) {
     // We can get in this situation if the context was created with a class path containing the
     // source dex files which were later removed (happens during run-tests).
     out << GetClassLoaderTypeName(kPathClassLoader)
@@ -328,62 +471,122 @@
     return out.str();
   }
 
-  for (size_t i = 0; i < class_loader_chain_.size(); i++) {
-    const ClassLoaderInfo& info = class_loader_chain_[i];
-    if (i > 0) {
-      out << kClassLoaderSeparator;
-    }
-    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) {
-        // dex2oat only needs the base location. It cannot accept multidex locations.
-        // So ensure we only add each file once.
-        bool new_insert = seen_locations.insert(
-            DexFileLoader::GetBaseLocation(dex_file->GetLocation())).second;
-        if (!new_insert) {
-          continue;
-        }
-      }
-      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;
-      }
-      // Find paths that were relative and convert them back from absolute.
-      if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) {
-        out << location.substr(base_dir.length() + 1).c_str();
-      } else {
-        out << location.c_str();
-      }
-      // dex2oat does not need the checksums.
-      if (!for_dex2oat) {
-        out << kDexFileChecksumSeparator;
-        out << dex_file->GetLocationChecksum();
-      }
-    }
-    out << kClassLoaderClosingMark;
-  }
+  EncodeContextInternal(
+      *class_loader_chain_,
+      base_dir,
+      for_dex2oat,
+      (stored_context == nullptr ? nullptr : stored_context->class_loader_chain_.get()),
+      out);
   return out.str();
 }
 
+void ClassLoaderContext::EncodeContextInternal(const ClassLoaderInfo& info,
+                                               const std::string& base_dir,
+                                               bool for_dex2oat,
+                                               ClassLoaderInfo* stored_info,
+                                               std::ostringstream& out) const {
+  out << GetClassLoaderTypeName(info.type);
+  out << kClassLoaderOpeningMark;
+  std::set<std::string> seen_locations;
+  SafeMap<std::string, std::string> remap;
+  if (stored_info != nullptr) {
+    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_info->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) {
+      // dex2oat only needs the base location. It cannot accept multidex locations.
+      // So ensure we only add each file once.
+      bool new_insert = seen_locations.insert(
+          DexFileLoader::GetBaseLocation(dex_file->GetLocation())).second;
+      if (!new_insert) {
+        continue;
+      }
+    }
+    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;
+    }
+    // Find paths that were relative and convert them back from absolute.
+    if (!base_dir.empty() && location.substr(0, base_dir.length()) == base_dir) {
+      out << location.substr(base_dir.length() + 1).c_str();
+    } else {
+      out << location.c_str();
+    }
+    // dex2oat does not need the checksums.
+    if (!for_dex2oat) {
+      out << kDexFileChecksumSeparator;
+      out << dex_file->GetLocationChecksum();
+    }
+  }
+  out << kClassLoaderClosingMark;
+
+  if (!info.shared_libraries.empty()) {
+    out << kClassLoaderSharedLibraryOpeningMark;
+    for (uint32_t i = 0; i < info.shared_libraries.size(); ++i) {
+      if (i > 0) {
+        out << kClassLoaderSharedLibrarySeparator;
+      }
+      EncodeContextInternal(
+          *info.shared_libraries[i].get(),
+          base_dir,
+          for_dex2oat,
+          (stored_info == nullptr ? nullptr : stored_info->shared_libraries[i].get()),
+          out);
+    }
+    out << kClassLoaderSharedLibraryClosingMark;
+  }
+  if (info.parent != nullptr) {
+    out << kClassLoaderSeparator;
+    EncodeContextInternal(
+        *info.parent.get(),
+        base_dir,
+        for_dex2oat,
+        (stored_info == nullptr ? nullptr : stored_info->parent.get()),
+        out);
+  }
+}
+
+// Returns the WellKnownClass for the given class loader type.
+static jclass GetClassLoaderClass(ClassLoaderContext::ClassLoaderType type) {
+  switch (type) {
+    case ClassLoaderContext::kPathClassLoader:
+      return WellKnownClasses::dalvik_system_PathClassLoader;
+    case ClassLoaderContext::kDelegateLastClassLoader:
+      return WellKnownClasses::dalvik_system_DelegateLastClassLoader;
+    case ClassLoaderContext::kInvalidClassLoader: break;  // will fail after the switch.
+  }
+  LOG(FATAL) << "Invalid class loader type " << type;
+  UNREACHABLE();
+}
+
+static jobject CreateClassLoaderInternal(Thread* self,
+                                         const ClassLoaderContext::ClassLoaderInfo& info)
+      REQUIRES_SHARED(Locks::mutator_lock_) {
+  CHECK(info.shared_libraries.empty()) << "Class loader shared library not implemented yet";
+  jobject parent = nullptr;
+  if (info.parent != nullptr) {
+    parent = CreateClassLoaderInternal(self, *info.parent.get());
+  }
+  std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(
+      info.opened_dex_files);
+  return Runtime::Current()->GetClassLinker()->CreateWellKnownClassLoader(
+      self,
+      class_path_files,
+      GetClassLoaderClass(info.type),
+      parent);
+}
+
 jobject ClassLoaderContext::CreateClassLoader(
     const std::vector<const DexFile*>& compilation_sources) const {
   CheckDexFilesOpened("CreateClassLoader");
@@ -393,22 +596,14 @@
 
   ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
 
-  if (class_loader_chain_.empty()) {
+  if (class_loader_chain_ == nullptr) {
     return class_linker->CreatePathClassLoader(self, compilation_sources);
   }
 
-  // Create the class loaders starting from the top most parent (the one on the last position
-  // in the chain) but omit the first class loader which will contain the compilation_sources and
-  // needs special handling.
-  jobject current_parent = nullptr;  // the starting parent is the BootClassLoader.
-  for (size_t i = class_loader_chain_.size() - 1; i > 0; i--) {
-    std::vector<const DexFile*> class_path_files = MakeNonOwningPointerVector(
-        class_loader_chain_[i].opened_dex_files);
-    current_parent = class_linker->CreateWellKnownClassLoader(
-        self,
-        class_path_files,
-        GetClassLoaderClass(class_loader_chain_[i].type),
-        current_parent);
+  // Create the class loader of the parent.
+  jobject parent = nullptr;
+  if (class_loader_chain_->parent != nullptr) {
+    parent = CreateClassLoaderInternal(self, *class_loader_chain_->parent.get());
   }
 
   // We set up all the parents. Move on to create the first class loader.
@@ -416,26 +611,34 @@
   // we need to resolve classes from it the classpath elements come first.
 
   std::vector<const DexFile*> first_class_loader_classpath = MakeNonOwningPointerVector(
-      class_loader_chain_[0].opened_dex_files);
+      class_loader_chain_->opened_dex_files);
   first_class_loader_classpath.insert(first_class_loader_classpath.end(),
-                                    compilation_sources.begin(),
-                                    compilation_sources.end());
+                                      compilation_sources.begin(),
+                                      compilation_sources.end());
 
   return class_linker->CreateWellKnownClassLoader(
       self,
       first_class_loader_classpath,
-      GetClassLoaderClass(class_loader_chain_[0].type),
-      current_parent);
+      GetClassLoaderClass(class_loader_chain_->type),
+      parent);
 }
 
 std::vector<const DexFile*> ClassLoaderContext::FlattenOpenedDexFiles() const {
   CheckDexFilesOpened("FlattenOpenedDexFiles");
 
   std::vector<const DexFile*> result;
-  for (const ClassLoaderInfo& info : class_loader_chain_) {
-    for (const std::unique_ptr<const DexFile>& dex_file : info.opened_dex_files) {
+  if (class_loader_chain_ == nullptr) {
+    return result;
+  }
+  std::vector<ClassLoaderInfo*> work_list;
+  work_list.push_back(class_loader_chain_.get());
+  while (!work_list.empty()) {
+    ClassLoaderInfo* info = work_list.back();
+    work_list.pop_back();
+    for (const std::unique_ptr<const DexFile>& dex_file : info->opened_dex_files) {
       result.push_back(dex_file.get());
     }
+    AddToWorkList(info, work_list);
   }
   return result;
 }
@@ -632,12 +835,21 @@
     GetDexFilesFromDexElementsArray(soa, dex_elements, &dex_files_loaded);
   }
 
-  class_loader_chain_.push_back(ClassLoaderContext::ClassLoaderInfo(type));
-  ClassLoaderInfo& info = class_loader_chain_.back();
+  ClassLoaderInfo* info = new ClassLoaderContext::ClassLoaderInfo(type);
+  if (class_loader_chain_ == nullptr) {
+    class_loader_chain_.reset(info);
+  } else {
+    ClassLoaderInfo* child = class_loader_chain_.get();
+    while (child->parent != nullptr) {
+      child = child->parent.get();
+    }
+    child->parent.reset(info);
+  }
+
   for (const DexFile* dex_file : dex_files_loaded) {
-    info.classpath.push_back(dex_file->GetLocation());
-    info.checksums.push_back(dex_file->GetLocationChecksum());
-    info.opened_dex_files.emplace_back(dex_file);
+    info->classpath.push_back(dex_file->GetLocation());
+    info->checksums.push_back(dex_file->GetLocationChecksum());
+    info->opened_dex_files.emplace_back(dex_file);
   }
 
   // We created the ClassLoaderInfo for the current loader. Move on to its parent.
@@ -696,7 +908,7 @@
   // collision check.
   if (expected_context.special_shared_library_) {
     // Special case where we are the only entry in the class path.
-    if (class_loader_chain_.size() == 1 && class_loader_chain_[0].classpath.size() == 0) {
+    if (class_loader_chain_->parent == nullptr && class_loader_chain_->classpath.size() == 0) {
       return VerificationResult::kVerifies;
     }
     return VerificationResult::kForcedToSkipChecks;
@@ -704,41 +916,43 @@
     return VerificationResult::kForcedToSkipChecks;
   }
 
-  if (expected_context.class_loader_chain_.size() != class_loader_chain_.size()) {
-    LOG(WARNING) << "ClassLoaderContext size mismatch. expected="
-        << expected_context.class_loader_chain_.size()
-        << ", actual=" << class_loader_chain_.size()
-        << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+  ClassLoaderInfo* info = class_loader_chain_.get();
+  ClassLoaderInfo* expected = expected_context.class_loader_chain_.get();
+  CHECK(info != nullptr);
+  CHECK(expected != nullptr);
+  if (!ClassLoaderInfoMatch(*info, *expected, context_spec, verify_names, verify_checksums)) {
     return VerificationResult::kMismatch;
   }
+  return VerificationResult::kVerifies;
+}
 
-  for (size_t i = 0; i < class_loader_chain_.size(); i++) {
-    const ClassLoaderInfo& info = class_loader_chain_[i];
-    const ClassLoaderInfo& expected_info = expected_context.class_loader_chain_[i];
-    if (info.type != expected_info.type) {
-      LOG(WARNING) << "ClassLoaderContext type mismatch for position " << i
-          << ". expected=" << GetClassLoaderTypeName(expected_info.type)
-          << ", found=" << GetClassLoaderTypeName(info.type)
+bool ClassLoaderContext::ClassLoaderInfoMatch(
+    const ClassLoaderInfo& info,
+    const ClassLoaderInfo& expected_info,
+    const std::string& context_spec,
+    bool verify_names,
+    bool verify_checksums) const {
+  if (info.type != expected_info.type) {
+    LOG(WARNING) << "ClassLoaderContext type mismatch"
+        << ". expected=" << GetClassLoaderTypeName(expected_info.type)
+        << ", found=" << GetClassLoaderTypeName(info.type)
+        << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+    return false;
+  }
+  if (info.classpath.size() != expected_info.classpath.size()) {
+    LOG(WARNING) << "ClassLoaderContext classpath size mismatch"
+          << ". expected=" << expected_info.classpath.size()
+          << ", found=" << info.classpath.size()
           << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-      return VerificationResult::kMismatch;
-    }
-    if (info.classpath.size() != expected_info.classpath.size()) {
-      LOG(WARNING) << "ClassLoaderContext classpath size mismatch for position " << i
-            << ". expected=" << expected_info.classpath.size()
-            << ", found=" << info.classpath.size()
-            << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-      return VerificationResult::kMismatch;
-    }
+    return false;
+  }
 
-    if (verify_checksums) {
-      DCHECK_EQ(info.classpath.size(), info.checksums.size());
-      DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
-    }
+  if (verify_checksums) {
+    DCHECK_EQ(info.classpath.size(), info.checksums.size());
+    DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
+  }
 
-    if (!verify_names) {
-      continue;
-    }
-
+  if (verify_names) {
     for (size_t k = 0; k < info.classpath.size(); k++) {
       // Compute the dex location that must be compared.
       // We shouldn't do a naive comparison `info.classpath[k] == expected_info.classpath[k]`
@@ -778,34 +992,58 @@
 
       // Compare the locations.
       if (dex_name != expected_dex_name) {
-        LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
+        LOG(WARNING) << "ClassLoaderContext classpath element mismatch"
             << ". expected=" << expected_info.classpath[k]
             << ", found=" << info.classpath[k]
             << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-        return VerificationResult::kMismatch;
+        return false;
       }
 
       // Compare the checksums.
       if (info.checksums[k] != expected_info.checksums[k]) {
-        LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
+        LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch"
                      << ". expected=" << expected_info.checksums[k]
                      << ", found=" << info.checksums[k]
                      << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
-        return VerificationResult::kMismatch;
+        return false;
       }
     }
   }
-  return VerificationResult::kVerifies;
-}
 
-jclass ClassLoaderContext::GetClassLoaderClass(ClassLoaderType type) {
-  switch (type) {
-    case kPathClassLoader: return WellKnownClasses::dalvik_system_PathClassLoader;
-    case kDelegateLastClassLoader: return WellKnownClasses::dalvik_system_DelegateLastClassLoader;
-    case kInvalidClassLoader: break;  // will fail after the switch.
+  if (info.shared_libraries.size() != expected_info.shared_libraries.size()) {
+    LOG(WARNING) << "ClassLoaderContext shared library size mismatch. "
+          << "Expected=" << expected_info.classpath.size()
+          << ", found=" << info.classpath.size()
+          << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+    return false;
   }
-  LOG(FATAL) << "Invalid class loader type " << type;
-  UNREACHABLE();
+  for (size_t i = 0; i < info.shared_libraries.size(); ++i) {
+    if (!ClassLoaderInfoMatch(*info.shared_libraries[i].get(),
+                              *expected_info.shared_libraries[i].get(),
+                              context_spec,
+                              verify_names,
+                              verify_checksums)) {
+      return false;
+    }
+  }
+  if (info.parent.get() == nullptr) {
+    if (expected_info.parent.get() != nullptr) {
+      LOG(WARNING) << "ClassLoaderContext parent mismatch. "
+            << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+      return false;
+    }
+    return true;
+  } else if (expected_info.parent.get() == nullptr) {
+    LOG(WARNING) << "ClassLoaderContext parent mismatch. "
+          << " (" << context_spec << " | " << EncodeContextForOatFile("") << ")";
+    return false;
+  } else {
+    return ClassLoaderInfoMatch(*info.parent.get(),
+                                *expected_info.parent.get(),
+                                context_spec,
+                                verify_names,
+                                verify_checksums);
+  }
 }
 
 }  // namespace art
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index a4268aa..37cef81 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -154,10 +154,11 @@
   // This will return a context with a single and empty PathClassLoader.
   static std::unique_ptr<ClassLoaderContext> Default();
 
- private:
   struct ClassLoaderInfo {
     // The type of this class loader.
     ClassLoaderType type;
+    // Shared libraries this context has.
+    std::vector<std::unique_ptr<ClassLoaderInfo>> shared_libraries;
     // The list of class path elements that this loader loads.
     // Note that this list may contain relative paths.
     std::vector<std::string> classpath;
@@ -171,13 +172,35 @@
     // After OpenDexFiles, in case some of the dex files were opened from their oat files
     // this holds the list of opened oat files.
     std::vector<std::unique_ptr<OatFile>> opened_oat_files;
+    // The parent class loader.
+    std::unique_ptr<ClassLoaderInfo> parent;
 
     explicit ClassLoaderInfo(ClassLoaderType cl_type) : type(cl_type) {}
   };
 
+ private:
   // Creates an empty context (with no class loaders).
   ClassLoaderContext();
 
+  // Get the parent of the class loader chain at depth `index`.
+  ClassLoaderInfo* GetParent(size_t index) const {
+    ClassLoaderInfo* result = class_loader_chain_.get();
+    while ((result != nullptr) && (index-- != 0)) {
+      result = result->parent.get();
+    }
+    return result;
+  }
+
+  size_t GetParentChainSize() const {
+    size_t result = 0;
+    ClassLoaderInfo* info = class_loader_chain_.get();
+    while (info != nullptr) {
+      ++result;
+      info = info->parent.get();
+    }
+    return result;
+  }
+
   // Constructs an empty context.
   // `owns_the_dex_files` specifies whether or not the context will own the opened dex files
   // present in the class loader chain. If `owns_the_dex_files` is true then OpenDexFiles cannot
@@ -188,13 +211,13 @@
   // Reads the class loader spec in place and returns true if the spec is valid and the
   // compilation context was constructed.
   bool Parse(const std::string& spec, bool parse_checksums = false);
+  ClassLoaderInfo* ParseInternal(const std::string& spec, bool parse_checksums);
 
-  // Attempts to parse a single class loader spec for the given class_loader_type.
-  // If successful the class loader spec will be added to the chain.
-  // Returns whether or not the operation was successful.
-  bool ParseClassLoaderSpec(const std::string& class_loader_spec,
-                            ClassLoaderType class_loader_type,
-                            bool parse_checksums = false);
+  // Attempts to parse a single class loader spec.
+  // Returns the ClassLoaderInfo abstraction for this spec, or null if it cannot be parsed.
+  std::unique_ptr<ClassLoaderInfo> ParseClassLoaderSpec(
+      const std::string& class_loader_spec,
+      bool parse_checksums = false);
 
   // CHECKs that the dex files were opened (OpenDexFiles was called and set dex_files_open_result_
   // to true). Aborts if not. The `calling_method` is used in the log message to identify the source
@@ -219,6 +242,20 @@
                             bool for_dex2oat,
                             ClassLoaderContext* stored_context) const;
 
+  // Internal version of `EncodeContext`, which will be called recursively
+  // on the parent and shared libraries.
+  void EncodeContextInternal(const ClassLoaderInfo& info,
+                             const std::string& base_dir,
+                             bool for_dex2oat,
+                             ClassLoaderInfo* stored_info,
+                             std::ostringstream& out) const;
+
+  bool ClassLoaderInfoMatch(const ClassLoaderInfo& info,
+                            const ClassLoaderInfo& expected_info,
+                            const std::string& context_spec,
+                            bool verify_names,
+                            bool verify_checksums) const;
+
   // Extracts the class loader type from the given spec.
   // Return ClassLoaderContext::kInvalidClassLoader if the class loader type is not
   // recognized.
@@ -228,13 +265,8 @@
   // The returned format can be used when parsing a context spec.
   static const char* GetClassLoaderTypeName(ClassLoaderType type);
 
-  // Returns the WellKnownClass for the given class loader type.
-  static jclass GetClassLoaderClass(ClassLoaderType type);
-
-  // The class loader chain represented as a vector.
-  // The parent of class_loader_chain_[i] is class_loader_chain_[i++].
-  // The parent of the last element is assumed to be the boot class loader.
-  std::vector<ClassLoaderInfo> class_loader_chain_;
+  // The class loader chain.
+  std::unique_ptr<ClassLoaderInfo> class_loader_chain_;
 
   // Whether or not the class loader context should be ignored at runtime when loading the oat
   // files. When true, dex2oat will use OatFile::kSpecialSharedLibrary as the classpath key in
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index ea624f1..cb3dc65 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -40,7 +40,7 @@
  public:
   void VerifyContextSize(ClassLoaderContext* context, size_t expected_size) {
     ASSERT_TRUE(context != nullptr);
-    ASSERT_EQ(expected_size, context->class_loader_chain_.size());
+    ASSERT_EQ(expected_size, context->GetParentChainSize());
   }
 
   void VerifyClassLoaderPCL(ClassLoaderContext* context,
@@ -57,6 +57,33 @@
         context, index, ClassLoaderContext::kDelegateLastClassLoader, classpath);
   }
 
+  void VerifyClassLoaderSharedLibraryPCL(ClassLoaderContext* context,
+                                         size_t loader_index,
+                                         size_t shared_library_index,
+                                         const std::string& classpath) {
+    VerifyClassLoaderInfoSL(
+        context, loader_index, shared_library_index, ClassLoaderContext::kPathClassLoader,
+        classpath);
+  }
+
+  void VerifySharedLibrariesSize(ClassLoaderContext* context,
+                                 size_t loader_index,
+                                 size_t expected_size) {
+    ASSERT_TRUE(context != nullptr);
+    ASSERT_GT(context->GetParentChainSize(), loader_index);
+    const ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(loader_index);
+    ASSERT_EQ(info.shared_libraries.size(), expected_size);
+  }
+
+  void VerifyClassLoaderSharedLibraryDLC(ClassLoaderContext* context,
+                                         size_t loader_index,
+                                         size_t shared_library_index,
+                                         const std::string& classpath) {
+    VerifyClassLoaderInfoSL(
+        context, loader_index, shared_library_index, ClassLoaderContext::kDelegateLastClassLoader,
+        classpath);
+  }
+
   void VerifyClassLoaderPCLFromTestDex(ClassLoaderContext* context,
                                        size_t index,
                                        const std::string& test_name) {
@@ -91,7 +118,7 @@
     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];
+    ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(index);
     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;
@@ -168,14 +195,31 @@
                              ClassLoaderContext::ClassLoaderType type,
                              const std::string& classpath) {
     ASSERT_TRUE(context != nullptr);
-    ASSERT_GT(context->class_loader_chain_.size(), index);
-    ClassLoaderContext::ClassLoaderInfo& info = context->class_loader_chain_[index];
+    ASSERT_GT(context->GetParentChainSize(), index);
+    ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(index);
     ASSERT_EQ(type, info.type);
     std::vector<std::string> expected_classpath;
     Split(classpath, ':', &expected_classpath);
     ASSERT_EQ(expected_classpath, info.classpath);
   }
 
+  void VerifyClassLoaderInfoSL(ClassLoaderContext* context,
+                               size_t loader_index,
+                               size_t shared_library_index,
+                               ClassLoaderContext::ClassLoaderType type,
+                               const std::string& classpath) {
+    ASSERT_TRUE(context != nullptr);
+    ASSERT_GT(context->GetParentChainSize(), loader_index);
+    const ClassLoaderContext::ClassLoaderInfo& info = *context->GetParent(loader_index);
+    ASSERT_GT(info.shared_libraries.size(), shared_library_index);
+    const ClassLoaderContext::ClassLoaderInfo& sl =
+        *info.shared_libraries[shared_library_index].get();
+    ASSERT_EQ(type, info.type);
+    std::vector<std::string> expected_classpath;
+    Split(classpath, ':', &expected_classpath);
+    ASSERT_EQ(expected_classpath, sl.classpath);
+  }
+
   void VerifyClassLoaderFromTestDex(ClassLoaderContext* context,
                                     size_t index,
                                     ClassLoaderContext::ClassLoaderType type,
@@ -223,6 +267,23 @@
   VerifyClassLoaderPCL(context.get(), 2, "e.dex");
 }
 
+TEST_F(ClassLoaderContextTest, ParseSharedLibraries) {
+  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
+      "PCL[a.dex:b.dex]{PCL[s1.dex]#PCL[s2.dex:s3.dex]};DLC[c.dex:d.dex]{DLC[s4.dex]}");
+  VerifyContextSize(context.get(), 2);
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex");
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "s2.dex:s3.dex");
+  VerifyClassLoaderDLC(context.get(), 1, "c.dex:d.dex");
+  VerifyClassLoaderSharedLibraryDLC(context.get(), 1, 0, "s4.dex");
+}
+
+TEST_F(ClassLoaderContextTest, ParseEnclosingSharedLibraries) {
+  std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(
+      "PCL[a.dex:b.dex]{PCL[s1.dex]{PCL[s2.dex:s3.dex];PCL[s4.dex]}}");
+  VerifyContextSize(context.get(), 1);
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "s1.dex");
+}
+
 TEST_F(ClassLoaderContextTest, ParseValidEmptyContextDLC) {
   std::unique_ptr<ClassLoaderContext> context =
       ClassLoaderContext::Create("DLC[]");
@@ -230,6 +291,13 @@
   VerifyClassLoaderDLC(context.get(), 0, "");
 }
 
+TEST_F(ClassLoaderContextTest, ParseValidEmptyContextSharedLibrary) {
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("DLC[]{}");
+  VerifyContextSize(context.get(), 1);
+  VerifySharedLibrariesSize(context.get(), 0, 0);
+}
+
 TEST_F(ClassLoaderContextTest, ParseValidContextSpecialSymbol) {
   std::unique_ptr<ClassLoaderContext> context =
     ClassLoaderContext::Create(OatFile::kSpecialSharedLibrary);
@@ -243,6 +311,11 @@
   ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCLa.dex]"));
   ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL{a.dex}"));
   ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex];DLC[b.dex"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex]{ABC};DLC[b.dex"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("PCL[a.dex]{};DLC[b.dex"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]}"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC[s4.dex]{"));
+  ASSERT_TRUE(nullptr == ClassLoaderContext::Create("DLC{DLC[s4.dex]}"));
 }
 
 TEST_F(ClassLoaderContextTest, OpenInvalidDexFiles) {
@@ -662,6 +735,62 @@
             ClassLoaderContext::VerificationResult::kMismatch);
 }
 
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchWithSL) {
+  std::string context_spec =
+      "PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
+      ";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");
+  VerifyClassLoaderDLC(context.get(), 1, "c.dex");
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 0, "d.dex");
+  VerifyClassLoaderSharedLibraryPCL(context.get(), 0, 1, "f.dex:g.dex");
+
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(context_spec),
+            ClassLoaderContext::VerificationResult::kVerifies);
+
+  std::string wrong_class_loader_type =
+      "PCL[a.dex*123:b.dex*456]{DLC[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
+      ";DLC[c.dex*890]";
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_type),
+            ClassLoaderContext::VerificationResult::kMismatch);
+
+  std::string wrong_class_loader_order =
+      "PCL[a.dex*123:b.dex*456]{PCL[f.dex#098:g.dex#999}#PCL[d.dex*321];PCL[e.dex*654]}"
+      ";DLC[c.dex*890]";
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_class_loader_order),
+            ClassLoaderContext::VerificationResult::kMismatch);
+
+  std::string wrong_classpath_order =
+      "PCL[a.dex*123:b.dex*456]{PCL[d.dex*321];PCL[e.dex*654]#PCL[g.dex*999:f.dex*098]}"
+      ";DLC[c.dex*890]";
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_classpath_order),
+            ClassLoaderContext::VerificationResult::kMismatch);
+
+  std::string wrong_checksum =
+      "PCL[a.dex*123:b.dex*456]{PCL[d.dex*333];PCL[e.dex*654]#PCL[g.dex*999:f.dex*098]}"
+      ";DLC[c.dex*890]";
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_checksum),
+            ClassLoaderContext::VerificationResult::kMismatch);
+
+  std::string wrong_extra_class_loader =
+      "PCL[a.dex*123:b.dex*456]"
+      "{PCL[d.dex*321];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999];PCL[i.dex#444]}"
+      ";DLC[c.dex*890]";
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader),
+            ClassLoaderContext::VerificationResult::kMismatch);
+
+  std::string wrong_extra_classpath =
+      "PCL[a.dex*123:b.dex*456]{PCL[d.dex*321:i.dex#444];PCL[e.dex*654]#PCL[f.dex*098:g.dex*999]}"
+      ";DLC[c.dex*890]";
+  ASSERT_EQ(context->VerifyClassLoaderContextMatch(wrong_extra_classpath),
+            ClassLoaderContext::VerificationResult::kMismatch);
+}
+
 TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
   jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
   jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);