diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 000000000..fa0cfecc9 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,27 @@ +name: Android CI + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Gradle + run: | + chmod +x gradlew + ./gradlew clean assembleDebug + ./gradlew test +# should test??? + + - name: Upload APK + uses: actions/upload-artifact@v2 + with: + name: app-debug + path: app/build/outputs/apk/debug/app-debug.apk diff --git a/.travis.yml b/.travis.yml.disabled similarity index 95% rename from .travis.yml rename to .travis.yml.disabled index 08286c419..2ebd745a9 100644 --- a/.travis.yml +++ b/.travis.yml.disabled @@ -50,9 +50,9 @@ deploy: api-key: $GITHUB_API_KEY file: $TRAVIS_BUILD_DIR/app/build/outputs/apk/debug/app-debug.apk skip_cleanup: true - name: PojavLauncher_DevBuild-$TRAVIS_COMMIT + name: PojavLauncher-v3_DevBuild-$TRAVIS_COMMIT body: Automatic build of PojavLauncher from the latest source commit ($TRAVIS_COMMIT) built by Travis CI on $(date +'%F %T %Z'). prerelease: true overwrite: true target_commitish: $TRAVIS_COMMIT - on: [push] +# on: [push] diff --git a/README.md b/README.md index 420124d20..535d3dbed 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![Android CI](https://github.com/PojavLauncherTeam/PojavLauncher/workflows/Android%20CI/badge.svg) # PojavLauncher A Minecraft: Java Edition launcher for Android based from Boardwalk. Support up-to Minecraft 1.11.2 @@ -6,7 +7,8 @@ A Minecraft: Java Edition launcher for Android based from Boardwalk. Support up- ## Current status - [x] **Removed** ~~BinaryExecutor: execute `java` binary, no `JNIInvocation`.~~ -- [x] JVDroid OpenJDK 11 (32 and 64-bit ARM and x86). Partial, no error `can't lock mutex`, but now exit with none output. +- [x] **Temporary removed** ~~JVDroid OpenJDK 11 (32 and 64-bit ARM and x86). Partial, no error `can't lock mutex`, but now exit with none output.~~ +- [x] OpenJDK 9 Mobile port - [ ] AWT/Swing for mod installer. Will use `Caciocavallo` project. - [ ] OpenGL in OpenJDK environment. Use Boardwalk 2 method or other. - [ ] OpenAL 64-bit version diff --git a/app/build.gradle b/app/build.gradle index af3887bd3..fbdb4076d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,11 +3,22 @@ apply plugin: 'com.android.application' android { compileSdkVersion 26 dexOptions { - javaMaxHeapSize "4g" + javaMaxHeapSize "4g" } - lintOptions { - abortOnError false - } + + lintOptions { + abortOnError false + } + + signingConfigs { + customDebug { + storeFile file("debug.keystore") + storePassword "android" + keyAlias "androiddebugkey" + keyPassword "android" + } + } + defaultConfig { applicationId "net.kdt.pojavlaunch" minSdkVersion 21 @@ -15,11 +26,21 @@ android { versionCode 156235 versionName "3.2.0_6401b_20200822" multiDexEnabled true //important + + ndk { + abiFilters "armeabi-v7a" + } } buildTypes { + debug { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.customDebug + } + release { - // Don't set to true or java.awt will be a.a or something similar. + // Don't set to true or java.awt will be a.a or something similar. minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' // defaultConfig already set @@ -28,6 +49,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/debug.keystore b/app/debug.keystore new file mode 100644 index 000000000..12fd3e638 Binary files /dev/null and b/app/debug.keystore differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index afda11161..77b8b5412 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -41,6 +41,11 @@ android:name=".MCLauncherActivity" android:configChanges="keyboardHidden|orientation|screenSize"/> + + javaArgList = new ArrayList(); + + javaArgList.add(Tools.homeJreDir + "/bin/java"); + + // javaArgList.add("-Xms512m"); + javaArgList.add("-Xmx512m"); + + javaArgList.add("-Djava.home=" + Tools.homeJreDir); + javaArgList.add("-Djava.io.tmpdir=" + getCacheDir().getAbsolutePath()); + javaArgList.add("-Dos.name=Linux"); + + File cacioAwtLibPath = new File(Tools.MAIN_PATH, "cacioawtlib"); + if (cacioAwtLibPath.exists()) { + StringBuilder libStr = new StringBuilder(); + for (File file: cacioAwtLibPath.listFiles()) { + if (file.getName().endsWith(".jar")) { + libStr.append(":" + file.getAbsolutePath()); + } + } + javaArgList.add("-Xbootclasspath/a" + libStr.toString()); + } + + File cacioArgOverrideFile = new File(cacioAwtLibPath, "overrideargs.txt"); + if (cacioArgOverrideFile.exists()) { + javaArgList.addAll(Arrays.asList(Tools.read(cacioArgOverrideFile.getAbsolutePath()).split(" "))); + } + + javaArgList.add("-jar"); + javaArgList.add(modFile.getAbsolutePath()); + + System.out.println(Arrays.toString(javaArgList.toArray(new String[0]))); + + JREUtils.setJavaEnvironment(this); + + JREUtils.redirectStdio(); + JREUtils.setJavaEnvironment(this); + JREUtils.initJavaRuntime(); + JREUtils.chdir(Tools.MAIN_PATH); + + VMLauncher.launchJVM(javaArgList.toArray(new String[0])); + } catch (Throwable th) { + Tools.showError(this, th, true); + } + } +} diff --git a/app/src/main/java/net/kdt/pojavlaunch/JREUtils.java b/app/src/main/java/net/kdt/pojavlaunch/JREUtils.java new file mode 100644 index 000000000..33d293b3c --- /dev/null +++ b/app/src/main/java/net/kdt/pojavlaunch/JREUtils.java @@ -0,0 +1,83 @@ +package net.kdt.pojavlaunch; + +import android.system.*; +import java.io.*; +import android.content.*; + +public class JREUtils +{ + private JREUtils() {} + + public static void initJavaRuntime() { + dlopen(Tools.homeJreDir + "/lib/jli/libjli.so"); + dlopen(Tools.homeJreDir + "/lib/server/libjvm.so"); + dlopen(Tools.homeJreDir + "/lib/libverify.so"); + dlopen(Tools.homeJreDir + "/lib/libjava.so"); + dlopen(Tools.homeJreDir + "/lib/libnet.so"); + dlopen(Tools.homeJreDir + "/lib/libnio.so"); + dlopen(Tools.homeJreDir + "/lib/libawt.so"); + dlopen(Tools.homeJreDir + "/lib/libawt_headless.so"); + } + + public static void redirectStdio() throws ErrnoException { + File logFile = new File(Tools.MAIN_PATH, "latestlog.txt"); + + FileDescriptor fd = Os.open(logFile.getAbsolutePath(), OsConstants.O_WRONLY | OsConstants.O_CREAT | OsConstants.O_TRUNC, 0666); + Os.dup2(fd, OsConstants.STDERR_FILENO); + Os.dup2(fd, OsConstants.STDOUT_FILENO); + + // return fd; + } + + public static void setJavaEnvironment(Context ctx) throws IOException, ErrnoException { + String libName = System.getProperty("os.arch").contains("64") ? "lib64" : "lib"; + String ldLibraryPath = ( + // To make libjli.so ignore re-execute + Tools.homeJreDir + "/lib/server:" + + + "/system/" + libName + ":" + + "/vendor/" + libName + ":" + + "/vendor/" + libName + "/hw:" + + + ctx.getApplicationInfo().nativeLibraryDir + ":" + + + Tools.homeJreDir + "/lib/jli:" + + Tools.homeJreDir + "/lib" + ); + + setEnvironment("JAVA_HOME", Tools.homeJreDir); + setEnvironment("HOME", Tools.MAIN_PATH); + setEnvironment("TMPDIR", ctx.getCacheDir().getAbsolutePath()); + // setEnvironment("LIBGL_MIPMAP", "3"); + setEnvironment("MESA_GLSL_CACHE_DIR", ctx.getCacheDir().getAbsolutePath()); + setEnvironment("LD_LIBRARY_PATH", ldLibraryPath); + setEnvironment("PATH", Tools.homeJreDir + "/bin:" + Os.getenv("PATH")); + + setLdLibraryPath(ldLibraryPath); + + // return ldLibraryPath; + } + + private static void setEnvironment(String name, String value) throws ErrnoException, IOException { + if (MainActivity.LAUNCH_TYPE == MainActivity.LTYPE_PROCESS) { + MainActivity.mLaunchShell.writeToProcess("export " + name + "=" + value); + } else { + Os.setenv(name, value, true); + } + } + + public static native int chdir(String path); + public static native boolean dlopen(String libPath); + public static native void setLdLibraryPath(String ldLibraryPath); + public static native void setupBridgeWindow(Object surface); + + public static native void setupBridgeSurfaceAWT(long surface); + + // BEFORE Load and execute PIE binary using dlopen and dlsym("main") + // AFTER: Execute a binary in forked process + public static native int executeBinary(String[] args); + + static { + System.loadLibrary("pojavexec"); + } +} diff --git a/app/src/main/java/net/kdt/pojavlaunch/MCLauncherActivity.java b/app/src/main/java/net/kdt/pojavlaunch/MCLauncherActivity.java index 2f3008dfe..cb38cdb22 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/MCLauncherActivity.java +++ b/app/src/main/java/net/kdt/pojavlaunch/MCLauncherActivity.java @@ -755,22 +755,19 @@ public class MCLauncherActivity extends AppCompatActivity @Override public void onClick(DialogInterface p1, int p2) { - switch(p2){ - case 0:{ // Mods manager - modManager(); - } break; - case 1:{ // OptiFine installer - installOptiFine(); - } break; - case 2:{ // Custom controls - if (Tools.enableDevFeatures) { - startActivity(new Intent(MCLauncherActivity.this, CustomControlsActivity.class)); - } - } break; - case 3:{ // Settings - startActivity(new Intent(MCLauncherActivity.this, LauncherPreferenceActivity.class)); - } break; - case 4:{ // About + switch (p2) { + case 0: // Mod installer + installMod(); + break; + case 1: // Custom controls + if (Tools.enableDevFeatures) { + startActivity(new Intent(MCLauncherActivity.this, CustomControlsActivity.class)); + } + break; + case 2: // Settings + startActivity(new Intent(MCLauncherActivity.this, LauncherPreferenceActivity.class)); + break; + case 3:{ // About final AlertDialog.Builder aboutB = new AlertDialog.Builder(MCLauncherActivity.this); aboutB.setTitle(R.string.mcl_option_about); try @@ -792,70 +789,11 @@ public class MCLauncherActivity extends AppCompatActivity builder.show(); } - public void modManager() - { - /* - File file1 = new File(Tools.mpModEnable); - File file2 = new File(Tools.mpModDisable); - File file3 = new File(Tools.mpModAddNewMo); - file1.mkdirs(); - file2.mkdir(); - try - { - file3.createNewFile(); - } - catch (IOException e){} - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle("Mods manager (Forge)"); - builder.setPositiveButton(android.R.string.cancel, null); - - AlertDialog dialog = builder.create(); - - MFileListView flv = new MFileListView(this, dialog); - flv.listFileAt(Tools.datapath + "/ModsManager"); - flv.setFileSelectedListener(new MFileSelectedListener(){ - - @Override - public void onFileLongClick(File file, String path, String nane, String extension) - { - // TODO: Implement this method - } - @Override - public void onFileSelected(File file, String path, String nane, String extension) - { - // TODO: Implement this method - if(extension.equals(".jar")) { - - } else { - openSelectMod(); - } - } - }); - dialog.setView(flv); - dialog.show(); - */ - - Tools.dialogOnUiThread(this, "Mods manager", "This feature is not yet supported!"); - } - - public void openSelectMod() - { + private void installMod() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.alerttitle_installmod); builder.setPositiveButton(android.R.string.cancel, null); - AlertDialog dialog = builder.create(); - FileListView flv = new FileListView(this); - - dialog.setView(flv); - dialog.show(); - } - - private void installOptiFine() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.alerttitle_installoptifine); - builder.setPositiveButton(android.R.string.cancel, null); - final AlertDialog dialog = builder.create(); FileListView flv = new FileListView(this); flv.setFileSelectedListener(new FileSelectedListener(){ @@ -863,7 +801,9 @@ public class MCLauncherActivity extends AppCompatActivity @Override public void onFileSelected(File file, String path, String name) { if (name.endsWith(".jar")) { - doInstallOptiFine(file); + Intent intent = new Intent(MCLauncherActivity.this, InstallModActivity.class); + intent.putExtra("modFile", file); + startActivity(intent); dialog.dismiss(); } } @@ -872,143 +812,6 @@ public class MCLauncherActivity extends AppCompatActivity dialog.show(); } - private void doInstallOptiFine(File optifineFile) { - new OptiFineInstaller().execute(optifineFile); - } - - private class OptiFineInstaller extends AsyncTask - { - private ProgressDialog dialog; - @Override - protected void onPreExecute() { - super.onPreExecute(); - dialog = new ProgressDialog(MCLauncherActivity.this); - dialog.setTitle("Installing OptiFine"); - dialog.setMessage("Preparing"); - dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - dialog.setMax(5); - dialog.setCancelable(false); - dialog.show(); - } - - @Override - protected Throwable doInBackground(File[] file) { - final StringBuilder currentLog = new StringBuilder(); - LoggerJava.LoggerOutputStream logOut = new LoggerJava.LoggerOutputStream(System.out, new LoggerJava.OnStringPrintListener(){ - @Override - public void onCharPrint(char c) - { - currentLog.append(c); - } - }); - LoggerJava.LoggerOutputStream logErr = new LoggerJava.LoggerOutputStream(System.err, new LoggerJava.OnStringPrintListener(){ - @Override - public void onCharPrint(char c) - { - currentLog.append(c); - } - }); - Throwable throwable = null; - File convertedFile = null; - try { - publishProgress("Preparing", "5"); - - String origMd5 = OptiFinePatcher.calculateMD5(file[0]); - convertedFile = new File(Tools.optifineDir, origMd5 + ".jar"); - if (!convertedFile.exists()) { - publishProgress("Patching OptiFine Installer", null, "1", "true"); - - Tools.extractAssetFolder(MCLauncherActivity.this, "optifine_patch", Tools.optifineDir, true); - - new File(Tools.optifineDir + "/optifine_patch/AndroidOptiFineUtilities.class.patch").delete(); - - String[] output = Tools.patchOptifineInstaller(MCLauncherActivity.this, file[0]); - File patchedFile = new File(output[1]); - - publishProgress("Converting OptiFine", null, null, "false"); - - System.setOut(new PrintStream(logOut)); - System.setErr(new PrintStream(logErr)); - - if (!convertedFile.exists()) { - RuntimeException dxError = new RuntimeException(getResources().getString(R.string.error_convert_lib, "OptiFine") + "\n" + currentLog.toString()); - dxError.setStackTrace(new StackTraceElement[0]); - throw dxError; - } - - patchedFile.delete(); - } - - publishProgress("Launching OptiFine installer", null, null, "true"); - - File optDir = getDir("dalvik-cache", 0); - optDir.mkdir(); - - DexClassLoader loader = new DexClassLoader(convertedFile.getAbsolutePath(), optDir.getAbsolutePath(), getApplicationInfo().nativeLibraryDir, getClass().getClassLoader()); - // AndroidOptiFineUtilities.originalOptifineJar = convertedFile.getAbsolutePath(); - - Class installerClass = loader.loadClass("optifine.AndroidInstaller"); - Method installerMethod = installerClass.getDeclaredMethod("doInstall", File.class); - installerMethod.invoke(null, new File(Tools.MAIN_PATH)); - - publishProgress("(4/5) Patching OptiFine Tweaker", null, null); - File optifineLibFile = new File("unimpl"); - if (!optifineLibFile.exists()) { - throw new FileNotFoundException(optifineLibFile.getAbsolutePath() + "\n\n--- OptiFine installer log ---\n" + currentLog.toString()); - } - new OptiFinePatcher(optifineLibFile).saveTweaker(); - convertedFile.delete(); - - publishProgress("(5/5) Done!", null, null); - Thread.sleep(500); - } catch (Throwable th) { - throwable = th; - } finally { - System.setOut(logOut.getRootStream()); - System.setErr(logErr.getRootStream()); - /* - if (throwable != null && convertedFile != null) { - convertedFile.delete(); - } - */ - return throwable; - } - } -/* - private Object fromConfig(DexClassLoader loader, String name) throws ReflectiveOperationException { - Field f = loader.loadClass("Config").getDeclaredField(name); - f.setAccessible(true); - return f.get(null); - } -*/ - @Override - protected void onProgressUpdate(String[] text) { - super.onProgressUpdate(text); - dialog.setMessage(text[0]); - if (text.length > 1 && text[1] != null) { - dialog.setMax(Integer.valueOf(text[1])); - } if (text.length > 2) { - dialog.setProgress(dialog.getProgress() + 1); - } if (text.length > 3 && text[3] != null) { - dialog.setIndeterminate(Boolean.getBoolean(text[3])); - } - } - - @Override - protected void onPostExecute(Throwable th) { - super.onPostExecute(th); - dialog.dismiss(); - - new RefreshVersionListTask().execute(); - - if (th == null) { - Toast.makeText(MCLauncherActivity.this, R.string.toast_optifine_success, Toast.LENGTH_SHORT).show(); - } else { - Tools.showError(MCLauncherActivity.this, th); - } - } - } - private class ViewPagerAdapter extends FragmentPagerAdapter { List fragmentList = new ArrayList<>(); diff --git a/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java b/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java index 73bef549e..738481e10 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java +++ b/app/src/main/java/net/kdt/pojavlaunch/MainActivity.java @@ -68,7 +68,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, } } }; - private MinecraftGLView glSurfaceView; + private MinecraftGLView minecraftGLView; private int guiScale; private DisplayMetrics displayMetrics; public boolean hiddenTextIgnoreUpdate = true; @@ -110,6 +110,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, private Button[] controlButtons; + private File logFile; private PrintStream logStream; /* @@ -122,10 +123,10 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, private boolean isLogAllow = false; // private int navBarHeight = 40; - private static final int LTYPE_PROCESS = 0; - private static final int LTYPE_INVOCATION = 1; - private static final int LTYPE_CREATEJAVAVM = 2; - private static final int LAUNCH_TYPE; + public static final int LTYPE_PROCESS = 0; + public static final int LTYPE_INVOCATION = 1; + public static final int LTYPE_CREATEJAVAVM = 2; + public static final int LAUNCH_TYPE; static { int launchTypeFinal = LTYPE_INVOCATION; @@ -155,7 +156,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, setContentView(R.layout.main); try { - File logFile = new File(Tools.MAIN_PATH, "latestlog.txt"); + logFile = new File(Tools.MAIN_PATH, "latestlog.txt"); logFile.delete(); logFile.createNewFile(); logStream = new PrintStream(logFile.getAbsolutePath()); @@ -175,52 +176,6 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, } }); - ExitManager.setExitTrappedListener(new ExitManager.ExitTrappedListener(){ - @Override - public void onExitTrapped() - { - logStream.close(); - runOnUiThread(new Runnable(){ - - @Override - public void run() { - isExited = true; - - AlertDialog.Builder d = new AlertDialog.Builder(MainActivity.this); - d.setTitle(R.string.mcn_exit_title); - - try { - File crashLog = Tools.lastFileModified(Tools.crashPath); - if(crashLog != null && Tools.read(crashLog.getAbsolutePath()).startsWith("---- Minecraft Crash Report ----")){ - d.setMessage(R.string.mcn_exit_crash); - } else { - fullyExit(); - return; - } - } catch (Throwable th) { - d.setMessage(getStr(R.string.mcn_exit_errcrash) + "\n" + Log.getStackTraceString(th)); - } - d.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){ - - @Override - public void onClick(DialogInterface p1, int p2) - { - fullyExit(); - } - }); - d.setCancelable(false); - d.show(); - } - }); - } - }); - - try { - ExitManager.disableSystemExit(); - } catch (Throwable th) { - Log.w(Tools.APP_NAME, "Could not disable System.exit() method!", th); - } - mProfile = PojavProfile.getCurrentProfileContent(this); mVersionInfo = Tools.getVersionInfo(mProfile.getVersion()); @@ -259,7 +214,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, @Override public boolean onNavigationItemSelected(MenuItem menuItem) { switch (menuItem.getItemId()) { - case R.id.nav_forceclose: dialogForceClose(); + case R.id.nav_forceclose: dialogForceClose(MainActivity.this); break; case R.id.nav_viewlog: openLogOutput(); break; @@ -330,22 +285,18 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, this.toggleControlButton.setOnClickListener(this); this.zoomButton.setVisibility(mVersionInfo.optifineLib == null ? View.GONE : View.VISIBLE); - this.glSurfaceView = (MinecraftGLView) findViewById(R.id.main_game_render_view); + this.minecraftGLView = (MinecraftGLView) findViewById(R.id.main_game_render_view); ControlButton[] specialButtons = ControlButton.getSpecialButtons(); specialButtons[0].specialButtonListener = new View.OnClickListener(){ - @Override - public void onClick(View p1) - { + public void onClick(View view) { showKeyboard(); } }; specialButtons[1].specialButtonListener = new View.OnClickListener(){ - @Override - public void onClick(View view) - { + public void onClick(View view) { MainActivity.this.onClick(toggleControlButton); } }; @@ -382,11 +333,11 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, if (isPointerCaptureSupported()) { if (!AndroidDisplay.grab && isCapturing) { - pointerSurface.releaseCapture(); // glSurfaceView.releasePointerCapture(); + pointerSurface.releaseCapture(); // minecraftGLView.releasePointerCapture(); isCapturing = false; } else if (AndroidDisplay.grab && !isCapturing) { - glSurfaceView.requestFocus(); - pointerSurface.requestCapture(); // glSurfaceView.requestPointerCapture(); + minecraftGLView.requestFocus(); + pointerSurface.requestCapture(); // minecraftGLView.requestPointerCapture(); isCapturing = true; } } @@ -465,9 +416,9 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, // System.loadLibrary("Regal"); - glSurfaceView.setFocusable(true); - glSurfaceView.setFocusableInTouchMode(true); - glSurfaceView.setEGLContextClientVersion(2); + minecraftGLView.setFocusable(true); + minecraftGLView.setFocusableInTouchMode(true); + // minecraftGLView.setEGLContextClientVersion(2); glTouchListener = new OnTouchListener(){ private boolean isTouchInHotbar = false; @@ -478,7 +429,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, // System.out.println("Pre touch, isTouchInHotbar=" + Boolean.toString(isTouchInHotbar) + ", action=" + MotionEvent.actionToString(e.getActionMasked())); int x = ((int) e.getX()) / scaleFactor; - int y = (glSurfaceView.getHeight() - ((int) e.getY())) / scaleFactor; + int y = (minecraftGLView.getHeight() - ((int) e.getY())) / scaleFactor; int hudKeyHandled = handleGuiBar(x, y, e); if (!AndroidDisplay.grab && gestureDetector.onTouchEvent(e)) { if (hudKeyHandled != -1) { @@ -660,7 +611,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, }; if (isPointerCaptureSupported()) { - this.pointerSurface = new PointerOreoWrapper(glSurfaceView); + this.pointerSurface = new PointerOreoWrapper(minecraftGLView); this.pointerSurface.setOnCapturedPointerListener(new PointerOreoWrapper.OnCapturedPointerListener(){ @Override public boolean onCapturedPointer(View view, MotionEvent event) { @@ -669,7 +620,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, }); } - glSurfaceView.setOnHoverListener(new View.OnHoverListener(){ + minecraftGLView.setOnHoverListener(new View.OnHoverListener(){ @Override public boolean onHover(View p1, MotionEvent p2) { if (!AndroidDisplay.grab && mIsResuming) { @@ -678,62 +629,56 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, return true; } }); - glSurfaceView.setOnTouchListener(glTouchListener); - glSurfaceView.setRenderer(new GLTextureView.Renderer() { + minecraftGLView.setOnTouchListener(glTouchListener); + minecraftGLView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener(){ + + private boolean isCalled = false; @Override - public void onSurfaceDestroyed(GL10 gl) { - Log.d(Tools.APP_NAME, "Surface destroyed."); - } - - @Override - public void onSurfaceCreated(GL10 gl, javax.microedition.khronos.egl.EGLConfig p2) - { + public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) { + AndroidDisplay.windowWidth = width; + AndroidDisplay.windowHeight = height; calculateMcScale(); - - EGL10 egl10 = (EGL10) EGLContext.getEGL(); - AndroidContextImplementation.theEgl = egl10; - AndroidContextImplementation.context = egl10.eglGetCurrentContext(); - AndroidContextImplementation.display = egl10.eglGetCurrentDisplay(); - AndroidContextImplementation.read = egl10.eglGetCurrentSurface(EGL10.EGL_READ); - AndroidContextImplementation.draw = egl10.eglGetCurrentSurface(EGL10.EGL_DRAW); - System.out.println(new StringBuffer().append("Gave up context: ").append(AndroidContextImplementation.context).toString()); + // Should we do that? + if (!isCalled) { + isCalled = true; + + JREUtils.setupBridgeWindow(new Surface(texture)); + + new Thread(new Runnable(){ - // Does it required anymore? - // AndroidDisplay.windowWidth += navBarHeight; - - if (LAUNCH_TYPE != LTYPE_PROCESS) { - BinaryExecutor.setupBridgeEGL(); - egl10.eglMakeCurrent(AndroidContextImplementation.display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - } - - new Thread(new Runnable(){ - - @Override - public void run() { - try { - Thread.sleep(200); - runCraft(); - } catch (Throwable e) { - Tools.showError(MainActivity.this, e, true); + @Override + public void run() { + try { + Thread.sleep(200); + runCraft(); + } catch (Throwable e) { + Tools.showError(MainActivity.this, e, true); + } } - } - }).start(); + }).start(); + } } - @Override - public void onDrawFrame(GL10 gl) { - //mkToast("onDrawFrame"); - } @Override - public void onSurfaceChanged(GL10 gl, int width, int height) { - AndroidDisplay.windowWidth = width / scaleFactor; - AndroidDisplay.windowHeight = height / scaleFactor; + public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) { + return true; + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) { + AndroidDisplay.windowWidth = width; + AndroidDisplay.windowHeight = height; + calculateMcScale(); + + // TODO: Implement this method for GLFW window size callback + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture texture) { + } }); - glSurfaceView.setPreserveEGLContextOnPause(true); - glSurfaceView.setRenderMode(MinecraftGLView.RENDERMODE_CONTINUOUSLY); - // glSurfaceView.requestRender(); } catch (Throwable e) { e.printStackTrace(); Tools.showError(this, e, true); @@ -790,7 +735,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, public void onResume() { super.onResume(); mIsResuming = true; - if (glSurfaceView != null) glSurfaceView.requestRender(); + // if (minecraftGLView != null) minecraftGLView.requestRender(); final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; final View decorView = getWindow().getDecorView(); decorView.setSystemUiVisibility(uiOptions); @@ -800,8 +745,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); /* - if (hasFocus && glSurfaceView.getVisibility() == View.GONE) { - glSurfaceView.setVisibility(View.VISIBLE); + if (hasFocus && minecraftGLView.getVisibility() == View.GONE) { + minecraftGLView.setVisibility(View.VISIBLE); } */ } @@ -884,11 +829,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, } public static void fullyExit() { - if (!ExitManager.isExiting()) { - ExitManager.enableSystemExit(); - System.exit(0); - } - ExitManager.stopExitLoop(); + System.exit(0); } public void forceUserHome(String s) throws Exception { @@ -1001,14 +942,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, return args; } - private ShellProcessOperation mLaunchShell; - private void setEnvironment(String name, String value) throws ErrnoException, IOException { - if (LAUNCH_TYPE == LTYPE_PROCESS) { - mLaunchShell.writeToProcess("export " + name + "=" + value); - } else { - Os.setenv(name, value, true); - } - } + public static ShellProcessOperation mLaunchShell; private static void startStrace(int pid) throws Exception { String[] straceArgs = new String[] {"/system/bin/strace", @@ -1029,16 +963,12 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, // javaArgList.add("-Xms512m"); javaArgList.add("-Xmx512m"); - /* - javaArgList.add("-Djava.library.path=" + - // TODO lwjgl2 vs lwjgl3 native path - getApplicationInfo().nativeLibraryDir - ); - */ javaArgList.add("-Djava.home=" + Tools.homeJreDir); + javaArgList.add("-Djava.io.tmpdir=" + getCacheDir().getAbsolutePath()); javaArgList.add("-Dos.name=Linux"); + // javaArgList.add("-Dorg.lwjgl.libname=liblwjgl3.so"); // javaArgList.add("-Dorg.lwjgl.system.jemalloc.libname=libjemalloc.so"); javaArgList.add("-Dorg.lwjgl.opengl.libname=libgl04es.so"); // javaArgList.add("-Dorg.lwjgl.opengl.libname=libRegal.so"); @@ -1052,6 +982,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, javaArgList.add("-Dglfwstub.windowWidth=" + AndroidDisplay.windowWidth); javaArgList.add("-Dglfwstub.windowHeight=" + AndroidDisplay.windowHeight); + javaArgList.add("-Dglfwstub.initEgl=false"); + if (mVersionInfo.arguments != null) { // Minecraft 1.13+ @@ -1092,35 +1024,11 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, mLaunchShell.initInputStream(this); } - String libName = System.getProperty("os.arch").contains("64") ? "lib64" : "lib"; - String ldLibraryPath = ( - // To make libjli.so ignore re-execute - Tools.homeJreDir + "/lib/server:" + - - "/system/" + libName + ":" + - "/vendor/" + libName + ":" + - "/vendor/" + libName + "/hw:" + - - getApplicationInfo().nativeLibraryDir + ":" + - - Tools.homeJreDir + "/lib/jli:" + - Tools.homeJreDir + "/lib" - - - // "$JAVA_HOME/lib:$JAVA_HOME/lib/jli:$JAVA_HOME/lib/server" - ); - - setEnvironment("JAVA_HOME", Tools.homeJreDir); - setEnvironment("HOME", Tools.MAIN_PATH); - setEnvironment("TMPDIR", getCacheDir().getAbsolutePath()); - // setEnvironment("LIBGL_MIPMAP", "3"); - setEnvironment("MESA_GLSL_CACHE_DIR", getCacheDir().getAbsolutePath()); - setEnvironment("LD_LIBRARY_PATH", ldLibraryPath); - setEnvironment("PATH", Tools.homeJreDir + "/bin:" + Os.getenv("PATH")); - // can fix java? // setEnvironment("ORIGIN", Tools.homeJreDir + "/lib"); + JREUtils.setJavaEnvironment(this); + if (LAUNCH_TYPE == LTYPE_PROCESS) { mLaunchShell.writeToProcess("cd $HOME"); @@ -1130,8 +1038,23 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, Tools.showError(this, new ErrnoException("java", exitCode), false); } } else { // Type Invocation - final FileDescriptor logFile = BinaryExecutor.redirectStdio(); - + // Is it need? + /* + Os.dup2(FileDescriptor.err, OsConstants.STDERR_FILENO); + Os.dup2(FileDescriptor.out, OsConstants.STDOUT_FILENO); + */ + + JREUtils.redirectStdio(); + // DEPRECATED constructor (String) api 29 + /* + FileObserver fobs = new FileObserver(logFile.getAbsolutePath(), FileObserver.MODIFY){ + @Override + public void onEvent(int event, String str) { + + } + }; + fobs.startWatching(); + */ new Thread(new Runnable() { @Override public void run() { @@ -1148,9 +1071,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, } }, "RuntimeLogThread").start(); - BinaryExecutor.setLdLibraryPath(ldLibraryPath); - 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()); @@ -1314,9 +1236,8 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, ((Button) view).setText(isVis ? R.string.control_mouseoff: R.string.control_mouseon); } - public void dialogForceClose() - { - new AlertDialog.Builder(this) + public static void dialogForceClose(Context ctx) { + new AlertDialog.Builder(ctx) .setMessage(R.string.mcn_exit_confirm) .setNegativeButton(android.R.string.cancel, null) .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){ @@ -1350,6 +1271,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, @Override public void onBackPressed() { // Prevent back + // Catch back as Esc keycode at another place } public void hideKeyboard() { @@ -1365,7 +1287,7 @@ public class MainActivity extends AppCompatActivity implements OnTouchListener, public void showKeyboard() { ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE)).toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY); - glSurfaceView.requestFocus(); + minecraftGLView.requestFocus(); } private void setRightOverride(boolean val) { diff --git a/app/src/main/java/net/kdt/pojavlaunch/MinecraftGLView.java b/app/src/main/java/net/kdt/pojavlaunch/MinecraftGLView.java index 8068650d1..0373e660e 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/MinecraftGLView.java +++ b/app/src/main/java/net/kdt/pojavlaunch/MinecraftGLView.java @@ -5,7 +5,7 @@ import android.util.*; import android.view.*; import com.kdt.glsupport.*; -public class MinecraftGLView extends GLTextureView +public class MinecraftGLView extends TextureView { // private View.OnTouchListener mTouchListener; public MinecraftGLView(Context context) { @@ -17,18 +17,5 @@ public class MinecraftGLView extends GLTextureView super(context, attributeSet); //setPreserveEGLContextOnPause(true); } - - @Override - public void setOnTouchListener(View.OnTouchListener l) - { - super.setOnTouchListener(l); - // mTouchListener = l; - } - - @Override - public void setOnClickListener(View.OnClickListener l) - { - super.setOnClickListener(l); - } } diff --git a/app/src/main/java/net/kdt/pojavlaunch/Tools.java b/app/src/main/java/net/kdt/pojavlaunch/Tools.java index 5b9a0278e..28e40407f 100644 --- a/app/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -84,6 +84,7 @@ public final class Tools String[] classpath = generateLibClasspath(info); // Debug: LWJGL 3 override + File lwjgl2Folder = new File(Tools.MAIN_PATH, "lwjgl2"); File lwjgl3Folder = new File(Tools.MAIN_PATH, "lwjgl3"); if (info.arguments != null && lwjgl3Folder.exists()) { for (File file: lwjgl3Folder.listFiles()) { @@ -91,6 +92,12 @@ public final class Tools libStr.append(file.getAbsolutePath() + ":"); } } + } else if (lwjgl2Folder.exists()) { + for (File file: lwjgl2Folder.listFiles()) { + if (file.getName().endsWith(".jar")) { + libStr.append(file.getAbsolutePath() + ":"); + } + } } if (isClientFirst) { diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk new file mode 100644 index 000000000..01952f1c1 --- /dev/null +++ b/app/src/main/jni/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +# Link GLESv2 for test +LOCAL_LDLIBS := -ldl -llog -landroid -lEGL -lGLESv2 +LOCAL_MODULE := pojavexec +LOCAL_CFLAGS += -DGLES_TEST +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..c1c040a55 --- /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..8f523dc83 --- /dev/null +++ b/app/src/main/jni/egl_bridge.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include + +#ifdef GLES_TEST +#include +#endif + +#include +#include + +struct PotatoBridge { + ANativeWindow* androidWindow; + void* androidDisplay; + + 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) { + potatoBridge.androidDisplay = EGL_DEFAULT_DISPLAY; + potatoBridge.androidWindow = ANativeWindow_fromSurface(env, surface); +} + +// Called from JNI_OnLoad of liblwjgl_opengl32 +void pojav_openGLOnLoad() { + +} +void pojav_openGLOnUnload() { + +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglInit(JNIEnv* env, jclass clazz) { + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglMakeCurrent(JNIEnv* env, jclass clazz) { + printf("EGLBridge: Initializing\n"); + printf("ANativeWindow pointer = %p\n", potatoBridge.androidWindow); + + potatoBridge.eglDisplay = eglGetDisplay(potatoBridge.androidDisplay); + if (potatoBridge.eglDisplay == EGL_NO_DISPLAY) { + printf("Error: eglGetDefaultDisplay() failed: %p\n", eglGetError()); + return JNI_FALSE; + } + + eglMakeCurrent(potatoBridge.eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (!eglInitialize(potatoBridge.eglDisplay, NULL, NULL)) { + printf("Error: eglInitialize() failed\n"); + return JNI_FALSE; + } + + static const EGLint attribs[] = { + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 0, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 0, + 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 JNI_FALSE; + } + + assert(config); + assert(num_configs > 0); + + if (!eglGetConfigAttrib(potatoBridge.eglDisplay, config, EGL_NATIVE_VISUAL_ID, &vid)) { + printf("Error: eglGetConfigAttrib() failed\n"); + return JNI_FALSE; + } + + 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 JNI_FALSE; + } + + // test eglQueryContext() + { + EGLint val; + eglQueryContext(potatoBridge.eglDisplay, potatoBridge.eglContext, EGL_CONTEXT_CLIENT_VERSION, &val); + assert(val == 2); + } + + potatoBridge.eglSurface = eglCreateWindowSurface(potatoBridge.eglDisplay, config, potatoBridge.androidWindow, NULL); + + if (!potatoBridge.eglSurface) { + printf("Error: eglCreateWindowSurface failed: %p\n", eglGetError()); + return JNI_FALSE; + } + + // sanity checks + { + EGLint val; + assert(eglGetConfigAttrib(potatoBridge.eglDisplay, config, EGL_SURFACE_TYPE, &val)); + assert(val & EGL_WINDOW_BIT); + } + +/* + return JNI_TRUE; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglMakeCurrent(JNIEnv* env, jclass clazz) { +*/ + printf("EGLBridge: Making current\n"); + printf("EGLContext=%p, EGLDisplay=%p, EGLSurface=%p\n", + potatoBridge.eglContext, + potatoBridge.eglDisplay, + potatoBridge.eglSurface + ); + + EGLBoolean success = eglMakeCurrent(potatoBridge.eglDisplay, potatoBridge.eglSurface, potatoBridge.eglSurface, potatoBridge.eglContext); + if (success == EGL_FALSE) { + printf("Error: eglMakeCurrent() failed: %p\n", eglGetError()); + } + + // Test +#ifdef GLES_TEST + glClearColor(0.5f, 0.5f, 0.5f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + eglSwapBuffers(potatoBridge.eglDisplay, potatoBridge.eglSurface); +#endif + + return success == EGL_TRUE ? JNI_TRUE : JNI_FALSE; +} + +JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_GLFW_nativeEglTerminate(JNIEnv* env, jclass clazz) { + printf("EGLBridge: Terminating\n"); + 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(); + + return JNI_TRUE; +} + +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 a7c4104f6..000000000 Binary files a/app/src/main/jniLibs/armeabi-v7a/libbinexecutor.so and /dev/null differ diff --git a/app/src/main/jniLibs/armeabi-v7a/liblwjgl.so b/app/src/main/jniLibs/armeabi-v7a/liblwjgl.so index d027f70ed..dccd33a5b 100644 Binary files a/app/src/main/jniLibs/armeabi-v7a/liblwjgl.so and b/app/src/main/jniLibs/armeabi-v7a/liblwjgl.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/liblwjgl_opengl.so b/app/src/main/jniLibs/armeabi-v7a/liblwjgl_opengl.so index 8a71b82c7..16a712fe1 100644 Binary files a/app/src/main/jniLibs/armeabi-v7a/liblwjgl_opengl.so and b/app/src/main/jniLibs/armeabi-v7a/liblwjgl_opengl.so differ diff --git a/app/src/main/jniLibs/armeabi-v7a/liblwjgl_stb.so b/app/src/main/jniLibs/armeabi-v7a/liblwjgl_stb.so index d041485b4..5356f2e9b 100644 Binary files a/app/src/main/jniLibs/armeabi-v7a/liblwjgl_stb.so and b/app/src/main/jniLibs/armeabi-v7a/liblwjgl_stb.so differ diff --git a/app/src/main/jniLibs_disabled/arm64-v8a/libbinexecutor.so b/app/src/main/jniLibs_disabled/arm64-v8a/libbinexecutor.so index 17afd5af6..87494f496 100644 Binary files a/app/src/main/jniLibs_disabled/arm64-v8a/libbinexecutor.so and b/app/src/main/jniLibs_disabled/arm64-v8a/libbinexecutor.so differ diff --git a/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl.so b/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl.so index 84c246131..95084baf9 100644 Binary files a/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl.so and b/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl.so differ diff --git a/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_opengl.so b/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_opengl.so index 5a63edc4a..e0f2bffaf 100644 Binary files a/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_opengl.so and b/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_opengl.so differ diff --git a/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_stb.so b/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_stb.so index 63a1121fb..27c9f3106 100644 Binary files a/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_stb.so and b/app/src/main/jniLibs_disabled/arm64-v8a/liblwjgl_stb.so differ diff --git a/app/src/main/jniLibs_disabled/x86/libbinexecutor.so b/app/src/main/jniLibs_disabled/x86/libbinexecutor.so index 44dedb9ca..319db32dc 100644 Binary files a/app/src/main/jniLibs_disabled/x86/libbinexecutor.so and b/app/src/main/jniLibs_disabled/x86/libbinexecutor.so differ diff --git a/app/src/main/jniLibs_disabled/x86/liblwjgl.so b/app/src/main/jniLibs_disabled/x86/liblwjgl.so index 821cac13e..596ec90ab 100644 Binary files a/app/src/main/jniLibs_disabled/x86/liblwjgl.so and b/app/src/main/jniLibs_disabled/x86/liblwjgl.so differ diff --git a/app/src/main/jniLibs_disabled/x86/liblwjgl_opengl.so b/app/src/main/jniLibs_disabled/x86/liblwjgl_opengl.so index 498f2b952..83c0b0685 100644 Binary files a/app/src/main/jniLibs_disabled/x86/liblwjgl_opengl.so and b/app/src/main/jniLibs_disabled/x86/liblwjgl_opengl.so differ diff --git a/app/src/main/jniLibs_disabled/x86/liblwjgl_stb.so b/app/src/main/jniLibs_disabled/x86/liblwjgl_stb.so index 049726864..bc46b37d9 100644 Binary files a/app/src/main/jniLibs_disabled/x86/liblwjgl_stb.so and b/app/src/main/jniLibs_disabled/x86/liblwjgl_stb.so differ diff --git a/app/src/main/jniLibs_disabled/x86_64/libbinexecutor.so b/app/src/main/jniLibs_disabled/x86_64/libbinexecutor.so index 35b7d35ae..ab86b718e 100644 Binary files a/app/src/main/jniLibs_disabled/x86_64/libbinexecutor.so and b/app/src/main/jniLibs_disabled/x86_64/libbinexecutor.so differ diff --git a/app/src/main/jniLibs_disabled/x86_64/liblwjgl.so b/app/src/main/jniLibs_disabled/x86_64/liblwjgl.so index abcf88002..6c752607a 100644 Binary files a/app/src/main/jniLibs_disabled/x86_64/liblwjgl.so and b/app/src/main/jniLibs_disabled/x86_64/liblwjgl.so differ diff --git a/app/src/main/jniLibs_disabled/x86_64/liblwjgl_opengl.so b/app/src/main/jniLibs_disabled/x86_64/liblwjgl_opengl.so index ee9bc1cbb..25a3c0ef5 100644 Binary files a/app/src/main/jniLibs_disabled/x86_64/liblwjgl_opengl.so and b/app/src/main/jniLibs_disabled/x86_64/liblwjgl_opengl.so differ diff --git a/app/src/main/jniLibs_disabled/x86_64/liblwjgl_stb.so b/app/src/main/jniLibs_disabled/x86_64/liblwjgl_stb.so index 035f5f8fa..3fcab30c9 100644 Binary files a/app/src/main/jniLibs_disabled/x86_64/liblwjgl_stb.so and b/app/src/main/jniLibs_disabled/x86_64/liblwjgl_stb.so differ diff --git a/app/src/main/res/layout/install_mod.xml b/app/src/main/res/layout/install_mod.xml new file mode 100644 index 000000000..b2c86494c --- /dev/null +++ b/app/src/main/res/layout/install_mod.xml @@ -0,0 +1,21 @@ + + + + + +