The open source OpenXR runtime
at main 190 lines 6.2 kB view raw
1// Copyright 2020, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Implementations for loading Java code from a package. 6 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 7 * @ingroup aux_android 8 */ 9 10#include "android_load_class.hpp" 11 12#include "util/u_logging.h" 13 14#include "wrap/android.content.h" 15#include "wrap/android.content.pm.h" 16#include "wrap/dalvik.system.h" 17 18#include "jni.h" 19 20#include <dlfcn.h> 21 22using wrap::android::content::Context; 23using wrap::android::content::pm::ApplicationInfo; 24using wrap::android::content::pm::PackageManager; 25using wrap::dalvik::system::DexClassLoader; 26 27namespace xrt::auxiliary::android { 28 29static constexpr char kIntentAction[] = "org.khronos.openxr.OpenXRRuntimeService"; 30 31/*! 32 * Hacky way to retrieve runtime source dir. 33 */ 34static std::string 35getRuntimeSourceDir() 36{ 37 Dl_info info{}; 38 std::string dir; 39 if (dladdr((void *)&getRuntimeSourceDir, &info)) { 40 // dli_filename is full path of the library contains the symbol. For example: 41 // /data/app/~~sha27MVNR46wLF-96zA_LQ==/org.freedesktop.monado.openxr_runtime.out_of_process-cqs8L2Co3WfHGgvDwF12JA==/lib/arm64/libopenxr_monado.so 42 dir = info.dli_fname; 43 44 // Trim trailing lib path to .so (e.g. /lib/arm64/libopenxr_monado.so) 45 dir = dir.substr(0, dir.find("/lib/")); 46 47 // In case the SO is not extracted, trim off the base APK name and ! 48 // This finishes handling cases like: 49 // /data/app/~~sha27MVNR46wLF-96zA_LQ==/org.freedesktop.monado.openxr_runtime.out_of_process-cqs8L2Co3WfHGgvDwF12JA==/base.apk!/lib/arm64/libopenxr_monado.so 50 dir = dir.substr(0, dir.find("/base.apk!")); 51 } 52 53 return dir; 54} 55 56ApplicationInfo 57getAppInfo(std::string const &packageName, jobject application_context) 58{ 59 try { 60 auto context = Context{application_context}; 61 if (context.isNull()) { 62 U_LOG_E("getAppInfo: application_context was null"); 63 return {}; 64 } 65 auto packageManager = PackageManager{context.getPackageManager()}; 66 if (packageManager.isNull()) { 67 U_LOG_E( 68 "getAppInfo: " 69 "application_context.getPackageManager() returned null"); 70 return {}; 71 } 72 auto intent = wrap::android::content::Intent::construct(kIntentAction); 73 auto resolutions = packageManager.queryIntentServices( 74 intent, PackageManager::GET_META_DATA | PackageManager::GET_SHARED_LIBRARY_FILES); 75 if (resolutions.isNull() || resolutions.size() == 0) { 76 U_LOG_E( 77 "getAppInfo: " 78 "application_context.getPackageManager().queryIntentServices() returned null or empty"); 79 return {}; 80 } 81 const auto n = resolutions.size(); 82 ApplicationInfo appInfo; 83 for (int32_t i = 0; i < n; ++i) { 84 wrap::android::content::pm::ResolveInfo resolution{resolutions.get(i)}; 85 auto service = resolution.getServiceInfo(); 86 if (service.isNull()) { 87 continue; 88 } 89 U_LOG_I("getAppInfo: Considering package %s", service.getPackageName().c_str()); 90 if (service.getPackageName() == packageName) { 91 appInfo = service.getApplicationInfo(); 92 break; 93 } 94 } 95 96 if (appInfo.isNull()) { 97 U_LOG_E( 98 "getAppInfo: " 99 "could not find a package advertising the intent %s named %s", 100 kIntentAction, packageName.c_str()); 101 return {}; 102 } 103 return appInfo; 104 } catch (std::exception const &e) { 105 U_LOG_E("Could not get App Info: %s", e.what()); 106 return {}; 107 } 108} 109 110wrap::java::lang::Class 111loadClassFromPackage(ApplicationInfo applicationInfo, jobject application_context, const char *clazz_name) 112{ 113 auto context = Context{application_context}.getApplicationContext(); 114 auto pkgContext = context.createPackageContext( 115 applicationInfo.getPackageName(), Context::CONTEXT_IGNORE_SECURITY | Context::CONTEXT_INCLUDE_CODE); 116 117 // Not using ClassLoader.loadClass because it expects a /-delimited 118 // class name, while we have a .-delimited class name. 119 // This does work 120 wrap::java::lang::ClassLoader pkgClassLoader = pkgContext.getClassLoader(); 121 122 try { 123 auto loadedClass = pkgClassLoader.loadClass(std::string(clazz_name)); 124 if (loadedClass.isNull()) { 125 U_LOG_E("Could not load class for name %s", clazz_name); 126 return wrap::java::lang::Class(); 127 } 128 129 return loadedClass; 130 } catch (std::exception const &e) { 131 U_LOG_E("Could not load class '%s' forName: %s", clazz_name, e.what()); 132 return wrap::java::lang::Class(); 133 } 134} 135 136wrap::java::lang::Class 137loadClassFromApk(jobject application_context, const char *apk_path, const char *clazz_name) 138{ 139 Context context = Context{application_context}.getApplicationContext(); 140 DexClassLoader classLoader = DexClassLoader::construct(apk_path, "", context.getClassLoader().object()); 141 try { 142 auto loadedClass = classLoader.loadClass(std::string(clazz_name)); 143 if (loadedClass.isNull()) { 144 U_LOG_E("Could not load class for name %s from %s", clazz_name, apk_path); 145 return wrap::java::lang::Class(); 146 } 147 148 return loadedClass; 149 } catch (std::exception const &e) { 150 U_LOG_E("Could not load class '%s' from '%s' forName: %s", clazz_name, apk_path, e.what()); 151 return wrap::java::lang::Class(); 152 } 153} 154 155wrap::java::lang::Class 156loadClassFromRuntimeApk(jobject application_context, const char *clazz_name) 157{ 158 if (!application_context) { 159 U_LOG_E("Could not load class %s, invalid context", clazz_name); 160 return {}; 161 } 162 163 std::string runtimeApkPath = getRuntimeSourceDir() + "/base.apk"; 164 return loadClassFromApk(application_context, runtimeApkPath.c_str(), clazz_name); 165} 166 167} // namespace xrt::auxiliary::android 168 169 170void * 171android_load_class_from_package(struct _JavaVM *vm, 172 const char *pkgname, 173 void *application_context, 174 const char *classname) 175{ 176 using namespace xrt::auxiliary::android; 177 jni::init(vm); 178 Context context((jobject)application_context); 179 auto info = getAppInfo(pkgname, (jobject)application_context); 180 if (info.isNull()) { 181 U_LOG_E("Could not get application info for package '%s'", pkgname); 182 return nullptr; 183 } 184 auto clazz = loadClassFromPackage(info, (jobject)application_context, classname); 185 if (clazz.isNull()) { 186 U_LOG_E("Could not load class '%s' from package '%s'", classname, pkgname); 187 return nullptr; 188 } 189 return clazz.object().getHandle(); 190}