diff options
author | 2019-12-23 06:59:06 -0800 | |
---|---|---|
committer | 2020-02-08 00:01:36 +0000 | |
commit | b682ea4d1f9f88ef41589007f385398033d61b65 (patch) | |
tree | b843077f05e2fe37ce5f566dfc823552f289f562 | |
parent | 77aa6807053a830fe5c951c7cb700813d7e2e27b (diff) |
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
-rw-r--r-- | runtime/Android.bp | 1 | ||||
-rw-r--r-- | runtime/class_loader_context.cc | 71 | ||||
-rw-r--r-- | runtime/class_loader_context.h | 34 | ||||
-rw-r--r-- | runtime/class_loader_context_test.cc | 180 | ||||
-rw-r--r-- | runtime/class_loader_utils.h | 8 | ||||
-rw-r--r-- | runtime/common_runtime_test.cc | 35 | ||||
-rw-r--r-- | runtime/common_runtime_test.h | 5 | ||||
-rw-r--r-- | runtime/native/dalvik_system_BaseDexClassLoader.cc | 70 | ||||
-rw-r--r-- | runtime/native/dalvik_system_BaseDexClassLoader.h | 29 | ||||
-rw-r--r-- | runtime/runtime.cc | 2 |
10 files changed, 419 insertions, 16 deletions
diff --git a/runtime/Android.bp b/runtime/Android.bp index 17cbd9d243..63c5d13b1a 100644 --- a/runtime/Android.bp +++ b/runtime/Android.bp @@ -175,6 +175,7 @@ libart_cc_defaults { "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 e835702baa..5dfbfe945f 100644 --- a/runtime/class_loader_context.cc +++ b/runtime/class_loader_context.cc @@ -569,6 +569,46 @@ std::string ClassLoaderContext::EncodeContextForOatFile(const std::string& base_ 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 @@ static bool CollectDexFilesFromSupportedClassLoader(ScopedObjectAccessAlreadyRun 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 @@ std::unique_ptr<ClassLoaderContext> ClassLoaderContext::CreateContextForClassLoa 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 87415cfe8e..73b8476f84 100644 --- a/runtime/class_loader_context.h +++ b/runtime/class_loader_context.h @@ -49,6 +49,12 @@ class ClassLoaderContext { 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 @@ class ClassLoaderContext { // 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 @@ class ClassLoaderContext { // 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 008327834b..e082c8cbcf 100644 --- a/runtime/class_loader_context_test.cc +++ b/runtime/class_loader_context_test.cc @@ -1136,6 +1136,186 @@ TEST_F(ClassLoaderContextTest, EncodeForDex2oatIMC) { 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 1c353604fa..934c92b630 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 64d2503801..40f35b3a97 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -272,16 +272,19 @@ jobject CommonRuntimeTestImpl::LoadDex(const char* dex_name) { 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::LoadDexInWellKnownClassLoader(const std::string& 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::LoadDexInPathClassLoader(const std::string& dex_n 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 2e9e078be9..750051576d 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -152,9 +152,12 @@ class CommonRuntimeTestImpl : public CommonArtTestImpl { 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 0000000000..607395d308 --- /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 0000000000..4ec03ef7a6 --- /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 615e37e92a..a40049f8fe 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 @@ jobject Runtime::GetSystemClassLoader() const { 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); |