Verify the class loader context when loading oat files

Previously, the oat_file_manager would expect and perform validation on a
simple classpath: a list of dex files separated by ':'.

This is no longer enough since the oat file may encode a chain of class
loaders now. The CL moves the validation logic in ClassLoaderContext and
extends it to verify the complete chain of class loaders.

Test: m test-art-host
Bug: 38138251
Change-Id: I8ac9c65db1a14909aaecb04fa7a7115ddedc673f
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index a6e5c21..678ae8f 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -353,38 +353,6 @@
       << "attempt=" << dex_files_open_attempted_ << ", result=" << dex_files_open_result_;
 }
 
-bool ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-    const std::string& context_spec,
-    std::vector<std::string>* out_classpath,
-    std::vector<uint32_t>* out_checksums,
-    bool* out_is_special_shared_library) {
-  ClassLoaderContext context;
-  if (!context.Parse(context_spec, /*parse_checksums*/ true)) {
-    LOG(ERROR) << "Invalid class loader context: " << context_spec;
-    return false;
-  }
-
-  *out_is_special_shared_library = context.special_shared_library_;
-  if (context.special_shared_library_) {
-    return true;
-  }
-
-  if (context.class_loader_chain_.empty()) {
-    return true;
-  }
-
-  // TODO(calin): assert that we only have a PathClassLoader until the logic for
-  // checking the context covers all case.
-  CHECK_EQ(1u, context.class_loader_chain_.size());
-  const ClassLoaderInfo& info = context.class_loader_chain_[0];
-  CHECK_EQ(kPathClassLoader, info.type);
-  DCHECK_EQ(info.classpath.size(), info.checksums.size());
-
-  *out_classpath = info.classpath;
-  *out_checksums = info.checksums;
-  return true;
-}
-
 // Collects the dex files from the give Java dex_file object. Only the dex files with
 // at least 1 class are collected. If a null java_dex_file is passed this method does nothing.
 static bool CollectDexFilesFromJavaDexFile(ObjPtr<mirror::Object> java_dex_file,
@@ -583,6 +551,8 @@
 std::unique_ptr<ClassLoaderContext> ClassLoaderContext::CreateContextForClassLoader(
     jobject class_loader,
     jobjectArray dex_elements) {
+  CHECK(class_loader != nullptr);
+
   ScopedObjectAccess soa(Thread::Current());
   StackHandleScope<2> hs(soa.Self());
   Handle<mirror::ClassLoader> h_class_loader =
@@ -590,8 +560,6 @@
   Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements =
       hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements));
 
-  CHECK(h_class_loader != nullptr);
-
   std::unique_ptr<ClassLoaderContext> result(new ClassLoaderContext(/*owns_the_dex_files*/ false));
   if (result->AddInfoToContextFromClassLoader(soa, h_class_loader, h_dex_elements)) {
     return result;
@@ -600,5 +568,60 @@
   }
 }
 
+bool ClassLoaderContext::VerifyClassLoaderContextMatch(const std::string& context_spec) {
+  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_) {
+    return true;
+  }
+
+  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();
+    return false;
+  }
+
+  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);
+      return false;
+    }
+    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();
+      return false;
+    }
+
+    DCHECK_EQ(info.classpath.size(), info.checksums.size());
+    DCHECK_EQ(expected_info.classpath.size(), expected_info.checksums.size());
+
+    for (size_t k = 0; k < info.classpath.size(); k++) {
+      if (info.classpath[k] != expected_info.classpath[k]) {
+        LOG(WARNING) << "ClassLoaderContext classpath element mismatch for position " << i
+            << ". expected=" << expected_info.classpath[k]
+            << ", found=" << info.classpath[k];
+        return false;
+      }
+      if (info.checksums[k] != expected_info.checksums[k]) {
+        LOG(WARNING) << "ClassLoaderContext classpath element checksum mismatch for position " << i
+            << ". expected=" << expected_info.checksums[k]
+            << ", found=" << info.checksums[k];
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 }  // namespace art
 
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 81c8903..8c65e2e 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -78,6 +78,13 @@
   // Should only be called if OpenDexFiles() returned true.
   std::vector<const DexFile*> FlattenOpenedDexFiles() const;
 
+  // Verifies that the current context is identical to the context encoded as `context_spec`.
+  // Identical means:
+  //    - 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)
+  bool VerifyClassLoaderContextMatch(const std::string& context_spec);
+
   // Creates the class loader context from the given string.
   // The format: ClassLoaderType1[ClasspathElem1:ClasspathElem2...];ClassLoaderType2[...]...
   // ClassLoaderType is either "PCL" (PathClassLoader) or "DLC" (DelegateLastClassLoader).
@@ -86,17 +93,6 @@
   // class loader for the source dex files.
   static std::unique_ptr<ClassLoaderContext> Create(const std::string& spec);
 
-  // Decodes the class loader context stored in the oat file with EncodeContextForOatFile.
-  // Returns true if the format matches, or false otherwise. If the return is true, the out
-  // arguments will contain the classpath dex files, their checksums and whether or not the
-  // context is a special shared library.
-  // The method asserts that the context is made out of only one PathClassLoader.
-  static bool DecodePathClassLoaderContextFromOatFileKey(
-      const std::string& context_spec,
-      std::vector<std::string>* out_classpath,
-      std::vector<uint32_t>* out_checksums,
-      bool* out_is_special_shared_library);
-
   // Creates a context for the given class_loader and dex_elements.
   // The method will walk the parent chain starting from `class_loader` and add their dex files
   // to the current class loaders chain. The `dex_elements` will be added at the end of the
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 50905c4..659db4b 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -101,6 +101,14 @@
     return ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr);
   }
 
