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);