mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-18 09:07:48 -04:00
Feat[launcher]: user facing dialog when exiting with a signal
This commit is contained in:
parent
6998624e44
commit
b70ba22ead
@ -4,7 +4,6 @@ import static net.kdt.pojavlaunch.Tools.shareLog;
|
|||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
@ -20,22 +19,26 @@ public class ExitActivity extends AppCompatActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
int code = -1;
|
int code = -1; boolean isSignal = false;
|
||||||
Bundle extras = getIntent().getExtras();
|
Bundle extras = getIntent().getExtras();
|
||||||
if(extras != null) {
|
if(extras != null) {
|
||||||
code = extras.getInt("code",-1);
|
code = extras.getInt("code",-1);
|
||||||
|
isSignal = extras.getBoolean("isSignal", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int message = isSignal ? R.string.mcn_signal_title : R.string.mcn_exit_title;
|
||||||
|
|
||||||
new AlertDialog.Builder(this)
|
new AlertDialog.Builder(this)
|
||||||
.setMessage(getString(R.string.mcn_exit_title,code))
|
.setMessage(getString(message,code))
|
||||||
.setPositiveButton(R.string.main_share_logs, (dialog, which) -> shareLog(this))
|
.setPositiveButton(R.string.main_share_logs, (dialog, which) -> shareLog(this))
|
||||||
.setOnDismissListener(dialog -> ExitActivity.this.finish())
|
.setOnDismissListener(dialog -> ExitActivity.this.finish())
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showExitMessage(Context ctx, int code) {
|
public static void showExitMessage(Context ctx, int code, boolean isSignal) {
|
||||||
Intent i = new Intent(ctx,ExitActivity.class);
|
Intent i = new Intent(ctx,ExitActivity.class);
|
||||||
i.putExtra("code",code);
|
i.putExtra("code",code);
|
||||||
|
i.putExtra("isSignal", isSignal);
|
||||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
ctx.startActivity(i);
|
ctx.startActivity(i);
|
||||||
|
@ -572,6 +572,5 @@ public class JREUtils {
|
|||||||
static {
|
static {
|
||||||
System.loadLibrary("pojavexec");
|
System.loadLibrary("pojavexec");
|
||||||
System.loadLibrary("pojavexec_awt");
|
System.loadLibrary("pojavexec_awt");
|
||||||
System.loadLibrary("istdio");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,15 @@ LOCAL_SRC_FILES := tinywrapper/main.c tinywrapper/string_utils.c
|
|||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)/tinywrapper
|
LOCAL_C_INCLUDES := $(LOCAL_PATH)/tinywrapper
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
|
|
||||||
|
$(call import-module,prefab/bytehook)
|
||||||
|
LOCAL_PATH := $(HERE_PATH)
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
# Link GLESv2 for test
|
# Link GLESv2 for test
|
||||||
LOCAL_LDLIBS := -ldl -llog -landroid
|
LOCAL_LDLIBS := -ldl -llog -landroid
|
||||||
# -lGLESv2
|
# -lGLESv2
|
||||||
LOCAL_MODULE := pojavexec
|
LOCAL_MODULE := pojavexec
|
||||||
|
LOCAL_SHARED_LIBRARIES := bytehook
|
||||||
# LOCAL_CFLAGS += -DDEBUG
|
# LOCAL_CFLAGS += -DDEBUG
|
||||||
# -DGLES_TEST
|
# -DGLES_TEST
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
@ -40,6 +43,7 @@ LOCAL_SRC_FILES := \
|
|||||||
input_bridge_v3.c \
|
input_bridge_v3.c \
|
||||||
jre_launcher.c \
|
jre_launcher.c \
|
||||||
utils.c \
|
utils.c \
|
||||||
|
stdio_is.c \
|
||||||
driver_helper/nsbypass.c
|
driver_helper/nsbypass.c
|
||||||
|
|
||||||
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
|
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
|
||||||
@ -56,16 +60,6 @@ LOCAL_LDFLAGS := -z global
|
|||||||
include $(BUILD_SHARED_LIBRARY)
|
include $(BUILD_SHARED_LIBRARY)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
$(call import-module,prefab/bytehook)
|
|
||||||
LOCAL_PATH := $(HERE_PATH)
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
LOCAL_MODULE := istdio
|
|
||||||
LOCAL_SHARED_LIBRARIES := bytehook
|
|
||||||
LOCAL_SRC_FILES := \
|
|
||||||
stdio_is.c
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
LOCAL_MODULE := pojavexec_awt
|
LOCAL_MODULE := pojavexec_awt
|
||||||
LOCAL_SRC_FILES := \
|
LOCAL_SRC_FILES := \
|
||||||
|
@ -65,6 +65,62 @@ typedef jint JLI_Launch_func(int argc, char ** argv, /* main argc, argc */
|
|||||||
jint ergo /* ergonomics class policy */
|
jint ergo /* ergonomics class policy */
|
||||||
);
|
);
|
||||||
|
|
||||||
|
struct {
|
||||||
|
sigset_t tracked_sigset;
|
||||||
|
int pipe[2];
|
||||||
|
} abrt_waiter_data;
|
||||||
|
|
||||||
|
_Noreturn extern void nominal_exit(int code, bool is_signal);
|
||||||
|
|
||||||
|
_Noreturn static void* abrt_waiter_thread(void* extraArg) {
|
||||||
|
// Don't allow this thread to receive signals this thread is tracking.
|
||||||
|
// We should only receive them externally.
|
||||||
|
pthread_sigmask(SIG_BLOCK, &abrt_waiter_data.tracked_sigset, NULL);
|
||||||
|
int signal;
|
||||||
|
// Block for reading the signal ID until it arrives
|
||||||
|
read(abrt_waiter_data.pipe[0], &signal, sizeof(int));
|
||||||
|
// Die
|
||||||
|
nominal_exit(signal, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn static void abrt_waiter_handler(int signal) {
|
||||||
|
// Write the final signal into the pipe and block forever.
|
||||||
|
write(abrt_waiter_data.pipe[1], &signal, sizeof(int));
|
||||||
|
while(1) {};
|
||||||
|
};
|
||||||
|
|
||||||
|
static void abrt_waiter_setup() {
|
||||||
|
// Only abort on SIGABRT as the JVM either emits SIGABRT or SIGKILL (which we can't catch)
|
||||||
|
// when a fatal crash occurs. Still, keep expandability if we would want to add more
|
||||||
|
// user-friendly fatal signals in the future.
|
||||||
|
const static int tracked_signals[] = {SIGABRT};
|
||||||
|
const static int ntracked = (sizeof(tracked_signals) / sizeof(tracked_signals[0]));
|
||||||
|
struct sigaction sigactions[ntracked];
|
||||||
|
sigemptyset(&abrt_waiter_data.tracked_sigset);
|
||||||
|
for(size_t i = 0; i < ntracked; i++) {
|
||||||
|
sigaddset(&abrt_waiter_data.tracked_sigset, tracked_signals[i]);
|
||||||
|
sigactions[i].sa_handler = abrt_waiter_handler;
|
||||||
|
}
|
||||||
|
if(pipe(abrt_waiter_data.pipe) != 0) {
|
||||||
|
printf("Failed to set up aborter pipe: %s\n", strerror(errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pthread_t waiter_thread; int result;
|
||||||
|
if((result = pthread_create(&waiter_thread, NULL, abrt_waiter_thread, NULL)) != 0) {
|
||||||
|
printf("Failed to start up waiter thread: %s", strerror(result));
|
||||||
|
for(int i = 0; i < 2; i++) close(abrt_waiter_data.pipe[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Only set the sigactions *after* we have already set up the pipe and the thread.
|
||||||
|
for(size_t i = 0; i < ntracked; i++) {
|
||||||
|
if(sigaction(tracked_signals[i], &sigactions[i], NULL) != 0) {
|
||||||
|
// Not returning here because we may have set some handlers successfully.
|
||||||
|
// Some handling is better than no handling.
|
||||||
|
printf("Failed to set signal hander for signal %i: %s", i, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static jint launchJVM(int margc, char** margv) {
|
static jint launchJVM(int margc, char** margv) {
|
||||||
void* libjli = dlopen("libjli.so", RTLD_LAZY | RTLD_GLOBAL);
|
void* libjli = dlopen("libjli.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||||
|
|
||||||
@ -80,6 +136,8 @@ static jint launchJVM(int margc, char** margv) {
|
|||||||
else clean_sa.sa_handler = SIG_DFL;
|
else clean_sa.sa_handler = SIG_DFL;
|
||||||
sigaction(sigid, &clean_sa, NULL);
|
sigaction(sigid, &clean_sa, NULL);
|
||||||
}
|
}
|
||||||
|
// Set up the thread that will abort the launcher with an user-facing dialog on a signal.
|
||||||
|
abrt_waiter_setup();
|
||||||
|
|
||||||
// Boardwalk: silence
|
// Boardwalk: silence
|
||||||
// LOGD("JLI lib = %x", (int)libjli);
|
// LOGD("JLI lib = %x", (int)libjli);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <bytehook.h>
|
#include <bytehook.h>
|
||||||
|
#include <environ/environ.h>
|
||||||
|
|
||||||
//
|
//
|
||||||
// Created by maks on 17.02.21.
|
// Created by maks on 17.02.21.
|
||||||
@ -19,7 +20,6 @@ static volatile jclass exitTrap_exitClass;
|
|||||||
static volatile jmethodID exitTrap_staticMethod;
|
static volatile jmethodID exitTrap_staticMethod;
|
||||||
static JavaVM *exitTrap_jvm;
|
static JavaVM *exitTrap_jvm;
|
||||||
|
|
||||||
static JavaVM *stdiois_jvm;
|
|
||||||
static int pfd[2];
|
static int pfd[2];
|
||||||
static pthread_t logger;
|
static pthread_t logger;
|
||||||
static jmethodID logger_onEventLogged;
|
static jmethodID logger_onEventLogged;
|
||||||
@ -37,19 +37,11 @@ static bool recordBuffer(char* buf, ssize_t len) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, __attribute((unused)) void* reserved) {
|
|
||||||
stdiois_jvm = vm;
|
|
||||||
JNIEnv *env;
|
|
||||||
(*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4);
|
|
||||||
jclass eventLogListener = (*env)->FindClass(env, "net/kdt/pojavlaunch/Logger$eventLogListener");
|
|
||||||
logger_onEventLogged = (*env)->GetMethodID(env, eventLogListener, "onEventLogged", "(Ljava/lang/String;)V");
|
|
||||||
return JNI_VERSION_1_4;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *logger_thread() {
|
static void *logger_thread() {
|
||||||
JNIEnv *env;
|
JNIEnv *env;
|
||||||
jstring writeString;
|
jstring writeString;
|
||||||
(*stdiois_jvm)->AttachCurrentThread(stdiois_jvm, &env, NULL);
|
JavaVM* dvm = pojav_environ->dalvikJavaVMPtr;
|
||||||
|
(*dvm)->AttachCurrentThread(dvm, &env, NULL);
|
||||||
ssize_t rsize;
|
ssize_t rsize;
|
||||||
char buf[2050];
|
char buf[2050];
|
||||||
while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {
|
while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {
|
||||||
@ -64,7 +56,7 @@ static void *logger_thread() {
|
|||||||
(*env)->DeleteLocalRef(env, writeString);
|
(*env)->DeleteLocalRef(env, writeString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(*stdiois_jvm)->DetachCurrentThread(stdiois_jvm);
|
(*dvm)->DetachCurrentThread(dvm);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
JNIEXPORT void JNICALL
|
JNIEXPORT void JNICALL
|
||||||
@ -74,6 +66,10 @@ Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass
|
|||||||
latestlog_fd = -1;
|
latestlog_fd = -1;
|
||||||
close(localfd);
|
close(localfd);
|
||||||
}
|
}
|
||||||
|
if(logger_onEventLogged == NULL) {
|
||||||
|
jclass eventLogListener = (*env)->FindClass(env, "net/kdt/pojavlaunch/Logger$eventLogListener");
|
||||||
|
logger_onEventLogged = (*env)->GetMethodID(env, eventLogListener, "onEventLogged", "(Ljava/lang/String;)V");
|
||||||
|
}
|
||||||
jclass ioeClass = (*env)->FindClass(env, "java/io/IOException");
|
jclass ioeClass = (*env)->FindClass(env, "java/io/IOException");
|
||||||
|
|
||||||
|
|
||||||
@ -109,7 +105,7 @@ Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass
|
|||||||
|
|
||||||
typedef void (*exit_func)(int);
|
typedef void (*exit_func)(int);
|
||||||
|
|
||||||
_Noreturn static void nominal_exit(int code) {
|
_Noreturn void nominal_exit(int code, bool is_signal) {
|
||||||
JNIEnv *env;
|
JNIEnv *env;
|
||||||
jint errorCode = (*exitTrap_jvm)->GetEnv(exitTrap_jvm, (void**)&env, JNI_VERSION_1_6);
|
jint errorCode = (*exitTrap_jvm)->GetEnv(exitTrap_jvm, (void**)&env, JNI_VERSION_1_6);
|
||||||
if(errorCode == JNI_EDETACHED) {
|
if(errorCode == JNI_EDETACHED) {
|
||||||
@ -124,7 +120,7 @@ _Noreturn static void nominal_exit(int code) {
|
|||||||
if(code != 0) {
|
if(code != 0) {
|
||||||
// Exit code 0 is pretty established as "eh it's fine"
|
// Exit code 0 is pretty established as "eh it's fine"
|
||||||
// so only open the GUI if the code is != 0
|
// so only open the GUI if the code is != 0
|
||||||
(*env)->CallStaticVoidMethod(env, exitTrap_exitClass, exitTrap_staticMethod, exitTrap_ctx, code);
|
(*env)->CallStaticVoidMethod(env, exitTrap_exitClass, exitTrap_staticMethod, exitTrap_ctx, code, is_signal);
|
||||||
}
|
}
|
||||||
// Delete the reference, not gonna need 'em later anyway
|
// Delete the reference, not gonna need 'em later anyway
|
||||||
(*env)->DeleteGlobalRef(env, exitTrap_ctx);
|
(*env)->DeleteGlobalRef(env, exitTrap_ctx);
|
||||||
@ -155,7 +151,7 @@ static void custom_exit(int code) {
|
|||||||
}
|
}
|
||||||
exit_tripped = true;
|
exit_tripped = true;
|
||||||
// Perform a nominal exit, as we expect.
|
// Perform a nominal exit, as we expect.
|
||||||
nominal_exit(code);
|
nominal_exit(code, false);
|
||||||
BYTEHOOK_POP_STACK();
|
BYTEHOOK_POP_STACK();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,14 +161,14 @@ static void custom_atexit() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
exit_tripped = true;
|
exit_tripped = true;
|
||||||
nominal_exit(0);
|
nominal_exit(0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_utils_JREUtils_setupExitTrap(JNIEnv *env, __attribute((unused)) jclass clazz, jobject context) {
|
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_utils_JREUtils_setupExitTrap(JNIEnv *env, __attribute((unused)) jclass clazz, jobject context) {
|
||||||
exitTrap_ctx = (*env)->NewGlobalRef(env,context);
|
exitTrap_ctx = (*env)->NewGlobalRef(env,context);
|
||||||
(*env)->GetJavaVM(env,&exitTrap_jvm);
|
(*env)->GetJavaVM(env,&exitTrap_jvm);
|
||||||
exitTrap_exitClass = (*env)->NewGlobalRef(env,(*env)->FindClass(env,"net/kdt/pojavlaunch/ExitActivity"));
|
exitTrap_exitClass = (*env)->NewGlobalRef(env,(*env)->FindClass(env,"net/kdt/pojavlaunch/ExitActivity"));
|
||||||
exitTrap_staticMethod = (*env)->GetStaticMethodID(env,exitTrap_exitClass,"showExitMessage","(Landroid/content/Context;I)V");
|
exitTrap_staticMethod = (*env)->GetStaticMethodID(env,exitTrap_exitClass,"showExitMessage","(Landroid/content/Context;IZ)V");
|
||||||
|
|
||||||
if(bytehook_init(BYTEHOOK_MODE_AUTOMATIC, false) == BYTEHOOK_STATUS_CODE_OK) {
|
if(bytehook_init(BYTEHOOK_MODE_AUTOMATIC, false) == BYTEHOOK_STATUS_CODE_OK) {
|
||||||
bytehook_hook_all(NULL,
|
bytehook_hook_all(NULL,
|
||||||
|
@ -93,6 +93,7 @@
|
|||||||
|
|
||||||
<!-- MainActivity: strings -->
|
<!-- MainActivity: strings -->
|
||||||
<string name="mcn_exit_title">Application/Game exited with code %d, check latestlog.txt for more details.</string>
|
<string name="mcn_exit_title">Application/Game exited with code %d, check latestlog.txt for more details.</string>
|
||||||
|
<string name="mcn_signal_title">Application/Game aborted by fatal signal %d, check latestlog.txt for more details.</string>
|
||||||
<string name="mcn_exit_confirm">Are you sure want to force close?</string>
|
<string name="mcn_exit_confirm">Are you sure want to force close?</string>
|
||||||
<string name="mcn_check_fail_vulkan_support">Zink (Vulkan) renderer is not supported on this device!</string>
|
<string name="mcn_check_fail_vulkan_support">Zink (Vulkan) renderer is not supported on this device!</string>
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user