Introduce BaseDexClassLoader.computeClassLoaderContextsNative

This will be used to compute the contexts that should be sent over to
the dex load reporter. See associated changes in libcore &
frameworks/base.

Motivation: At the moment of committing there are two classloader
context encoders- one in ART and one in the package manager. The
duplicate logic is susceptible to divergences. For example at the moment
if a package uses shared libraries and has secondary dex files then the
context encoded for secondary dex files will be incorrect[1]. In order to
eliminate this bug and future possible bugs lets centralize where all
classloader context computation is done.

[1]: The context will be incorrect because it doesn't take into account
the shared libraries that are loaded at runtime.

Test: m test-art-host-gtest-class_loader_context_test
Test: m test-art-host-gtest
Test: ./test/testrunner/testrunner.py --host -b
Test: Introduced a set of tests for the new API(s)
Test: See tests in associated libcore & framework/base commits

Bug: 148494302
Change-Id: Id39293a2e1d3d05194f2864f4febb3e652bce075
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 17cbd9d..63c5d13 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -175,6 +175,7 @@
         "native_bridge_art_interface.cc",
         "native_stack_dump.cc",
         "native/dalvik_system_DexFile.cc",
+        "native/dalvik_system_BaseDexClassLoader.cc",
         "native/dalvik_system_VMDebug.cc",
         "native/dalvik_system_VMRuntime.cc",
         "native/dalvik_system_VMStack.cc",
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index e835702..5dfbfe9 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -569,6 +569,46 @@
   return EncodeContext(base_dir, /*for_dex2oat=*/ false, stored_context);
 }
 
+std::map<std::string, std::string>
+ClassLoaderContext::EncodeClassPathContexts(const std::string& base_dir) const {
+  CheckDexFilesOpened("EncodeClassPathContexts");
+  if (class_loader_chain_ == nullptr) {
+    return std::map<std::string, std::string>{};
+  }
+
+  std::map<std::string, std::string> results;
+  std::vector<std::string> dex_locations;
+  std::vector<uint32_t> checksums;
+  dex_locations.reserve(class_loader_chain_->original_classpath.size());
+
+  std::ostringstream encoded_libs_and_parent_stream;
+  EncodeSharedLibAndParent(*class_loader_chain_,
+                           base_dir,
+                           /*for_dex2oat=*/true,
+                           /*stored_info=*/nullptr,
+                           encoded_libs_and_parent_stream);
+  std::string encoded_libs_and_parent(encoded_libs_and_parent_stream.str());
+
+  std::set<std::string> seen_locations;
+  for (const std::string& path : class_loader_chain_->classpath) {
+    // The classpath will contain multiple entries for multidex files, so make sure this is the
+    // first time we're seeing this file.
+    const std::string base_location(DexFileLoader::GetBaseLocation(path));
+    if (!seen_locations.insert(base_location).second) {
+      continue;
+    }
+
+    std::ostringstream out;
+    EncodeClassPath(base_dir, dex_locations, checksums, class_loader_chain_->type, out);
+    out << encoded_libs_and_parent;
+    results.emplace(base_location, out.str());
+
+    dex_locations.push_back(base_location);
+  }
+
+  return results;
+}
+
 std::string ClassLoaderContext::EncodeContext(const std::string& base_dir,
                                               bool for_dex2oat,
                                               ClassLoaderContext* stored_context) const {
@@ -937,9 +977,7 @@
                                                     Handle<mirror::ClassLoader> class_loader,
                                                     std::vector<const DexFile*>* out_dex_files)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-  CHECK(IsPathOrDexClassLoader(soa, class_loader) ||
-        IsDelegateLastClassLoader(soa, class_loader) ||
-        IsInMemoryDexClassLoader(soa, class_loader));
+  CHECK(IsInstanceOfBaseDexClassLoader(soa, class_loader));
 
   // All supported class loaders inherit from BaseDexClassLoader.
   // We need to get the DexPathList and loop through it.
@@ -1152,6 +1190,33 @@
   return result;
 }
 