+  std::unique_ptr<ClassLoaderContext> ParseContextWithChecksums(const std::string& context_spec) {
+    std::unique_ptr<ClassLoaderContext> context(new ClassLoaderContext());
+    if (!context->Parse(context_spec, /*parse_checksums*/ true)) {
+      return nullptr;
+    }
+    return context;
+  }
+
   void VerifyContextForClassLoader(ClassLoaderContext* context) {
     ASSERT_TRUE(context != nullptr);
     ASSERT_TRUE(context->dex_files_open_attempted_);
@@ -336,42 +344,6 @@
   ASSERT_EQ(expected_encoding, context->EncodeContextForOatFile(""));
 }
 
-TEST_F(ClassLoaderContextTest, DecodeOatFileKey) {
-  std::string oat_file_encoding = "PCL[a.dex*123:b.dex*456]";
-  std::vector<std::string> classpath;
-  std::vector<uint32_t> checksums;
-  bool is_special_shared_library;
-  bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-      oat_file_encoding,
-      &classpath,
-      &checksums,
-      &is_special_shared_library);
-  ASSERT_TRUE(result);
-  ASSERT_FALSE(is_special_shared_library);
-  ASSERT_EQ(2u, classpath.size());
-  ASSERT_EQ(2u, checksums.size());
-  ASSERT_EQ("a.dex", classpath[0]);
-  ASSERT_EQ(123u, checksums[0]);
-  ASSERT_EQ("b.dex", classpath[1]);
-  ASSERT_EQ(456u, checksums[1]);
-}
-
-TEST_F(ClassLoaderContextTest, DecodeOatFileKeySpecialLibrary) {
-  std::string oat_file_encoding = "&";
-  std::vector<std::string> classpath;
-  std::vector<uint32_t> checksums;
-  bool is_special_shared_library;
-  bool result = ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-      oat_file_encoding,
-      &classpath,
-      &checksums,
-      &is_special_shared_library);
-  ASSERT_TRUE(result);
-  ASSERT_TRUE(is_special_shared_library);
-  ASSERT_TRUE(classpath.empty());
-  ASSERT_TRUE(checksums.empty());
-}
-
 // TODO(calin) add a test which creates the context for a class loader together with dex_elements.
 TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
   // The chain is
@@ -402,4 +374,47 @@
   VerifyClassLoaderPCLFromTestDex(context.get(), 3, "ForClassLoaderA");
 }
 
