aux/android: Using DexClassLoader to load class

This commit is contained in:
Jarvis Huang 2022-08-30 23:41:41 +08:00 committed by Ryan Pavlik
parent dfd37bba78
commit 25e96a508c
7 changed files with 118 additions and 39 deletions

View file

@ -10,6 +10,8 @@ DexClassLoader::Meta::Meta()
: MetaBase(DexClassLoader::getTypeName()),
init(classRef().getMethod("<init>",
"(Ljava/lang/String;Ljava/lang/String;Ljava/"
"lang/String;Ljava/lang/ClassLoader;)V")) {}
"lang/String;Ljava/lang/ClassLoader;)V")),
loadClass(classRef().getMethod("loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;")) {}
} // namespace dalvik::system
} // namespace wrap

View file

@ -12,6 +12,7 @@ class DexClassLoader;
} // namespace dalvik::system
namespace java::lang {
class Class;
class ClassLoader;
} // namespace java::lang
@ -44,11 +45,24 @@ class DexClassLoader : public ObjectWrapperBase {
std::string const &nativeSearchPath,
jni::Object parentClassLoader);
/*!
* Wrapper for the loadClass method
*
* Java prototype:
* `public java.lang.Class<?> loadClass(java.lang.String) throws
* java.lang.ClassNotFoundException;`
*
* JNI signature: (Ljava/lang/String;)Ljava/lang/Class;
*
*/
java::lang::Class loadClass(std::string const &name);
/*!
* Class metadata
*/
struct Meta : public MetaBase {
jni::method_t init;
jni::method_t loadClass;
/*!
* Singleton accessor

View file

@ -19,5 +19,11 @@ DexClassLoader::construct(std::string const &searchPath,
nativeSearchPath, parentClassLoader)};
}
inline java::lang::Class
DexClassLoader::loadClass(const std::string &name) {
assert(!isNull());
return java::lang::Class{object().call<jni::Object>(Meta::data().loadClass, name)};
}
} // namespace dalvik::system
} // namespace wrap

View file

@ -24,8 +24,7 @@
using wrap::android::app::Activity;
using wrap::android::view::SurfaceHolder;
using wrap::org::freedesktop::monado::auxiliary::MonadoView;
using xrt::auxiliary::android::getAppInfo;
using xrt::auxiliary::android::loadClassFromPackage;
using xrt::auxiliary::android::loadClassFromRuntimeApk;
struct android_custom_surface
@ -53,24 +52,14 @@ android_custom_surface::~android_custom_surface()
}
}
constexpr auto FULLY_QUALIFIED_CLASSNAME = "org.freedesktop.monado.auxiliary.MonadoView";
struct android_custom_surface *
android_custom_surface_async_start(struct _JavaVM *vm, void *activity)
{
jni::init(vm);
try {
auto info = getAppInfo(XRT_ANDROID_PACKAGE, (jobject)activity);
if (info.isNull()) {
U_LOG_E("Could not get application info for package '%s'",
"org.freedesktop.monado.openxr_runtime");
return nullptr;
}
auto clazz = loadClassFromPackage(info, (jobject)activity, FULLY_QUALIFIED_CLASSNAME);
auto clazz = loadClassFromRuntimeApk((jobject)activity, MonadoView::getFullyQualifiedTypeName());
if (clazz.isNull()) {
U_LOG_E("Could not load class '%s' from package '%s'", FULLY_QUALIFIED_CLASSNAME,
U_LOG_E("Could not load class '%s' from package '%s'", MonadoView::getFullyQualifiedTypeName(),
XRT_ANDROID_PACKAGE);
return nullptr;
}
@ -90,7 +79,7 @@ android_custom_surface_async_start(struct _JavaVM *vm, void *activity)
}
std::string clazz_name = ret->monadoViewClass.getName();
if (clazz_name != FULLY_QUALIFIED_CLASSNAME) {
if (clazz_name != MonadoView::getFullyQualifiedTypeName()) {
U_LOG_E("Unexpected class name: %s", clazz_name.c_str());
return nullptr;
}
@ -156,17 +145,9 @@ android_custom_surface_get_display_metrics(struct _JavaVM *vm,
{
jni::init(vm);
try {
auto info = getAppInfo(XRT_ANDROID_PACKAGE, (jobject)activity);
if (info.isNull()) {
U_LOG_E("Could not get application info for package '%s'",
"org.freedesktop.monado.openxr_runtime");
return false;
}
auto clazz = loadClassFromPackage(info, (jobject)activity, FULLY_QUALIFIED_CLASSNAME);
auto clazz = loadClassFromRuntimeApk((jobject)activity, MonadoView::getFullyQualifiedTypeName());
if (clazz.isNull()) {
U_LOG_E("Could not load class '%s' from package '%s'", FULLY_QUALIFIED_CLASSNAME,
U_LOG_E("Could not load class '%s' from package '%s'", MonadoView::getFullyQualifiedTypeName(),
XRT_ANDROID_PACKAGE);
return false;
}

View file

@ -12,15 +12,37 @@
#include "util/u_logging.h"
#include "wrap/android.content.h"
#include "wrap/dalvik.system.h"
#include "jni.h"
#include <dlfcn.h>
using wrap::android::content::Context;
using wrap::android::content::pm::ApplicationInfo;
using wrap::android::content::pm::PackageManager;
using wrap::dalvik::system::DexClassLoader;
namespace xrt::auxiliary::android {
/*!
* Hacky way to retrieve runtime source dir.
*/
static std::string
getRuntimeSourceDir()
{
Dl_info info{};
std::string dir;
if (dladdr((void *)&getRuntimeSourceDir, &info)) {
// dli_filename is full path of the library contains the symbol. For example:
// /data/app/~~sha27MVNR46wLF-96zA_LQ==/org.freedesktop.monado.openxr_runtime.out_of_process-cqs8L2Co3WfHGgvDwF12JA==/lib/arm64/libopenxr_monado.so
dir = info.dli_fname;
dir = dir.substr(0, dir.find("/lib/"));
}
return dir;
}
ApplicationInfo
getAppInfo(std::string const &packageName, jobject application_context)
{
@ -50,7 +72,7 @@ getAppInfo(std::string const &packageName, jobject application_context)
}
return packageInfo.getApplicationInfo();
} catch (std::exception const &e) {
U_LOG_E("Could get App Info: %s", e.what());
U_LOG_E("Could not get App Info: %s", e.what());
return {};
}
}
@ -76,10 +98,42 @@ loadClassFromPackage(ApplicationInfo applicationInfo, jobject application_contex
return loadedClass;
} catch (std::exception const &e) {
U_LOG_E("Could load class '%s' forName: %s", clazz_name, e.what());
U_LOG_E("Could not load class '%s' forName: %s", clazz_name, e.what());
return wrap::java::lang::Class();
}
}
wrap::java::lang::Class
loadClassFromApk(jobject application_context, const char *apk_path, const char *clazz_name)
{
Context context = Context{application_context}.getApplicationContext();
DexClassLoader classLoader = DexClassLoader::construct(apk_path, "", context.getClassLoader().object());
try {
auto loadedClass = classLoader.loadClass(std::string(clazz_name));
if (loadedClass.isNull()) {
U_LOG_E("Could not load class for name %s from %s", clazz_name, apk_path);
return wrap::java::lang::Class();
}
return loadedClass;
} catch (std::exception const &e) {
U_LOG_E("Could not load class '%s' from '%s' forName: %s", clazz_name, apk_path, e.what());
return wrap::java::lang::Class();
}
}
wrap::java::lang::Class
loadClassFromRuntimeApk(jobject application_context, const char *clazz_name)
{
if (!application_context) {
U_LOG_E("Could not load class %s, invalid context", clazz_name);
return {};
}
std::string runtimeApkPath = getRuntimeSourceDir() + "/base.apk";
return loadClassFromApk(application_context, runtimeApkPath.c_str(), clazz_name);
}
} // namespace xrt::auxiliary::android

