From e66d7fd516fda8d629c4397c2e839d7dc7860bdf Mon Sep 17 00:00:00 2001 From: Maksim Date: Mon, 16 May 2022 16:31:30 +0300 Subject: [PATCH] Android: fix few crashes --- build/android/app/build.gradle | 11 ++- .../workmanager/WorkerViewModelFactory.kt | 2 +- build/android/build.gradle | 5 +- .../gradle/wrapper/gradle-wrapper.properties | 4 +- build/android/native/build.gradle | 6 +- src/client/clientlauncher.cpp | 3 +- src/porting_android.cpp | 71 +++++++++++-------- src/porting_android.h | 1 + 8 files changed, 59 insertions(+), 44 deletions(-) diff --git a/build/android/app/build.gradle b/build/android/app/build.gradle index 630187070..0b7895d54 100644 --- a/build/android/app/build.gradle +++ b/build/android/app/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'kotlin-android' android { compileSdkVersion 32 buildToolsVersion '32.0.0' - ndkVersion '23.1.7779620' + ndkVersion '23.2.8568313' defaultConfig { applicationId 'com.multicraft.game' minSdkVersion 21 @@ -61,7 +61,6 @@ android { } } -import com.android.build.OutputFile import org.apache.tools.ant.taskdefs.condition.Os task prepareAssetsFiles() { @@ -125,7 +124,7 @@ def abiCodes = ['armeabi-v7a': 0, 'arm64-v8a': 1] android.applicationVariants.all { variant -> variant.outputs.each { output -> - def abiName = output.getFilter(OutputFile.ABI) + def abiName = output.filters[0].identifier output.versionCodeOverride = abiCodes.get(abiName, 0) + variant.versionCode } } @@ -135,9 +134,9 @@ dependencies { implementation project(':native') /* Third-party libraries */ - implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'androidx.appcompat:appcompat-resources:1.4.1' + implementation 'androidx.appcompat:appcompat:1.4.2' + implementation 'androidx.appcompat:appcompat-resources:1.4.2' implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' implementation 'androidx.work:work-runtime-ktx:2.7.1' - implementation 'com.google.android.material:material:1.5.0' + implementation 'com.google.android.material:material:1.6.1' } diff --git a/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModelFactory.kt b/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModelFactory.kt index 1547c6b27..8d4280956 100644 --- a/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModelFactory.kt +++ b/build/android/app/src/main/java/com/multicraft/game/workmanager/WorkerViewModelFactory.kt @@ -30,7 +30,7 @@ class WorkerViewModelFactory( private val zips: Array ) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { + override fun create(modelClass: Class): T { if (modelClass.isAssignableFrom(WorkerViewModel::class.java)) { @Suppress("UNCHECKED_CAST") return WorkerViewModel(application, zips) as T diff --git a/build/android/build.gradle b/build/android/build.gradle index db3bc0f19..c06b87608 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -15,9 +15,10 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' + classpath 'com.android.tools.build:gradle:7.2.1' + //noinspection GradleDependency classpath 'de.undercouch:gradle-download-task:4.1.2' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/build/android/gradle/wrapper/gradle-wrapper.properties b/build/android/gradle/wrapper/gradle-wrapper.properties index 37ebaa21e..dc3e3d104 100644 --- a/build/android/gradle/wrapper/gradle-wrapper.properties +++ b/build/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Fri Feb 11 12:29:43 EET 2022 +#Wed May 25 12:00:00 EET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/build/android/native/build.gradle b/build/android/native/build.gradle index 79e08f3d0..8aaa36a7b 100644 --- a/build/android/native/build.gradle +++ b/build/android/native/build.gradle @@ -4,7 +4,7 @@ apply plugin: 'de.undercouch.download' android { compileSdkVersion 32 buildToolsVersion '32.0.0' - ndkVersion '23.1.7779620' + ndkVersion '23.2.8568313' defaultConfig { minSdkVersion 21 targetSdkVersion 32 @@ -92,3 +92,7 @@ task getIconv(dependsOn: downloadIconv, type: Copy) { preBuild.dependsOn getDeps preBuild.dependsOn getIconv + +android.defaultConfig.externalNativeBuild.ndkBuild { + arguments 'prebuilt=$(if $(strip $(wildcard $(prebuilt_path))),$(prebuilt_path),.)' +} diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 3633aaf96..88f18c4b9 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -279,7 +279,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) #endif #if defined(__ANDROID__) || defined(__IOS__) - porting::notifyExitGame(); + if (!g_gamecallback->shutdown_requested) + porting::notifyExitGame(); #endif } //try diff --git a/src/porting_android.cpp b/src/porting_android.cpp index a46c2795a..59b4cbc8c 100644 --- a/src/porting_android.cpp +++ b/src/porting_android.cpp @@ -1,6 +1,7 @@ /* Minetest Copyright (C) 2014 celeron55, Perttu Ahola +Copyright (C) 2014-2022 Maksim Gamarnik [MoNTE48] Maksym48@pm.me This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by @@ -29,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "log.h" +#include #include #include #include @@ -40,8 +42,17 @@ with this program; if not, write to the Free Software Foundation, Inc., extern int main(int argc, char *argv[]); extern "C" void external_pause_game(); +static std::atomic ran = {false}; + +jmethodID notifyExit; + void android_main(android_app *app) { + if (ran.exchange(true)) { + errorstream << "Caught second android_main execution in a process" << std::endl; + return; + } + porting::app_global = app; Thread::setName("Main"); @@ -58,9 +69,9 @@ void android_main(android_app *app) porting::finishGame("Unknown error"); } - porting::cleanupAndroid(); infostream << "Shutting down." << std::endl; - exit(0); + porting::cleanupAndroid(); + _Exit(0); } /** @@ -69,13 +80,6 @@ void android_main(android_app *app) * ToDo: this doesn't work as expected, there's a workaround for it right now */ extern "C" { - JNIEXPORT void JNICALL Java_com_multicraft_game_GameActivity_putMessageBoxResult( - JNIEnv *env, jclass thiz, jstring text) - { - errorstream << - "Java_com_multicraft_game_GameActivity_putMessageBoxResult got: " << - std::string((const char*) env->GetStringChars(text, nullptr)) << std::endl; - } JNIEXPORT void JNICALL Java_com_multicraft_game_GameActivity_pauseGame( JNIEnv *env, jclass clazz) { @@ -93,6 +97,7 @@ namespace porting { android_app *app_global; JNIEnv *jnienv; jclass nativeActivity; +jobject activityObj; jclass findClass(const std::string &classname) { @@ -102,8 +107,7 @@ jclass findClass(const std::string &classname) jclass nativeactivity = jnienv->FindClass("android/app/NativeActivity"); jmethodID getClassLoader = jnienv->GetMethodID( nativeactivity, "getClassLoader", "()Ljava/lang/ClassLoader;"); - jobject cls = jnienv->CallObjectMethod( - app_global->activity->clazz, getClassLoader); + jobject cls = jnienv->CallObjectMethod(activityObj, getClassLoader); jclass classLoader = jnienv->FindClass("java/lang/ClassLoader"); jmethodID findClass = jnienv->GetMethodID(classLoader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); @@ -119,6 +123,7 @@ void initAndroid() lJavaVMAttachArgs.version = JNI_VERSION_1_6; lJavaVMAttachArgs.name = PROJECT_NAME_C "NativeThread"; lJavaVMAttachArgs.group = nullptr; + activityObj = app_global->activity->clazz; if (jvm->AttachCurrentThread(&porting::jnienv, &lJavaVMAttachArgs) == JNI_ERR) { errorstream << "Failed to attach native thread to jvm" << std::endl; @@ -131,6 +136,10 @@ void initAndroid() "porting::initAndroid unable to find java native activity class" << std::endl; + notifyExit = jnienv->GetMethodID(nativeActivity, "notifyExitGame", "()V"); + FATAL_ERROR_IF(notifyExit == nullptr, + "porting::initAndroid unable to find java notifyExit method"); + #ifdef GPROF // in the start-up code __android_log_print(ANDROID_LOG_ERROR, PROJECT_NAME_C, @@ -149,6 +158,7 @@ void cleanupAndroid() JavaVM *jvm = app_global->activity->vm; jvm->DetachCurrentThread(); + ANativeActivity_finish(porting::app_global->activity); } static std::string javaStringToUTF8(jstring js) @@ -198,14 +208,14 @@ void initializePathsAndroid() "getAbsolutePath", "()Ljava/lang/String;"); std::string path_storage = getAndroidPath(cls_Env, nullptr, mt_getAbsPath, "getExternalStorageDirectory"); - std::string path_data = getAndroidPath(nativeActivity, app_global->activity->clazz, mt_getAbsPath, + std::string path_data = getAndroidPath(nativeActivity, activityObj, mt_getAbsPath, "getFilesDir"); path_user = path_storage + DIR_DELIM + "Android/data/com.multicraft.game/files"; path_share = path_data; path_locale = path_data + DIR_DELIM + "locale"; path_cache = getAndroidPath(nativeActivity, - app_global->activity->clazz, mt_getAbsPath, "getCacheDir"); + activityObj, mt_getAbsPath, "getCacheDir"); } void showInputDialog(const std::string &acceptButton, const std::string &hint, @@ -222,7 +232,7 @@ void showInputDialog(const std::string &acceptButton, const std::string &hint, jstring jcurrent = jnienv->NewStringUTF(current.c_str()); jint jeditType = editType; - jnienv->CallVoidMethod(app_global->activity->clazz, showdialog, + jnienv->CallVoidMethod(activityObj, showdialog, jacceptButton, jhint, jcurrent, jeditType); } @@ -235,7 +245,7 @@ void openURIAndroid(const std::string &url) "porting::openURIAndroid unable to find java openURI method"); jstring jurl = jnienv->NewStringUTF(url.c_str()); - jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl); + jnienv->CallVoidMethod(activityObj, url_open, jurl); } int getInputDialogState() @@ -246,7 +256,7 @@ int getInputDialogState() FATAL_ERROR_IF(dialogstate == nullptr, "porting::getInputDialogState unable to find java dialog state method"); - return jnienv->CallIntMethod(app_global->activity->clazz, dialogstate); + return jnienv->CallIntMethod(activityObj, dialogstate); } std::string getInputDialogValue() @@ -257,8 +267,7 @@ std::string getInputDialogValue() FATAL_ERROR_IF(dialogvalue == nullptr, "porting::getInputDialogValue unable to find java dialog value method"); - jobject result = jnienv->CallObjectMethod(app_global->activity->clazz, - dialogvalue); + jobject result = jnienv->CallObjectMethod(activityObj, dialogvalue); const char *javachars = jnienv->GetStringUTFChars((jstring) result, nullptr); std::string text(javachars); @@ -272,7 +281,7 @@ float getTotalSystemMemory() long pages = sysconf(_SC_PHYS_PAGES); long page_size = sysconf(_SC_PAGE_SIZE); int divisor = 1024 * 1024 * 1024; - return pages * page_size / (float) divisor; + return (float) (pages * page_size) / (float) divisor; } bool hasRealKeyboard() @@ -290,7 +299,7 @@ void handleError(const std::string &errType, const std::string &err) std::string errorMessage = errType + ": " + err; jstring jerr = porting::getJniString(errorMessage); - jnienv->CallVoidMethod(app_global->activity->clazz, report_err, jerr); + jnienv->CallVoidMethod(activityObj, report_err, jerr); } void notifyServerConnect(bool is_multiplayer) @@ -303,18 +312,18 @@ void notifyServerConnect(bool is_multiplayer) auto param = (jboolean) is_multiplayer; - jnienv->CallVoidMethod(app_global->activity->clazz, notifyConnect, param); + jnienv->CallVoidMethod(activityObj, notifyConnect, param); } void notifyExitGame() { - jmethodID notifyExit = jnienv->GetMethodID(nativeActivity, - "notifyExitGame", "()V"); + if (jnienv == nullptr || activityObj == nullptr || notifyExit == nullptr) + return; - FATAL_ERROR_IF(notifyExit == nullptr, - "porting::notifyExit unable to find java notifyExit method"); + jnienv->CallVoidMethod(activityObj, notifyExit); - jnienv->CallVoidMethod(app_global->activity->clazz, notifyExit); + if (jnienv->ExceptionOccurred()) + jnienv->ExceptionClear(); } #ifndef SERVER @@ -330,7 +339,7 @@ float getDisplayDensity() FATAL_ERROR_IF(getDensity == nullptr, "porting::getDisplayDensity unable to find java getDensity method"); - value = jnienv->CallFloatMethod(app_global->activity->clazz, getDensity); + value = jnienv->CallFloatMethod(activityObj, getDensity); firstrun = false; } return value; @@ -355,7 +364,7 @@ void finishGame(const std::string &exc) exit(-1); jstring jexc = jnienv->NewStringUTF(exc.c_str()); - jnienv->CallVoidMethod(app_global->activity->clazz, finishMe, jexc); + jnienv->CallVoidMethod(activityObj, finishMe, jexc); exit(0); } @@ -366,13 +375,13 @@ jstring getJniString(const std::string &message) jbyteArray bytes = jnienv->NewByteArray(byteCount); jnienv->SetByteArrayRegion(bytes, 0, byteCount, pNativeMessage); - jclass charsetClass = jnienv->FindClass("java/nio/charset/Charset"); + jclass charsetClass = findClass("java/nio/charset/Charset"); jmethodID forName = jnienv->GetStaticMethodID( charsetClass, "forName", "(Ljava/lang/String;)Ljava/nio/charset/Charset;"); jstring utf8 = jnienv->NewStringUTF("UTF-8"); jobject charset = jnienv->CallStaticObjectMethod(charsetClass, forName, utf8); - jclass stringClass = jnienv->FindClass("java/lang/String"); + jclass stringClass = findClass("java/lang/String"); jmethodID ctor = jnienv->GetMethodID( stringClass, "", "([BLjava/nio/charset/Charset;)V"); @@ -390,6 +399,6 @@ void upgrade(const std::string &item) "porting::upgradeGame unable to find java upgrade method"); jstring jitem = jnienv->NewStringUTF(item.c_str()); - jnienv->CallVoidMethod(app_global->activity->clazz, upgradeGame, jitem); + jnienv->CallVoidMethod(activityObj, upgradeGame, jitem); } } diff --git a/src/porting_android.h b/src/porting_android.h index 8c62e4afe..59aebe7e0 100644 --- a/src/porting_android.h +++ b/src/porting_android.h @@ -1,6 +1,7 @@ /* Minetest Copyright (C) 2014 celeron55, Perttu Ahola +Copyright (C) 2014-2022 Maksim Gamarnik [MoNTE48] Maksym48@pm.me This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by