+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);
+
+  VerifyContextSize(context.get(), 2);
+  VerifyClassLoaderPCL(context.get(), 0, "a.dex:b.dex");
+  VerifyClassLoaderDLC(context.get(), 1, "c.dex");
+
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context_spec));
+
+  std::string wrong_class_loader_type = "PCL[a.dex*123:b.dex*456];PCL[c.dex*890]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_type));
+
+  std::string wrong_class_loader_order = "DLC[c.dex*890];PCL[a.dex*123:b.dex*456]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_class_loader_order));
+
+  std::string wrong_classpath_order = "PCL[b.dex*456:a.dex*123];DLC[c.dex*890]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_classpath_order));
+
+  std::string wrong_checksum = "PCL[a.dex*999:b.dex*456];DLC[c.dex*890]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_checksum));
+
+  std::string wrong_extra_class_loader = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890];PCL[d.dex*321]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_class_loader));
+
+  std::string wrong_extra_classpath = "PCL[a.dex*123:b.dex*456];DLC[c.dex*890:d.dex*321]";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_extra_classpath));
+
+  std::string wrong_spec = "PCL[a.dex*999:b.dex*456];DLC[";
+  ASSERT_FALSE(context->VerifyClassLoaderContextMatch(wrong_spec));
+}
+
+TEST_F(ClassLoaderContextTest, VerifyClassLoaderContextMatchAfterEncoding) {
+  jobject class_loader_a = LoadDexInPathClassLoader("ForClassLoaderA", nullptr);
+  jobject class_loader_b = LoadDexInDelegateLastClassLoader("ForClassLoaderB", class_loader_a);
+  jobject class_loader_c = LoadDexInPathClassLoader("ForClassLoaderC", class_loader_b);
+  jobject class_loader_d = LoadDexInDelegateLastClassLoader("ForClassLoaderD", class_loader_c);
+
+  std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_d);
+
+  ASSERT_TRUE(context->VerifyClassLoaderContextMatch(context->EncodeContextForOatFile("")));
+}
+
 }  // namespace art
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index b166961..cba85ad 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -264,212 +264,6 @@
   }
 }
 