View file

@ -20,12 +20,43 @@ namespace xrt::auxiliary::android {
using wrap::android::content::pm::ApplicationInfo;
/*!
* @note Starting from Android 11, NameNotFoundException exception is thrown if application doesn't
* specify either <queries> or "android.permission.QUERY_ALL_PACKAGES".
* See https://developer.android.com/training/package-visibility for detail.
*/
ApplicationInfo
getAppInfo(std::string const &packageName, jobject application_context);
/*!
* @note Starting from Android 11, NameNotFoundException exception is thrown if application doesn't
* specify either <queries> or "android.permission.QUERY_ALL_PACKAGES".
* See https://developer.android.com/training/package-visibility for detail.
*/
wrap::java::lang::Class
loadClassFromPackage(ApplicationInfo applicationInfo, jobject application_context, const char *clazz_name);
/*!
* Loading class from given apk path.
*
* @param application_context Context.
* @param apk_path Path to apk.
* @param clazz_name Name of class to be loaded.
* @return Class object.
*/
wrap::java::lang::Class
loadClassFromApk(jobject application_context, const char *apk_path, const char *clazz_name);
/*!
* Loading class from runtime apk.
*
* @param application_context Context.
* @param clazz_name Name of class to be loaded.
* @return Class object.
*/
wrap::java::lang::Class
loadClassFromRuntimeApk(jobject application_context, const char *clazz_name);
} // namespace xrt::auxiliary::android
#endif // XRT_OS_ANDROID

View file

@ -20,8 +20,7 @@
using wrap::android::app::Activity;
using wrap::org::freedesktop::monado::ipc::Client;
using xrt::auxiliary::android::getAppInfo;
using xrt::auxiliary::android::loadClassFromPackage;
using xrt::auxiliary::android::loadClassFromRuntimeApk;
struct ipc_client_android
{
@ -52,15 +51,7 @@ ipc_client_android_create(struct _JavaVM *vm, void *activity)
jni::init(vm);
try {
auto info = getAppInfo(XRT_ANDROID_PACKAGE, (jobject)activity);
if (info.isNull()) {
U_LOG_E("Could not get application info for package '%s'",
"org.freedesktop.monado.openxr_runtime");
return nullptr;
}
auto clazz = loadClassFromPackage(info, (jobject)activity, Client::getFullyQualifiedTypeName());
auto clazz = loadClassFromRuntimeApk((jobject)activity, Client::getFullyQualifiedTypeName());
if (clazz.isNull()) {
U_LOG_E("Could not load class '%s' from package '%s'", Client::getFullyQualifiedTypeName(),
XRT_ANDROID_PACKAGE);