+std::map<std::string, std::string>
+ClassLoaderContext::EncodeClassPathContextsForClassLoader(jobject class_loader) {
+  std::unique_ptr<ClassLoaderContext> clc =
+      ClassLoaderContext::CreateContextForClassLoader(class_loader, nullptr);
+  if (clc != nullptr) {
+    return clc->EncodeClassPathContexts("");
+  }
+
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ClassLoader> h_class_loader =
+      hs.NewHandle(soa.Decode<mirror::ClassLoader>(class_loader));
+  if (!IsInstanceOfBaseDexClassLoader(soa, h_class_loader)) {
+    return std::map<std::string, std::string>{};
+  }
+
+  std::vector<const DexFile*> dex_files_loaded;
+  CollectDexFilesFromSupportedClassLoader(soa, h_class_loader, &dex_files_loaded);
+
+  std::map<std::string, std::string> results;
+  for (const DexFile* dex_file : dex_files_loaded) {
+    results.emplace(DexFileLoader::GetBaseLocation(dex_file->GetLocation()),
+                    ClassLoaderContext::kUnsupportedClassLoaderContextEncoding);
+  }
+  return results;
+}
+
 ClassLoaderContext::VerificationResult ClassLoaderContext::VerifyClassLoaderContextMatch(
     const std::string& context_spec,
     bool verify_names,
diff --git a/runtime/class_loader_context.h b/runtime/class_loader_context.h
index 87415cf..73b8476 100644
--- a/runtime/class_loader_context.h
+++ b/runtime/class_loader_context.h
@@ -49,6 +49,12 @@
     kInMemoryDexClassLoader = 3
   };
 
+  // Special encoding used to denote a foreign ClassLoader was found when trying to encode class
+  // loader contexts for each classpath element in a ClassLoader. See
+  // EncodeClassPathContextsForClassLoader. Keep in sync with PackageDexUsage in the framework.
+  static constexpr const char* kUnsupportedClassLoaderContextEncoding =
+      "=UnsupportedClassLoaderContext=";
+
   ~ClassLoaderContext();
 
   // Opens requested class path files and appends them to ClassLoaderInfo::opened_dex_files.
@@ -121,6 +127,25 @@
   // Should only be called if OpenDexFiles() returned true.
   std::string EncodeContextForDex2oat(const std::string& base_dir) const;
 
+  // Encodes the contexts for each of the classpath elements in the child-most
+  // classloader. Under the hood EncodeContextForDex2oat is used, so no checksums
+  // will be encoded.
+  // Should only be called if the dex files are opened (either via OpenDexFiles() or by creating the
+  // context from a live class loader).
+  // Notably, for each classpath element the encoded classloader context will contain only the
+  // elements that appear before it in the containing classloader. E.g. if `this` contains
+  // (from child to parent):
+  //
+  // PathClassLoader { multidex.apk!classes.dex, multidex.apk!classes2.dex, foo.dex, bar.dex } ->
+  //    PathClassLoader { baz.dex } -> BootClassLoader
+  //
+  // then the return value will look like:
+  //
+  // `{ "multidex.apk": "PCL[];PCL[baz.dex]",
+  //    "foo.dex"     : "PCL[multidex.apk];PCL[baz.dex]",
+  //    "bar.dex"     : "PCL[multidex.apk:foo.dex];PCL[baz.dex]" }`
+  std::map<std::string, std::string> EncodeClassPathContexts(const std::string& base_dir) const;
+
   // Flattens the opened dex files into the given vector.
   // Should only be called if OpenDexFiles() returned true.
   std::vector<const DexFile*> FlattenOpenedDexFiles() const;
@@ -168,6 +193,15 @@
   // This will return a context with a single and empty PathClassLoader.
   static std::unique_ptr<ClassLoaderContext> Default();
 
+  // Encodes the contexts for each of the classpath elements in `class_loader`. See
+  // ClassLoaderContext::EncodeClassPathContexts for more information about the return value.
+  //
+  // If `class_loader` does not derive from BaseDexClassLoader then an empty map is returned.
+  // Otherwise if a foreign ClassLoader is found in the class loader chain then the results values
+  // will all be ClassLoaderContext::kUnsupportedClassLoaderContextEncoding.
+  static std::map<std::string, std::string> EncodeClassPathContextsForClassLoader(
+      jobject class_loader);
+
   struct ClassLoaderInfo {
     // The type of this class loader.
     ClassLoaderType type;
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index 0083278..e082c8c 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -1136,6 +1136,186 @@
   ASSERT_EQ(expected_encoding, context->EncodeContextForDex2oat(""));
 }
 