-template <typename T>
-static void IterateOverJavaDexFile(ObjPtr<mirror::Object> dex_file,
-                                   ArtField* const cookie_field,
-                                   const T& fn)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (dex_file != nullptr) {
-    mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
-    if (long_array == nullptr) {
-      // This should never happen so log a warning.
-      LOG(WARNING) << "Null DexFile::mCookie";
-      return;
-    }
-    int32_t long_array_size = long_array->GetLength();
-    // Start from 1 to skip the oat file.
-    for (int32_t j = 1; j < long_array_size; ++j) {
-      const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
-          long_array->GetWithoutChecks(j)));
-      if (!fn(cp_dex_file)) {
-        return;
-      }
-    }
-  }
-}
-
-template <typename T>
-static void IterateOverPathClassLoader(
-    Handle<mirror::ClassLoader> class_loader,
-    MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements,
-    const T& fn) REQUIRES_SHARED(Locks::mutator_lock_) {
-  // Handle this step.
-  // Handle as if this is the child PathClassLoader.
-  // The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
-  // We need to get the DexPathList and loop through it.
-  ArtField* const cookie_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-  ArtField* const dex_file_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-  ObjPtr<mirror::Object> dex_path_list =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_BaseDexClassLoader_pathList)->
-          GetObject(class_loader.Get());
-  if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
-    // DexPathList has an array dexElements of Elements[] which each contain a dex file.
-    ObjPtr<mirror::Object> dex_elements_obj =
-        jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
-            GetObject(dex_path_list);
-    // Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
-    // at the mCookie which is a DexFile vector.
-    if (dex_elements_obj != nullptr) {
-      dex_elements.Assign(dex_elements_obj->AsObjectArray<mirror::Object>());
-      for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
-        mirror::Object* element = dex_elements->GetWithoutChecks(i);
-        if (element == nullptr) {
-          // Should never happen, fall back to java code to throw a NPE.
-          break;
-        }
-        ObjPtr<mirror::Object> dex_file = dex_file_field->GetObject(element);
-        IterateOverJavaDexFile(dex_file, cookie_field, fn);
-      }
-    }
-  }
-}
-
-static bool GetDexFilesFromClassLoader(
-    ScopedObjectAccessAlreadyRunnable& soa,
-    mirror::ClassLoader* class_loader,
-    std::vector<const DexFile*>* dex_files)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (ClassLinker::IsBootClassLoader(soa, class_loader)) {
-    // The boot class loader. We don't load any of these files, as we know we compiled against
-    // them correctly.
-    return true;
-  }
-
-  // Unsupported class-loader?
-  if (soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_PathClassLoader) !=
-      class_loader->GetClass()) {
-    VLOG(class_linker) << "Unsupported class-loader "
-                       << mirror::Class::PrettyClass(class_loader->GetClass());
-    return false;
-  }
-
-  bool recursive_result = GetDexFilesFromClassLoader(soa, class_loader->GetParent(), dex_files);
-  if (!recursive_result) {
-    // Something wrong up the chain.
-    return false;
-  }
-
-  // Collect all the dex files.
-  auto GetDexFilesFn = [&] (const DexFile* cp_dex_file)
-            REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (cp_dex_file->NumClassDefs() > 0) {
-      dex_files->push_back(cp_dex_file);
-    }
-    return true;  // Continue looking.
-  };
-
-  // Handle for dex-cache-element.
-  StackHandleScope<3> hs(soa.Self());
-  MutableHandle<mirror::ObjectArray<mirror::Object>> dex_elements(
-      hs.NewHandle<mirror::ObjectArray<mirror::Object>>(nullptr));
-  Handle<mirror::ClassLoader> h_class_loader(hs.NewHandle(class_loader));
-
-  IterateOverPathClassLoader(h_class_loader, dex_elements, GetDexFilesFn);
-
-  return true;
-}
-
-static void GetDexFilesFromDexElementsArray(
-    ScopedObjectAccessAlreadyRunnable& soa,
-    Handle<mirror::ObjectArray<mirror::Object>> dex_elements,
-    std::vector<const DexFile*>* dex_files)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  if (dex_elements == nullptr) {
-    // Nothing to do.
-    return;
-  }
-
-  ArtField* const cookie_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexFile_cookie);
-  ArtField* const dex_file_field =
-      jni::DecodeArtField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
-  ObjPtr<mirror::Class> const element_class = soa.Decode<mirror::Class>(
-      WellKnownClasses::dalvik_system_DexPathList__Element);
-  ObjPtr<mirror::Class> const dexfile_class = soa.Decode<mirror::Class>(
-      WellKnownClasses::dalvik_system_DexFile);
-
-  // Collect all the dex files.
-  auto GetDexFilesFn = [&] (const DexFile* cp_dex_file)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    if (cp_dex_file != nullptr && cp_dex_file->NumClassDefs() > 0) {
-      dex_files->push_back(cp_dex_file);
-    }
-    return true;  // Continue looking.
-  };
-
-  for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
-    mirror::Object* element = dex_elements->GetWithoutChecks(i);
-    if (element == nullptr) {
-      continue;
-    }
-
-    // We support this being dalvik.system.DexPathList$Element and dalvik.system.DexFile.
-
-    ObjPtr<mirror::Object> dex_file;
-    if (element_class == element->GetClass()) {
-      dex_file = dex_file_field->GetObject(element);
-    } else if (dexfile_class == element->GetClass()) {
-      dex_file = element;
-    } else {
-      LOG(WARNING) << "Unsupported element in dex_elements: "
-                   << mirror::Class::PrettyClass(element->GetClass());
-      continue;
-    }
-
-    IterateOverJavaDexFile(dex_file, cookie_field, GetDexFilesFn);
-  }
-}
-
-static bool AreSharedLibrariesOk(const std::string& context_spec,
-                                 std::vector<const DexFile*>& dex_files,
-                                 std::string* error_msg) {
-  std::vector<std::string> classpath;
-  std::vector<uint32_t> checksums;
-  bool is_special_shared_library;
-  if (!ClassLoaderContext::DecodePathClassLoaderContextFromOatFileKey(
-          context_spec, &classpath, &checksums, &is_special_shared_library)) {
-    *error_msg = "Could not decode the class loader context from the oat file key.";
-    return false;
-  }
-
-  DCHECK_EQ(classpath.size(), checksums.size());
-
-  // The classpath size should match the number of dex files.
-  if (classpath.size() != dex_files.size()) {
-    *error_msg = "The number of loaded dex files does not match the number of files "
-        "specified in the context. Expected=" + std::to_string(classpath.size()) +
-        ", found=" + std::to_string(dex_files.size());
-    return false;
-  }
-
-  // If we find the special shared library, skip the shared libraries check.
-  if (is_special_shared_library) {
-    return true;
-  }
-
-  // Check that the loaded dex files have the same order and checksums as the shared libraries.
-  for (size_t i = 0; i < dex_files.size(); ++i) {
-    const std::string& dex_location = dex_files[i]->GetLocation();
-    uint32_t dex_location_checksum = dex_files[i]->GetLocationChecksum();
-    std::string absolute_library_path =
-        OatFile::ResolveRelativeEncodedDexLocation(dex_location.c_str(), classpath[i]);
-    if (dex_location != absolute_library_path) {
-      *error_msg = "SharedLibraryCheck: expected=" + absolute_library_path + ", found=" +
-          dex_location;
-      return false;
-    }
-    if (dex_location_checksum  != checksums[i]) {
-      *error_msg = "SharedLibraryCheck: checksum mismatch for " + dex_location + ". Expected=" +
-          std::to_string(checksums[i]) + ", found=" + std::to_string(dex_location_checksum);
-      return false;
-    }
-  }
-
-  return true;
-}
-
 static bool CollisionCheck(std::vector<const DexFile*>& dex_files_loaded,
                            std::vector<const DexFile*>& dex_files_unloaded,
                            std::string* error_msg /*out*/) {
@@ -554,52 +348,38 @@
   DCHECK(oat_file != nullptr);
   DCHECK(error_msg != nullptr);
 
-  std::vector<const DexFile*> dex_files_loaded;
-
-  // Try to get dex files from the given class loader. If the class loader is null, or we do
-  // not support one of the class loaders in the chain, we do nothing and assume the collision
-  // check has succeeded.
-  bool class_loader_ok = false;
-  {
-    ScopedObjectAccess soa(Thread::Current());
-    StackHandleScope<2> hs(Thread::Current());
-    Handle<mirror::ClassLoader> h_class_loader =
-        hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader));
-    Handle<mirror::ObjectArray<mirror::Object>> h_dex_elements =
-        hs.NewHandle(soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements));
-    if (h_class_loader != nullptr &&
-        GetDexFilesFromClassLoader(soa, h_class_loader.Get(), &dex_files_loaded)) {
-      class_loader_ok = true;
-
-      // In this case, also take into account the dex_elements array, if given. We don't need to
-      // read it otherwise, as we'll compare against all open oat files anyways.
-      GetDexFilesFromDexElementsArray(soa, h_dex_elements, &dex_files_loaded);
-    } else if (h_class_loader != nullptr) {
-      VLOG(class_linker) << "Something unsupported with "
-                         << mirror::Class::PrettyClass(h_class_loader->GetClass());
-
-      // This is a class loader we don't recognize. Our earlier strategy would
-      // be to perform a global duplicate class check (with all loaded oat files)
-      // but that seems overly conservative - we have no way of knowing that
-      // those files are present in the same loader hierarchy. Among other
-      // things, it hurt GMS core and its filtering class loader.
-    }
+  // If the class_loader is null there's not much we can do. This happens if a dex files is loaded
+  // directly with DexFile APIs instead of using class loaders.
+  if (class_loader == nullptr) {
+    LOG(WARNING) << "Opening an oat file without a class loader. "
+        << "Are you using the deprecated DexFile APIs?";
+    return false;
   }
 
