// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include "base/android/bundle_utils.h" #include "base/android/jni_android.h" #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/check.h" #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_restrictions.h" #include "components/module_installer/android/jni_headers/Module_jni.h" #include "ui/base/resource/resource_bundle_android.h" using base::android::BundleUtils; namespace module_installer { // Allows memory mapping module PAK files on the main thread. // // We expect the memory mapping step to be quick. All it does is retrieve a // region from the APK file that should already be memory mapped and read the // PAK file header. Most of the disk IO is happening when accessing a resource. // And this traditionally happens synchronously on the main thread. // // This class needs to be a friend of base::ScopedAllowBlocking and so cannot be // in the unnamed namespace. class ScopedAllowModulePakLoad { public: ScopedAllowModulePakLoad() {} ~ScopedAllowModulePakLoad() {} private: base::ScopedAllowBlocking allow_blocking_; }; namespace { typedef bool JniRegistrationFunction(JNIEnv* env); void* LoadLibrary(const std::string& library_name, const std::string& module_name) { void* library_handle = nullptr; #if defined(LOAD_FROM_PARTITIONS) // The partition library must be opened via native code (using // android_dlopen_ext() under the hood). There is no need to repeat the // operation on the Java side, because JNI registration is done explicitly // (hence there is no reason for the Java ClassLoader to be aware of the // library, for lazy JNI registration). const std::string partition_name = module_name + "_partition"; library_handle = BundleUtils::DlOpenModuleLibraryPartition(library_name, partition_name); #elif defined(COMPONENT_BUILD) std::string library_path = BundleUtils::ResolveLibraryPath(library_name); library_handle = dlopen(library_path.c_str(), RTLD_LOCAL); #else #error "Unsupported configuration." #endif // defined(COMPONENT_BUILD) CHECK(library_handle != nullptr) << "Could not open feature library " << library_name << ": " << dlerror(); return library_handle; } void RegisterJni(JNIEnv* env, void* library_handle, const std::string& name) { const std::string registration_name = "JNI_OnLoad_" + name; // Find the module's JNI registration method from the feature library. void* symbol = dlsym(library_handle, registration_name.c_str()); CHECK(symbol) << "Could not find JNI registration method '" << registration_name << "' for '" << name << "': " << dlerror(); auto* registration_function = reinterpret_cast(symbol); CHECK(registration_function(env)) << "JNI registration failed: " << name; } void LoadResources(const std::string& pak) { module_installer::ScopedAllowModulePakLoad scoped_allow_module_pak_load; ui::LoadPackFileFromApk("assets/" + pak); } } // namespace static void JNI_Module_LoadNative( JNIEnv* env, const base::android::JavaParamRef& jname, const base::android::JavaParamRef& jlibraries, const base::android::JavaParamRef& jpaks) { std::string name; base::android::ConvertJavaStringToUTF8(env, jname, &name); std::vector libraries; base::android::AppendJavaStringArrayToStringVector(env, jlibraries, &libraries); if (libraries.size() > 0) { void* library_handle = nullptr; for (const auto& library : libraries) { library_handle = LoadLibrary(library, name); } // module libraries are ordered such that the root library will be the last // item in the list. We expect this library to provide the JNI registration // function. RegisterJni(env, library_handle, name); } std::vector paks; base::android::AppendJavaStringArrayToStringVector(env, jpaks, &paks); for (const auto& pak : paks) { LoadResources(pak); } } } // namespace module_installer