diff --git a/app/build.gradle b/app/build.gradle index af3887bd3..071194ede 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -15,6 +15,10 @@ android { versionCode 156235 versionName "3.2.0_6401b_20200822" multiDexEnabled true //important + + ndk { + abiFilters "armeabi-v7a" + } } buildTypes { @@ -28,6 +32,12 @@ android { } } + externalNativeBuild { + ndkBuild { + path file('src/main/jni/Android.mk') + } + } + // Keep the following configuration in order to might make Minecraft 1.12 support. compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/app/src/main/assets/busybox-arm64 b/app/src/main/assets/busybox-arm64 deleted file mode 100644 index e0f4cf397..000000000 Binary files a/app/src/main/assets/busybox-arm64 and /dev/null differ diff --git a/app/src/main/java/net/kdt/pojavlaunch/InstallModActivity.java b/app/src/main/java/net/kdt/pojavlaunch/InstallModActivity.java index 915aed135..6d1497ec5 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/InstallModActivity.java +++ b/app/src/main/java/net/kdt/pojavlaunch/InstallModActivity.java @@ -28,7 +28,7 @@ public class InstallModActivity extends AppCompatActivity Surface surface = new Surface(tex); Field field = surface.getClass().getDeclaredField("mNativeObject"); field.setAccessible(true); - BinaryExecutor.setupBridgeSurfaceAWT((long) field.get(surface)); + JREUtils.setupBridgeSurfaceAWT((long) field.get(surface)); } catch (Throwable th) { Tools.showError(InstallModActivity.this, th, true); } @@ -97,12 +97,12 @@ public class InstallModActivity extends AppCompatActivity System.out.println(Arrays.toString(javaArgList.toArray(new String[0]))); - BinaryExecutor.setJavaEnvironment(this); + JREUtils.setJavaEnvironment(this); - BinaryExecutor.redirectStdio(); - BinaryExecutor.setJavaEnvironment(this); - BinaryExecutor.initJavaRuntime(); - BinaryExecutor.chdir(Tools.MAIN_PATH); + JREUtils.redirectStdio(); + JREUtils.setJavaEnvironment(this); + JREUtils.initJavaRuntime(); + JREUtils.chdir(Tools.MAIN_PATH); VMLauncher.launchJVM(javaArgList.toArray(new String[0])); } catch (Throwable th) { diff --git a/app/src/main/java/net/kdt/pojavlaunch/BinaryExecutor.java b/app/src/main/java/net/kdt/pojavlaunch/JREUtils.java similarity index 94% rename from app/src/main/java/net/kdt/pojavlaunch/BinaryExecutor.java rename to app/src/main/java/net/kdt/pojavlaunch/JREUtils.java index 21bccdf62..33d293b3c 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/BinaryExecutor.java +++ b/app/src/main/java/net/kdt/pojavlaunch/JREUtils.java @@ -4,10 +4,9 @@ import android.system.*; import java.io.*; import android.content.*; -// This class should be named as 'LoadMe' as original -public class BinaryExecutor +public class JREUtils { - private BinaryExecutor() {} + private JREUtils() {} public static void initJavaRuntime() { dlopen(Tools.homeJreDir + "/lib/jli/libjli.so"); @@ -79,6 +78,6 @@ public class BinaryExecutor public static native int executeBinary(String[] args); static { - System.loadLibrary("binexecutor"); + System.loadLibrary("pojavexec"); } } diff --git a/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java index 0d0f3bfed..738481e10 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java +++ b/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java @@ -643,7 +643,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, if (!isCalled) { isCalled = true; - BinaryExecutor.setupBridgeWindow(new Surface(texture)); + JREUtils.setupBridgeWindow(new Surface(texture)); new Thread(new Runnable(){ @@ -1027,7 +1027,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, // can fix java? // setEnvironment("ORIGIN", Tools.homeJreDir + "/lib"); - BinaryExecutor.setJavaEnvironment(this); + JREUtils.setJavaEnvironment(this); if (LAUNCH_TYPE == LTYPE_PROCESS) { mLaunchShell.writeToProcess("cd $HOME"); @@ -1044,7 +1044,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, Os.dup2(FileDescriptor.out, OsConstants.STDOUT_FILENO); */ - BinaryExecutor.redirectStdio(); + JREUtils.redirectStdio(); // DEPRECATED constructor (String) api 29 /* FileObserver fobs = new FileObserver(logFile.getAbsolutePath(), FileObserver.MODIFY){ @@ -1071,8 +1071,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, } }, "RuntimeLogThread").start(); - BinaryExecutor.initJavaRuntime(); - BinaryExecutor.chdir(Tools.MAIN_PATH); + JREUtils.initJavaRuntime(); + JREUtils.chdir(Tools.MAIN_PATH); if (new File(Tools.MAIN_PATH, "strace.txt").exists()) { startStrace(android.os.Process.myTid()); diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 000000000..b065d3f38 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_LDLIBS := -ldl -llog -lEGL -landroid +LOCAL_MODULE := pojavexec + +LOCAL_SRC_FILES := \ + egl_bridge.c \ + jre_launcher.c \ + utils.c + +include $(BUILD_SHARED_LIBRARY) + diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk new file mode 100644 index 000000000..b143b2cfd --- /dev/null +++ b/app/src/main/jni/Application.mk @@ -0,0 +1,5 @@ +# NDK_TOOLCHAIN_VERSION := 4.9 +APP_PLATFORM := android-21 +APP_STL := system +APP_ABI := armeabi-v7a x86 +# arm64-v8a x86_64 diff --git a/app/src/main/jni/egl_bridge.c b/app/src/main/jni/egl_bridge.c new file mode 100644 index 000000000..5c9669353 --- /dev/null +++ b/app/src/main/jni/egl_bridge.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include + +#include +#include + +struct ANativeWindow* windowBridge; + +struct PotatoBridge { + void* eglContext; + void* eglDisplay; + void* eglSurface; +/* + void* eglSurfaceRead; + void* eglSurfaceDraw; +*/ +}; +struct PotatoBridge potatoBridge; + +JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_JREUtils_setupBridgeWindow(JNIEnv* env, jclass clazz, jobject surface) { + windowBridge = ANativeWindow_fromSurface(env, surface); +} + +// Called from JNI_OnLoad of liblwjgl_opengl32 +void pojav_openGLOnLoad() { + printf("ANativeWindow pointer = %p\n", windowBridge); + + potatoBridge.eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (potatoBridge.eglDisplay == EGL_NO_DISPLAY) { + printf("Error: eglGetDefaultDisplay() failed: %p\n", eglGetError()); + return; // -1; + } + + eglMakeCurrent(potatoBridge.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (!eglInitialize(potatoBridge.eglDisplay, NULL, NULL)) { + printf("Error: eglInitialize() failed\n"); + return; // -2; + } + + static const EGLint attribs[] = { + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_DEPTH_SIZE, 1, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_NONE + }; + static const EGLint ctx_attribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLConfig config; + EGLint num_configs; + EGLint vid; + + if (!eglChooseConfig(potatoBridge.eglDisplay, attribs, &config, 1, &num_configs)) { + printf("Error: couldn't get an EGL visual config\n"); + return; // -3; + } + + assert(config); + assert(num_configs > 0); + + if (!eglGetConfigAttrib(potatoBridge.eglDisplay, config, EGL_NATIVE_VISUAL_ID, &vid)) { + printf("Error: eglGetConfigAttrib() failed\n"); + return; // -4; + } + + eglBindAPI(EGL_OPENGL_ES_API); + + potatoBridge.eglContext = eglCreateContext(potatoBridge.eglDisplay, config, EGL_NO_CONTEXT, ctx_attribs); + if (!potatoBridge.eglContext) { + printf("Error: eglCreateContext failed\n"); + return; // -5; + } + + // test eglQueryContext() + { + EGLint val; + eglQueryContext(potatoBridge.eglDisplay, potatoBridge.eglContext, EGL_CONTEXT_CLIENT_VERSION, &val); + assert(val == 2); + } + + potatoBridge.eglSurface = eglCreateWindowSurface(potatoBridge.eglDisplay, config, windowBridge, NULL); + + if (!potatoBridge.eglSurface) { + printf("Error: eglCreateWindowSurface failed: %p\n", eglGetError()); + return; // -6; + } + + // sanity checks + { + EGLint val; + assert(eglGetConfigAttrib(potatoBridge.eglDisplay, config, EGL_SURFACE_TYPE, &val)); + assert(val & EGL_WINDOW_BIT); + } + + printf("EGLContext=%p, EGLDisplay=%p, EGLSurface=%p\n", + potatoBridge.eglContext, + potatoBridge.eglDisplay, + potatoBridge.eglSurface + ); + + if (eglMakeCurrent(potatoBridge.eglDisplay, potatoBridge.eglSurface, potatoBridge.eglSurface, potatoBridge.eglContext) == EGL_FALSE) { + printf("Error: eglMakeCurrent() failed: %p\n", eglGetError()); + } +} + +void pojav_openGLOnUnload() { + eglMakeCurrent(potatoBridge.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(potatoBridge.eglDisplay, potatoBridge.eglSurface); + eglDestroyContext(potatoBridge.eglDisplay, potatoBridge.eglContext); + eglTerminate(potatoBridge.eglDisplay); + eglReleaseThread(); +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglSwapBuffers(JNIEnv *env, jclass clazz) { + return eglSwapBuffers(potatoBridge.eglDisplay, potatoBridge.eglSurface); +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglSwapInterval(JNIEnv *env, jclass clazz, jint interval) { + return eglSwapInterval(potatoBridge.eglDisplay, interval); +} + diff --git a/app/src/main/jni/jre_launcher.c b/app/src/main/jni/jre_launcher.c new file mode 100644 index 000000000..06dc15d85 --- /dev/null +++ b/app/src/main/jni/jre_launcher.c @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +#include +#include +#include +#include +#include +// Boardwalk: missing include +#include + +#include "log.h" + +#include "utils.h" + +// PojavLancher: fixme: are these wrong? +#define FULL_VERSION "1.9.0-internal" +#define DOT_VERSION "1.9" + +typedef jint JNI_CreateJavaVM_func(JavaVM **pvm, void **penv, void *args); + +typedef jint JLI_Launch_func(int argc, char ** argv, /* main argc, argc */ + int jargc, const char** jargv, /* java args */ + int appclassc, const char** appclassv, /* app classpath */ + const char* fullversion, /* full version defined */ + const char* dotversion, /* dot version defined */ + const char* pname, /* program name */ + const char* lname, /* launcher name */ + jboolean javaargs, /* JAVA_ARGS */ + jboolean cpwildcard, /* classpath wildcard*/ + jboolean javaw, /* windows-only javaw */ + jint ergo /* ergonomics class policy */ +); + +JavaVM *runtimeJavaVMPtr = NULL;; +JNIEnv *runtimeJNIEnvPtr = NULL; + +JavaVM *dalvikJavaVMPtr = NULL; +JNIEnv *dalvikJNIEnvPtr = NULL; + +jint JNI_OnLoad(JavaVM* vm, void* reserved) +{ + if (dalvikJavaVMPtr == NULL) { + //Save dalvik global JavaVM pointer + dalvikJavaVMPtr = vm; + LOGD("JNI_OnLoad calling GetEnv()"); + JNIEnv* env = NULL; + (*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4); +/* Boardwalk: not used + LOGD("JNI_OnLoad calling initDalvikProxySelectorData()"); + initDalvikProxySelectorData(env); +*/ + } + LOGD("JNI_OnLoad returning()"); + return JNI_VERSION_1_4; +} + +static void logArgs(int argc, char** argv) { +/* BlockLauncher: disable logging + int i; + + for (i = 0; i < argc; i++) { + LOGD("arg[%d]: %s", i, argv[i]); + } +*/ +} + +JNIEXPORT jint JNICALL Java_com_oracle_dalvik_VMLauncher_createLaunchMainJVM(JNIEnv *env, jclass clazz, jobjectArray vmArgArr, jstring mainClassStr, jobjectArray mainArgArr) { + void *libjvm = dlopen("libjvm.so", RTLD_NOW + RTLD_GLOBAL); + if (libjvm == NULL) { + LOGE("dlopen failed to open libjvm.so (dlerror %s).", dlerror()); + return -1; + } + + JNI_CreateJavaVM_func *jl_JNI_CreateJavaVM = (JNI_CreateJavaVM_func *) dlsym(libjvm, "JNI_CreateJavaVM"); + if (jl_JNI_CreateJavaVM == NULL) { + LOGE("dlsym failed to get JNI_CreateJavaVM (dlerror %s).", dlerror()); + return -1; + } + + int vm_argc = (*env)->GetArrayLength(env, vmArgArr); + char **vm_argv = convert_to_char_array(env, vmArgArr); + + int main_argc = (*env)->GetArrayLength(env, mainArgArr); + char **main_argv = convert_to_char_array(env, mainArgArr); + + JavaVMInitArgs vm_args; + JavaVMOption options[vm_argc]; + for (int i = 0; i < vm_argc; i++) { + options[i].optionString = vm_argv[i]; + } + vm_args.version = JNI_VERSION_1_6; + vm_args.options = options; + vm_args.nOptions = vm_argc; + vm_args.ignoreUnrecognized = JNI_FALSE; + + jint res = (jint) jl_JNI_CreateJavaVM(&runtimeJavaVMPtr, (void**)&runtimeJNIEnvPtr, &vm_args); + // delete options; + + char *main_class_c = (*env)->GetStringUTFChars(env, mainClassStr, 0); + + jclass mainClass = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, main_class_c); + jmethodID mainMethod = (*runtimeJNIEnvPtr)->GetStaticMethodID(runtimeJNIEnvPtr, mainClass, "main", "([Ljava/lang/String;)V"); + + // Need recreate jobjectArray to make JNIEnv is 'runtimeJNIEnvPtr'. + jobjectArray runtime_main_argv = convert_from_char_array(runtimeJNIEnvPtr, main_argv, main_argc); + (*runtimeJNIEnvPtr)->CallStaticVoidMethod(runtimeJNIEnvPtr, mainClass, mainMethod, runtime_main_argv); + + (*env)->ReleaseStringUTFChars(env, mainClassStr, main_class_c); + free_char_array(env, mainArgArr, main_argv); + free_char_array(env, vmArgArr, vm_argv); + + return res; +} + +static jint launchJVM(int argc, char** argv) { + logArgs(argc, argv); + + void* libjli = dlopen("libjli.so", RTLD_LAZY | RTLD_GLOBAL); + // Boardwalk: silence + // LOGD("JLI lib = %x", (int)libjli); + if (NULL == libjli) { + LOGE("JLI lib = NULL: %s", dlerror()); + return 0; + } + LOGD("Found JLI lib"); + + JLI_Launch_func *pJLI_Launch = + (JLI_Launch_func *)dlsym(libjli, "JLI_Launch"); + // Boardwalk: silence + // LOGD("JLI_Launch = 0x%x", *(int*)&pJLI_Launch); + + if (NULL == pJLI_Launch) { + LOGE("JLI_Launch = NULL"); + return 0; + } + + LOGD("Calling JLI_Launch"); + + return pJLI_Launch(argc, argv, + 0, NULL, 0, NULL, FULL_VERSION, + DOT_VERSION, *argv, *argv, /* "java", "openjdk", */ + JNI_FALSE, JNI_TRUE, JNI_FALSE, 0); +} + +/* + * Class: com_oracle_embedded_launcher_VMLauncher + * Method: launchJVM + * Signature: ([Ljava/lang/String;)I + */ +JNIEXPORT jint JNICALL Java_com_oracle_dalvik_VMLauncher_launchJVM(JNIEnv *env, jclass clazz, jobjectArray argsArray) { + jint res = 0; + // int i; + + // Save dalvik JNIEnv pointer for JVM launch thread + dalvikJNIEnvPtr = env; + + if (argsArray == NULL) { + LOGE("Args array null, returning"); + //handle error + return 0; + } + + int argc = (*env)->GetArrayLength(env, argsArray); + char **argv = convert_to_char_array(env, argsArray); + + LOGD("Done processing args"); + + res = launchJVM(argc, argv); + + LOGD("Freeing args"); + free_char_array(env, argsArray, argv); + + LOGD("Free done"); + + return res; +} + + diff --git a/app/src/main/jni/log.h b/app/src/main/jni/log.h new file mode 100644 index 000000000..f46b02cd0 --- /dev/null +++ b/app/src/main/jni/log.h @@ -0,0 +1,26 @@ +#ifdef __ANDROID__ +#include + +#define TAG "LaunchJVM" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __ANDROID__ + #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) + #define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__) + #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) + #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) +#else + #define LOGE(...) printf(__VA_ARGS__) + #define LOGW(...) printf(__VA_ARGS__) + #define LOGI(...) printf(__VA_ARGS__) + #define LOGD(...) printf(__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif + diff --git a/app/src/main/jni/utils.c b/app/src/main/jni/utils.c new file mode 100644 index 000000000..a973d7350 --- /dev/null +++ b/app/src/main/jni/utils.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include + +#include "log.h" + +#include "utils.h" + +typedef int (*Main_Function_t)(int, char**); +typedef void (*android_update_LD_LIBRARY_PATH_t)(char*); + +long shared_awt_surface; + +char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray) { + int num_rows = (*env)->GetArrayLength(env, jstringArray); + char **cArray = (char **) malloc(num_rows * sizeof(char*)); + jstring row; + + for (int i = 0; i < num_rows; i++) { + row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i); + cArray[i] = (char*)(*env)->GetStringUTFChars(env, row, 0); + } + + return cArray; +} + +jobjectArray convert_from_char_array(JNIEnv *env, char **charArray, int num_rows) { + jobjectArray resultArr = (*env)->NewObjectArray(env, num_rows, (*env)->FindClass(env, "java/lang/String"), NULL); + jstring row; + + for (int i = 0; i < num_rows; i++) { + row = (jstring) (*env)->NewStringUTF(env, charArray[i]); + (*env)->SetObjectArrayElement(env, resultArr, i, row); + } + + return resultArr; +} + +void free_char_array(JNIEnv *env, jobjectArray jstringArray, const char **charArray) { + int num_rows = (*env)->GetArrayLength(env, jstringArray); + jstring row; + + for (int i = 0; i < num_rows; i++) { + row = (jstring) (*env)->GetObjectArrayElement(env, jstringArray, i); + (*env)->ReleaseStringUTFChars(env, row, charArray[i]); + } +} + +JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_JREUtils_setupBridgeSurfaceAWT(JNIEnv *env, jclass clazz, jlong surface) { + shared_awt_surface = surface; +} + +JNIEXPORT jlong JNICALL Java_android_view_Surface_nativeGetBridgeSurfaceAWT(JNIEnv *env, jclass clazz) { + return (jlong) shared_awt_surface; +} + +JNIEXPORT jint JNICALL Java_android_os_OpenJDKNativeRegister_nativeRegisterNatives(JNIEnv *env, jclass clazz, jstring registerSymbol) { + const char *register_symbol_c = (*env)->GetStringUTFChars(env, registerSymbol, 0); + void *symbol = dlsym(RTLD_DEFAULT, register_symbol_c); + if (symbol == NULL) { + printf("dlsym %s failed: %s\n", register_symbol_c, dlerror()); + return -1; + } + + int (*registerNativesForClass)(JNIEnv*) = symbol; + int result = registerNativesForClass(env); + (*env)->ReleaseStringUTFChars(env, registerSymbol, register_symbol_c); + + return (jint) result; +} + +JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_JREUtils_setLdLibraryPath(JNIEnv *env, jclass clazz, jstring ldLibraryPath) { + // jclass exception_cls = (*env)->FindClass(env, "java/lang/UnsatisfiedLinkError"); + + android_update_LD_LIBRARY_PATH_t android_update_LD_LIBRARY_PATH; + + void *libdl_handle = dlopen("libdl.so", RTLD_LAZY); + void *updateLdLibPath = dlsym(libdl_handle, "android_update_LD_LIBRARY_PATH"); + if (updateLdLibPath == NULL) { + updateLdLibPath = dlsym(libdl_handle, "__loader_android_update_LD_LIBRARY_PATH"); + if (updateLdLibPath == NULL) { + char *dl_error_c = dlerror(); + LOGE("Error getting symbol android_update_LD_LIBRARY_PATH: %s", dl_error_c); + // (*env)->ThrowNew(env, exception_cls, dl_error_c); + } + } + + android_update_LD_LIBRARY_PATH = (android_update_LD_LIBRARY_PATH_t) updateLdLibPath; + const char* ldLibPathUtf = (*env)->GetStringUTFChars(env, ldLibraryPath, 0); + android_update_LD_LIBRARY_PATH(ldLibPathUtf); + (*env)->ReleaseStringUTFChars(env, ldLibraryPath, ldLibPathUtf); +} + +JNIEXPORT jboolean JNICALL Java_net_kdt_pojavlaunch_JREUtils_dlopen(JNIEnv *env, jclass clazz, jstring name) { + const char *nameUtf = (*env)->GetStringUTFChars(env, name, 0); + void* handle = dlopen(nameUtf, RTLD_GLOBAL | RTLD_LAZY); + if (!handle) { + LOGE("dlopen %s failed: %s", nameUtf, dlerror()); + } else { + LOGD("dlopen %s success", nameUtf); + } + (*env)->ReleaseStringUTFChars(env, name, nameUtf); + return handle != NULL; +} + +JNIEXPORT jint JNICALL Java_net_kdt_pojavlaunch_JREUtils_chdir(JNIEnv *env, jclass clazz, jstring nameStr) { + const char *name = (*env)->GetStringUTFChars(env, nameStr, NULL); + int retval = chdir(name); + (*env)->ReleaseStringUTFChars(env, nameStr, name); + return retval; +} + +JNIEXPORT jint JNICALL Java_net_kdt_pojavlaunch_JREUtils_executeBinary(JNIEnv *env, jclass clazz, jobjectArray cmdArgs) { + jclass exception_cls = (*env)->FindClass(env, "java/lang/UnsatisfiedLinkError"); + jstring execFile = (*env)->GetObjectArrayElement(env, cmdArgs, 0); + + char *exec_file_c = (char*) (*env)->GetStringUTFChars(env, execFile, 0); + void *exec_binary_handle = dlopen(exec_file_c, RTLD_LAZY); + + // (*env)->ReleaseStringUTFChars(env, ldLibraryPath, ld_library_path_c); + (*env)->ReleaseStringUTFChars(env, execFile, exec_file_c); + + char *exec_error_c = dlerror(); + if (exec_error_c != NULL) { + LOGE("Error: %s", exec_error_c); + (*env)->ThrowNew(env, exception_cls, exec_error_c); + return -1; + } + + Main_Function_t Main_Function; + Main_Function = (Main_Function_t) dlsym(exec_binary_handle, "main"); + + exec_error_c = dlerror(); + if (exec_error_c != NULL) { + LOGE("Error: %s", exec_error_c); + (*env)->ThrowNew(env, exception_cls, exec_error_c); + return -1; + } + + int cmd_argv = (*env)->GetArrayLength(env, cmdArgs); + char **cmd_args_c = convert_to_char_array(env, cmdArgs); + int result = Main_Function(cmd_argv, cmd_args_c); + free_char_array(env, cmdArgs, cmd_args_c); + return result; +} + +// METHOD 2 +/* +JNIEXPORT jint JNICALL Java_net_kdt_pojavlaunch_JREUtils_executeForkedBinary(JNIEnv *env, jclass clazz, jobjectArray cmdArgs) { + int x, status; + x = fork(); + if (x > 0) { + wait(&status); + } else { + execvpe(); + } + return status; +} +*/ + diff --git a/app/src/main/jni/utils.h b/app/src/main/jni/utils.h new file mode 100644 index 000000000..9eb52db49 --- /dev/null +++ b/app/src/main/jni/utils.h @@ -0,0 +1,9 @@ +#ifndef _BINARY_UTILS_H_ +#define _BINARY_UTILS_H_ + +char** convert_to_char_array(JNIEnv *env, jobjectArray jstringArray); +jobjectArray convert_from_char_array(JNIEnv *env, char **charArray, int num_rows); +void free_char_array(JNIEnv *env, jobjectArray jstringArray, const char **charArray); + +#endif // _BINARY_UTILS_H_ + diff --git a/app/src/main/jniLibs/armeabi-v7a/libbinexecutor.so b/app/src/main/jniLibs/armeabi-v7a/libbinexecutor.so deleted file mode 100644 index 6f3876e23..000000000 Binary files a/app/src/main/jniLibs/armeabi-v7a/libbinexecutor.so and /dev/null differ