+TEST_F(ClassLoaderContextTest, EncodeContextsSinglePath) {
+  jobject class_loader = LoadDexInPathClassLoader("Main", nullptr);
+  std::unique_ptr<ClassLoaderContext> context =
+      CreateContextForClassLoader(class_loader);
+
+  std::map<std::string, std::string> encodings = context->EncodeClassPathContexts("");
+  ASSERT_EQ(1u, encodings.size());
+  ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main")));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsMultiDex) {
+  jobject class_loader = LoadDexInPathClassLoader("MultiDex", nullptr);
+  std::unique_ptr<ClassLoaderContext> context =
+      CreateContextForClassLoader(class_loader);
+
+  std::map<std::string, std::string> encodings = context->EncodeClassPathContexts("");
+  ASSERT_EQ(1u, encodings.size());
+  ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("MultiDex")));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsRepeatedMultiDex) {
+  jobject top_class_loader = LoadDexInPathClassLoader("MultiDex", nullptr);
+  jobject middle_class_loader =
+      LoadDexInPathClassLoader("Main", top_class_loader);
+  jobject bottom_class_loader =
+      LoadDexInPathClassLoader("MultiDex", middle_class_loader);
+  std::unique_ptr<ClassLoaderContext> context =
+      CreateContextForClassLoader(bottom_class_loader);
+
+  std::map<std::string, std::string> encodings = context->EncodeClassPathContexts("");
+  ASSERT_EQ(1u, encodings.size());
+
+  std::string main_dex_name = GetTestDexFileName("Main");
+  std::string multidex_dex_name = GetTestDexFileName("MultiDex");
+  ASSERT_EQ(
+      "PCL[];PCL[" + main_dex_name + "];PCL[" + multidex_dex_name + "]",
+      encodings.at(multidex_dex_name));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsSinglePathWithShared) {
+  jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> libraries = hs.NewHandle(
+    mirror::ObjectArray<mirror::ClassLoader>::Alloc(
+        soa.Self(),
+        GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(),
+        1));
+  libraries->Set(0, soa.Decode<mirror::ClassLoader>(class_loader_a));
+
+  jobject class_loader_b = LoadDexInPathClassLoader(
+      "Main", nullptr, soa.AddLocalReference<jobject>(libraries.Get()));
+
+  std::unique_ptr<ClassLoaderContext> context = CreateContextForClassLoader(class_loader_b);
+
+  std::map<std::string, std::string> encodings = context->EncodeClassPathContexts("");
+  ASSERT_EQ(1u, encodings.size());
+  ASSERT_EQ(
+      "PCL[]{PCL[" + GetTestDexFileName("MyClass") + "]}",
+      encodings.at(GetTestDexFileName("Main")));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsMultiplePaths) {
+  jobject class_loader = LoadDexInPathClassLoader(
+      std::vector<std::string>{ "Main", "MultiDex"}, nullptr);
+
+  std::unique_ptr<ClassLoaderContext> context =
+      CreateContextForClassLoader(class_loader);
+
+  std::map<std::string, std::string> encodings = context->EncodeClassPathContexts("");
+  ASSERT_EQ(2u, encodings.size());
+  ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main")));
+  ASSERT_EQ(
+      "PCL[" + GetTestDexFileName("Main") + "]", encodings.at(GetTestDexFileName("MultiDex")));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsMultiplePathsWithShared) {
+  jobject class_loader_a = LoadDexInPathClassLoader("MyClass", nullptr);
+
+  ScopedObjectAccess soa(Thread::Current());
+  StackHandleScope<1> hs(soa.Self());
+  Handle<mirror::ObjectArray<mirror::ClassLoader>> libraries = hs.NewHandle(
+    mirror::ObjectArray<mirror::ClassLoader>::Alloc(
+        soa.Self(),
+        GetClassRoot<mirror::ObjectArray<mirror::ClassLoader>>(),
+        1));
+  libraries->Set(0, soa.Decode<mirror::ClassLoader>(class_loader_a));
+
+  jobject class_loader_b = LoadDexInPathClassLoader(
+      std::vector<std::string> { "Main", "MultiDex" },
+      nullptr, soa.AddLocalReference<jobject>(libraries.Get()));
+
+  std::unique_ptr<ClassLoaderContext> context =
+      CreateContextForClassLoader(class_loader_b);
+
+  std::map<std::string, std::string> encodings = context->EncodeClassPathContexts("");
+  ASSERT_EQ(2u, encodings.size());
+  const std::string context_suffix =
+      "{PCL[" + GetTestDexFileName("MyClass") + "]}";
+  ASSERT_EQ("PCL[]" + context_suffix, encodings.at(GetTestDexFileName("Main")));
+  ASSERT_EQ(
+      "PCL[" + GetTestDexFileName("Main") + "]" + context_suffix,
+      encodings.at(GetTestDexFileName("MultiDex")));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsIMC) {
+  jobject class_loader_a = LoadDexInPathClassLoader("Main", nullptr);
+  jobject class_loader_b =
+      LoadDexInInMemoryDexClassLoader("MyClass", class_loader_a);
+
+  std::unique_ptr<ClassLoaderContext> context =
+      CreateContextForClassLoader(class_loader_b);
+
+  std::map<std::string, std::string> encodings = context->EncodeClassPathContexts("");
+  ASSERT_EQ(1u, encodings.size());
+  ASSERT_EQ(
+      "IMC[];PCL[" + GetTestDexFileName("Main") + "]",
+      encodings.at("<unknown>"));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsForSingleDex) {
+  jobject class_loader = LoadDexInPathClassLoader("Main", nullptr);
+  std::map<std::string, std::string> encodings =
+      ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
+  ASSERT_EQ(1u, encodings.size());
+  ASSERT_EQ("PCL[]", encodings.at(GetTestDexFileName("Main")));
+}
+
+static jobject CreateForeignClassLoader() {
+  ScopedObjectAccess soa(Thread::Current());
+  JNIEnv* env = soa.Env();
+
+  // We cannot instantiate a ClassLoader directly, so instead we allocate an Object to represent
+  // our foreign ClassLoader (this works because the runtime does proper instanceof checks before
+  // operating on this object.
+  jmethodID ctor = env->GetMethodID(WellKnownClasses::java_lang_Object, "<init>", "()V");
+  return env->NewObject(WellKnownClasses::java_lang_Object, ctor);
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedBase) {
+  std::map<std::string, std::string> empty;
+  ASSERT_EQ(
+      empty, ClassLoaderContext::EncodeClassPathContextsForClassLoader(CreateForeignClassLoader()));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChain) {
+  jobject class_loader = LoadDexInPathClassLoader("Main", CreateForeignClassLoader());
+  std::map<std::string, std::string> encodings =
+      ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
+  ASSERT_EQ(1u, encodings.size());
+  ASSERT_EQ(
+      ClassLoaderContext::kUnsupportedClassLoaderContextEncoding,
+      encodings.at(GetTestDexFileName("Main")));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChainMultiPath) {
+  jobject class_loader = LoadDexInPathClassLoader(std::vector<std::string> { "Main", "MyClass" },
+                                                  CreateForeignClassLoader());
+  std::map<std::string, std::string> encodings =
+      ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
+  ASSERT_EQ(2u, encodings.size());
+  ASSERT_EQ(
+      ClassLoaderContext::kUnsupportedClassLoaderContextEncoding,
+      encodings.at(GetTestDexFileName("Main")));
+  ASSERT_EQ(
+      ClassLoaderContext::kUnsupportedClassLoaderContextEncoding,
+      encodings.at(GetTestDexFileName("MyClass")));
+}
+
+TEST_F(ClassLoaderContextTest, EncodeContextsForUnsupportedChainMultiDex) {
+  jobject class_loader = LoadDexInPathClassLoader("MultiDex", CreateForeignClassLoader());
+  std::map<std::string, std::string> encodings =
+      ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
+  ASSERT_EQ(1u, encodings.size());
+  ASSERT_EQ(
+      ClassLoaderContext::kUnsupportedClassLoaderContextEncoding,
+      encodings.at(GetTestDexFileName("MultiDex")));
+}
+
 // TODO(calin) add a test which creates the context for a class loader together with dex_elements.
 TEST_F(ClassLoaderContextTest, CreateContextForClassLoader) {
   // The chain is
diff --git a/runtime/class_loader_utils.h b/runtime/class_loader_utils.h
index 1c35360..934c92b 100644
--- a/runtime/class_loader_utils.h
+++ b/runtime/class_loader_utils.h
@@ -30,6 +30,14 @@
 
 namespace art {
 
+// Returns true if the given class loader derives from BaseDexClassLoader.
+inline bool IsInstanceOfBaseDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
+                                           Handle<mirror::ClassLoader> class_loader)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  return class_loader->InstanceOf(
+      soa.Decode<mirror::Class>(WellKnownClasses::dalvik_system_BaseDexClassLoader));
+}
+
 // Returns true if the given class loader is either a PathClassLoader or a DexClassLoader.
 // (they both have the same behaviour with respect to class lookup order)
 inline bool IsPathOrDexClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 64d2503..40f35b3 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -272,16 +272,19 @@
   return class_loader;
 }
 
