Skip to content

Commit

Permalink
Feat[launcher]: user facing dialog when exiting with a signal
Browse files Browse the repository at this point in the history
  • Loading branch information
artdeell committed Sep 30, 2024
1 parent 917e9b3 commit a9ebb9c
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;

Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,5 @@ public static int getDetectedVersion() {
static {
System.loadLibrary("pojavexec");
System.loadLibrary("pojavexec_awt");
System.loadLibrary("istdio");
}
}
14 changes: 4 additions & 10 deletions app_pojavlauncher/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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 := \
Expand All @@ -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)
Expand All @@ -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 := \
Expand Down
58 changes: 58 additions & 0 deletions app_pojavlauncher/src/main/jni/jre_launcher.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
Expand Down
30 changes: 13 additions & 17 deletions app_pojavlauncher/src/main/jni/stdio_is.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <errno.h>
#include <stdlib.h>
#include <bytehook.h>
#include <environ/environ.h>

//
// Created by maks on 17.02.21.
Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -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
Expand All @@ -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");


Expand Down Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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();
}

Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions app_pojavlauncher/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down

0 comments on commit a9ebb9c

Please sign in to comment.