mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-18 00:59:35 -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.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
@ -20,22 +19,26 @@ public class ExitActivity extends AppCompatActivity {
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
int code = -1;
|
||||
int code = -1; boolean isSignal = false;
|
||||
Bundle extras = getIntent().getExtras();
|
||||
if(extras != null) {
|
||||
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)
|
||||
.setMessage(getString(R.string.mcn_exit_title,code))
|
||||
.setMessage(getString(message,code))
|
||||
.setPositiveButton(R.string.main_share_logs, (dialog, which) -> shareLog(this))
|
||||
.setOnDismissListener(dialog -> ExitActivity.this.finish())
|
||||
.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);
|
||||
i.putExtra("code",code);
|
||||
i.putExtra("isSignal", isSignal);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ctx.startActivity(i);
|
||||
|
@ -572,6 +572,5 @@ public class JREUtils {
|
||||
static {
|
||||
System.loadLibrary("pojavexec");
|
||||
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
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
$(call import-module,prefab/bytehook)
|
||||
LOCAL_PATH := $(HERE_PATH)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
# Link GLESv2 for test
|
||||
LOCAL_LDLIBS := -ldl -llog -landroid
|
||||
# -lGLESv2
|
||||
LOCAL_MODULE := pojavexec
|
||||
LOCAL_SHARED_LIBRARIES := bytehook
|
||||
# LOCAL_CFLAGS += -DDEBUG
|
||||
# -DGLES_TEST
|
||||
LOCAL_SRC_FILES := \
|
||||
@ -40,6 +43,7 @@ LOCAL_SRC_FILES := \
|
||||
input_bridge_v3.c \
|
||||
jre_launcher.c \
|
||||
utils.c \
|
||||
stdio_is.c \
|
||||
driver_helper/nsbypass.c
|
||||
|
||||
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
|
||||
@ -56,16 +60,6 @@ LOCAL_LDFLAGS := -z global
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
#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)
|
||||
LOCAL_MODULE := pojavexec_awt
|
||||
LOCAL_SRC_FILES := \
|
||||
|
@ -65,6 +65,62 @@ typedef jint JLI_Launch_func(int argc, char ** argv, /* main argc, argc */
|
||||
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) {
|
||||
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;
|
||||
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
|
||||
// LOGD("JLI lib = %x", (int)libjli);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <bytehook.h>
|
||||
#include <environ/environ.h>
|
||||
|
||||
//
|
||||
// Created by maks on 17.02.21.
|
||||
@ -19,7 +20,6 @@ static volatile jclass exitTrap_exitClass;
|
||||
static volatile jmethodID exitTrap_staticMethod;
|
||||
static JavaVM *exitTrap_jvm;
|
||||
|
||||
static JavaVM *stdiois_jvm;
|
||||
static int pfd[2];
|
||||
static pthread_t logger;
|
||||
static jmethodID logger_onEventLogged;
|
||||
@ -37,19 +37,11 @@ static bool recordBuffer(char* buf, ssize_t len) {
|
||||
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() {
|
||||
JNIEnv *env;
|
||||
jstring writeString;
|
||||
(*stdiois_jvm)->AttachCurrentThread(stdiois_jvm, &env, NULL);
|
||||
JavaVM* dvm = pojav_environ->dalvikJavaVMPtr;
|
||||
(*dvm)->AttachCurrentThread(dvm, &env, NULL);
|
||||
ssize_t rsize;
|
||||
char buf[2050];
|
||||
while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {
|
||||
@ -64,7 +56,7 @@ static void *logger_thread() {
|
||||
(*env)->DeleteLocalRef(env, writeString);
|
||||
}
|
||||
}
|
||||
(*stdiois_jvm)->DetachCurrentThread(stdiois_jvm);
|
||||
(*dvm)->DetachCurrentThread(dvm);
|
||||
return NULL;
|
||||
}
|
||||
JNIEXPORT void JNICALL
|
||||
@ -74,6 +66,10 @@ Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass
|
||||
latestlog_fd = -1;
|
||||
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");
|
||||
|
||||
|
||||
@ -109,7 +105,7 @@ Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass
|
||||
|
||||
typedef void (*exit_func)(int);
|
||||
|
||||
_Noreturn static void nominal_exit(int code) {
|
||||
_Noreturn void nominal_exit(int code, bool is_signal) {
|
||||
JNIEnv *env;
|
||||
jint errorCode = (*exitTrap_jvm)->GetEnv(exitTrap_jvm, (void**)&env, JNI_VERSION_1_6);
|
||||
if(errorCode == JNI_EDETACHED) {
|
||||
@ -124,7 +120,7 @@ _Noreturn static void nominal_exit(int code) {
|
||||
if(code != 0) {
|
||||
// Exit code 0 is pretty established as "eh it's fine"
|
||||
// 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
|
||||
(*env)->DeleteGlobalRef(env, exitTrap_ctx);
|
||||
@ -155,7 +151,7 @@ static void custom_exit(int code) {
|
||||
}
|
||||
exit_tripped = true;
|
||||
// Perform a nominal exit, as we expect.
|
||||
nominal_exit(code);
|
||||
nominal_exit(code, false);
|
||||
BYTEHOOK_POP_STACK();
|
||||
}
|
||||
|
||||
@ -165,14 +161,14 @@ static void custom_atexit() {
|
||||
return;
|
||||
}
|
||||
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) {
|
||||
exitTrap_ctx = (*env)->NewGlobalRef(env,context);
|
||||
(*env)->GetJavaVM(env,&exitTrap_jvm);
|
||||
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) {
|
||||
bytehook_hook_all(NULL,
|
||||
|
@ -93,6 +93,7 @@
|
||||
|
||||
<!-- MainActivity: strings -->
|
||||
<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_check_fail_vulkan_support">Zink (Vulkan) renderer is not supported on this device!</string>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user