-jobject CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::string& dex_name,
-                                                             jclass loader_class,
-                                                             jobject parent_loader,
-                                                             jobject shared_libraries) {
-  std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
+jobject
+CommonRuntimeTestImpl::LoadDexInWellKnownClassLoader(const std::vector<std::string>& dex_names,
+                                                     jclass loader_class,
+                                                     jobject parent_loader,
+                                                     jobject shared_libraries) {
   std::vector<const DexFile*> class_path;
-  CHECK_NE(0U, dex_files.size());
-  for (auto& dex_file : dex_files) {
-    class_path.push_back(dex_file.get());
-    loaded_dex_files_.push_back(std::move(dex_file));
+  for (const std::string& dex_name : dex_names) {
+    std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles(dex_name.c_str());
+    CHECK_NE(0U, dex_files.size());
+    for (auto& dex_file : dex_files) {
+      class_path.push_back(dex_file.get());
+      loaded_dex_files_.push_back(std::move(dex_file));
+    }
   }
   Thread* self = Thread::Current();
   ScopedObjectAccess soa(self);
@@ -320,7 +323,15 @@
 jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::string& dex_name,
                                                         jobject parent_loader,
                                                         jobject shared_libraries) {
-  return LoadDexInWellKnownClassLoader(dex_name,
+  return LoadDexInPathClassLoader(std::vector<std::string>{ dex_name },
+                                  parent_loader,
+                                  shared_libraries);
+}
+
+jobject CommonRuntimeTestImpl::LoadDexInPathClassLoader(const std::vector<std::string>& names,
+                                                        jobject parent_loader,
+                                                        jobject shared_libraries) {
+  return LoadDexInWellKnownClassLoader(names,
                                        WellKnownClasses::dalvik_system_PathClassLoader,
                                        parent_loader,
                                        shared_libraries);
@@ -328,14 +339,14 @@
 
 jobject CommonRuntimeTestImpl::LoadDexInDelegateLastClassLoader(const std::string& dex_name,
                                                                 jobject parent_loader) {
-  return LoadDexInWellKnownClassLoader(dex_name,
+  return LoadDexInWellKnownClassLoader({ dex_name },
                                        WellKnownClasses::dalvik_system_DelegateLastClassLoader,
                                        parent_loader);
 }
 
 jobject CommonRuntimeTestImpl::LoadDexInInMemoryDexClassLoader(const std::string& dex_name,
                                                                jobject parent_loader) {
-  return LoadDexInWellKnownClassLoader(dex_name,
+  return LoadDexInWellKnownClassLoader({ dex_name },
                                        WellKnownClasses::dalvik_system_InMemoryDexClassLoader,
                                        parent_loader);
 }
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index 2e9e078..7500515 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -152,9 +152,12 @@
   jobject LoadDexInPathClassLoader(const std::string& dex_name,
                                    jobject parent_loader,
                                    jobject shared_libraries = nullptr);
+  jobject LoadDexInPathClassLoader(const std::vector<std::string>& dex_names,
+                                   jobject parent_loader,
+                                   jobject shared_libraries = nullptr);
   jobject LoadDexInDelegateLastClassLoader(const std::string& dex_name, jobject parent_loader);
   jobject LoadDexInInMemoryDexClassLoader(const std::string& dex_name, jobject parent_loader);
-  jobject LoadDexInWellKnownClassLoader(const std::string& dex_name,
+  jobject LoadDexInWellKnownClassLoader(const std::vector<std::string>& dex_names,
                                         jclass loader_class,
                                         jobject parent_loader,
                                         jobject shared_libraries = nullptr);
diff --git a/runtime/native/dalvik_system_BaseDexClassLoader.cc b/runtime/native/dalvik_system_BaseDexClassLoader.cc
new file mode 100644
index 0000000..607395d
--- /dev/null
+++ b/runtime/native/dalvik_system_BaseDexClassLoader.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dalvik_system_DexFile.h"
+
+#include <memory>
+
+#include "class_loader_context.h"
+#include "native_util.h"
+#include "nativehelper/jni_macros.h"
+#include "well_known_classes.h"
+
+namespace art {
+
+static bool append_string(JNIEnv* env, jobjectArray array, uint32_t& i, const std::string& string) {
+  ScopedLocalRef<jstring> jstring(env, env->NewStringUTF(string.c_str()));
+  if (jstring.get() == nullptr) {
+    DCHECK(env->ExceptionCheck());
+    return false;
+  }
+  env->SetObjectArrayElement(array, i++, jstring.get());
+  return true;
+}
+
+static jobjectArray BaseDexClassLoader_computeClassLoaderContextsNative(JNIEnv* env,
+                                                                        jobject class_loader) {
+  CHECK(class_loader != nullptr);
+  std::map<std::string, std::string> contextMap =
+      ClassLoaderContext::EncodeClassPathContextsForClassLoader(class_loader);
+  jobjectArray result = env->NewObjectArray(2 * contextMap.size(),
+                                            WellKnownClasses::java_lang_String,
+                                            nullptr);
+  if (result == nullptr) {
+    DCHECK(env->ExceptionCheck());
+    return nullptr;
+  }
+  uint32_t i = 0;
+  for (const auto& classpath_to_context : contextMap) {
+    const std::string& classpath = classpath_to_context.first;
+    const std::string& context = classpath_to_context.second;
+    if (!append_string(env, result, i, classpath) || !append_string(env, result, i, context)) {
+      return nullptr;
+    }
+  }
+  return result;
+}
+
+static JNINativeMethod gMethods[] = {
+  NATIVE_METHOD(BaseDexClassLoader, computeClassLoaderContextsNative,
+                "()[Ljava/lang/String;"),
+};
+
+void register_dalvik_system_BaseDexClassLoader(JNIEnv* env) {
+  REGISTER_NATIVE_METHODS("dalvik/system/BaseDexClassLoader");
+}
+
+}  // namespace art
diff --git a/runtime/native/dalvik_system_BaseDexClassLoader.h b/runtime/native/dalvik_system_BaseDexClassLoader.h
new file mode 100644
index 0000000..4ec03ef
--- /dev/null
+++ b/runtime/native/dalvik_system_BaseDexClassLoader.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_NATIVE_DALVIK_SYSTEM_BASEDEXCLASSLOADER_H_
+#define ART_RUNTIME_NATIVE_DALVIK_SYSTEM_BASEDEXCLASSLOADER_H_
+
+#include <jni.h>
+#include <unistd.h>
+
+namespace art {
+
+void register_dalvik_system_BaseDexClassLoader(JNIEnv* env);
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_NATIVE_DALVIK_SYSTEM_BASEDEXCLASSLOADER_H_
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 615e37e..a40049f 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -117,6 +117,7 @@
 #include "mirror/var_handle.h"
 #include "monitor.h"
 #include "native/dalvik_system_DexFile.h"
+#include "native/dalvik_system_BaseDexClassLoader.h"
 #include "native/dalvik_system_VMDebug.h"
 #include "native/dalvik_system_VMRuntime.h"
 #include "native/dalvik_system_VMStack.h"
@@ -1955,6 +1956,7 @@
 
 void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
   register_dalvik_system_DexFile(env);
+  register_dalvik_system_BaseDexClassLoader(env);
   register_dalvik_system_VMDebug(env);
   register_dalvik_system_VMRuntime(env);
   register_dalvik_system_VMStack(env);