-  // Exit if we find a class loader we don't recognize. Proceed to check shared
-  // libraries and do a full class loader check otherwise.
-  if (!class_loader_ok) {
-      LOG(WARNING) << "Skipping duplicate class check due to unrecognized classloader";
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::CreateContextForClassLoader(class_loader, dex_elements);
+
+  // The context might be null if there are unrecognized class loaders in the chain or they
+  // don't meet sensible sanity conditions. In this case we assume that the app knows what it's
+  // doing and accept the oat file.
+  // Note that this has correctness implications as we cannot guarantee that the class resolution
+  // used during compilation is OK (b/37777332).
+  if (context == nullptr) {
+      LOG(WARNING) << "Skipping duplicate class check due to unsupported classloader";
       return false;
   }
 
-  // Exit if shared libraries are ok. Do a full duplicate classes check otherwise.
-  const std::string
-      shared_libraries(oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
-  if (AreSharedLibrariesOk(shared_libraries, dex_files_loaded, error_msg)) {
+  // If the pat file loading context matches the context used during compilation then we accept
+  // the oat file without addition checks
+  if (context->VerifyClassLoaderContextMatch(
+      oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey))) {
     return false;
   }
 
+  // The class loader context does not match. Perform a full duplicate classes check.
+
+  std::vector<const DexFile*> dex_files_loaded = context->FlattenOpenedDexFiles();
+
   // Vector that holds the newly opened dex files live, this is done to prevent leaks.
   std::vector<std::unique_ptr<const DexFile>> opened_dex_files;