Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/PojavLauncher
Path: blob/v3_openjdk/app_pojavlauncher/src/main/jni/stdio_is.c
2128 views
1
#include <jni.h>
2
#include <sys/types.h>
3
#include <stdbool.h>
4
#include <unistd.h>
5
#include <pthread.h>
6
#include <stdio.h>
7
#include <fcntl.h>
8
#include <string.h>
9
#include <errno.h>
10
#include <stdlib.h>
11
#include <environ/environ.h>
12
13
#include "stdio_is.h"
14
15
//
16
// Created by maks on 17.02.21.
17
//
18
19
static volatile jobject exitTrap_ctx;
20
static volatile jclass exitTrap_exitClass;
21
static volatile jmethodID exitTrap_staticMethod;
22
static JavaVM *exitTrap_jvm;
23
24
static int pfd[2];
25
static pthread_t logger;
26
static jmethodID logger_onEventLogged;
27
static volatile jobject logListener = NULL;
28
static int latestlog_fd = -1;
29
30
31
static bool recordBuffer(char* buf, ssize_t len) {
32
if(strstr(buf, "Session ID is")) return false;
33
if(latestlog_fd != -1) {
34
write(latestlog_fd, buf, len);
35
fdatasync(latestlog_fd);
36
}
37
return true;
38
}
39
40
static void *logger_thread() {
41
JNIEnv *env;
42
jstring writeString;
43
JavaVM* dvm = pojav_environ->dalvikJavaVMPtr;
44
(*dvm)->AttachCurrentThread(dvm, &env, NULL);
45
ssize_t rsize;
46
char buf[2050];
47
while((rsize = read(pfd[0], buf, sizeof(buf)-1)) > 0) {
48
bool shouldRecordString = recordBuffer(buf, rsize); //record with newline int latestlog
49
if(buf[rsize-1]=='\n') {
50
rsize=rsize-1; //truncate
51
}
52
buf[rsize]=0x00;
53
if(shouldRecordString && logListener != NULL) {
54
writeString = (*env)->NewStringUTF(env, buf); //send to app without newline
55
(*env)->CallVoidMethod(env, logListener, logger_onEventLogged, writeString);
56
(*env)->DeleteLocalRef(env, writeString);
57
}
58
}
59
(*dvm)->DetachCurrentThread(dvm);
60
return NULL;
61
}
62
JNIEXPORT void JNICALL
63
Java_net_kdt_pojavlaunch_Logger_begin(JNIEnv *env, __attribute((unused)) jclass clazz, jstring logPath) {
64
if(latestlog_fd != -1) {
65
int localfd = latestlog_fd;
66
latestlog_fd = -1;
67
close(localfd);
68
}
69
if(logger_onEventLogged == NULL) {
70
jclass eventLogListener = (*env)->FindClass(env, "net/kdt/pojavlaunch/Logger$eventLogListener");
71
logger_onEventLogged = (*env)->GetMethodID(env, eventLogListener, "onEventLogged", "(Ljava/lang/String;)V");
72
}
73
jclass ioeClass = (*env)->FindClass(env, "java/io/IOException");
74
75
76
setvbuf(stdout, 0, _IOLBF, 0); // make stdout line-buffered
77
setvbuf(stderr, 0, _IONBF, 0); // make stderr unbuffered
78
79
/* create the pipe and redirect stdout and stderr */
80
pipe(pfd);
81
dup2(pfd[1], 1);
82
dup2(pfd[1], 2);
83
84
/* open latestlog.txt for writing */
85
const char* logFilePath = (*env)->GetStringUTFChars(env, logPath, NULL);
86
latestlog_fd = open(logFilePath, O_WRONLY | O_TRUNC);
87
if(latestlog_fd == -1) {
88
latestlog_fd = 0;
89
(*env)->ThrowNew(env, ioeClass, strerror(errno));
90
return;
91
}
92
(*env)->ReleaseStringUTFChars(env, logPath, logFilePath);
93
94
/* spawn the logging thread */
95
int result = pthread_create(&logger, 0, logger_thread, 0);
96
if(result != 0) {
97
close(latestlog_fd);
98
(*env)->ThrowNew(env, ioeClass, strerror(result));
99
}
100
pthread_detach(logger);
101
}
102
103
_Noreturn void nominal_exit(int code, bool is_signal) {
104
JNIEnv *env;
105
jint errorCode = (*exitTrap_jvm)->GetEnv(exitTrap_jvm, (void**)&env, JNI_VERSION_1_6);
106
if(errorCode == JNI_EDETACHED) {
107
errorCode = (*exitTrap_jvm)->AttachCurrentThread(exitTrap_jvm, &env, NULL);
108
}
109
if(errorCode != JNI_OK) {
110
// Step on a landmine and die, since we can't invoke the Dalvik exit without attaching to
111
// Dalvik.
112
// I mean, if Zygote can do that, why can't I?
113
killpg(getpgrp(), SIGTERM);
114
}
115
if(code != 0) {
116
// Exit code 0 is pretty established as "eh it's fine"
117
// so only open the GUI if the code is != 0
118
(*env)->CallStaticVoidMethod(env, exitTrap_exitClass, exitTrap_staticMethod, exitTrap_ctx, code, is_signal);
119
}
120
// Delete the reference, not gonna need 'em later anyway
121
(*env)->DeleteGlobalRef(env, exitTrap_ctx);
122
(*env)->DeleteGlobalRef(env, exitTrap_exitClass);
123
124
// A hat trick, if you will
125
// Call the Android System.exit() to perform Android's shutdown hooks and do a
126
// fully clean exit.
127
// After doing this, either of these will happen:
128
// 1. Runtime calls exit() for real and it will be handled by ByteHook's recurse handler
129
// and redirected back to the OS
130
// 2. Zygote sends SIGTERM (no handling necessary, the process perishes)
131
// 3. A different thread calls exit() and the hook will go through the exit_tripped path
132
jclass systemClass = (*env)->FindClass(env,"java/lang/System");
133
jmethodID exitMethod = (*env)->GetStaticMethodID(env, systemClass, "exit", "(I)V");
134
(*env)->CallStaticVoidMethod(env, systemClass, exitMethod, 0);
135
// System.exit() should not ever return, but the compiler doesn't know about that
136
// so put a while loop here
137
while(1) {}
138
}
139
140
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_Logger_appendToLog(JNIEnv *env, __attribute((unused)) jclass clazz, jstring text) {
141
jsize appendStringLength = (*env)->GetStringUTFLength(env, text);
142
char newChars[appendStringLength+2];
143
(*env)->GetStringUTFRegion(env, text, 0, (*env)->GetStringLength(env, text), newChars);
144
newChars[appendStringLength] = '\n';
145
newChars[appendStringLength+1] = 0;
146
if(recordBuffer(newChars, appendStringLength+1) && logListener != NULL) {
147
(*env)->CallVoidMethod(env, logListener, logger_onEventLogged, text);
148
}
149
}
150
151
JNIEXPORT void JNICALL
152
Java_net_kdt_pojavlaunch_Logger_setLogListener(JNIEnv *env, __attribute((unused)) jclass clazz, jobject log_listener) {
153
jobject logListenerLocal = logListener;
154
if(log_listener == NULL) {
155
logListener = NULL;
156
}else{
157
logListener = (*env)->NewGlobalRef(env, log_listener);
158
}
159
if(logListenerLocal != NULL) (*env)->DeleteGlobalRef(env, logListenerLocal);
160
}
161
162
163
JNIEXPORT void JNICALL
164
Java_net_kdt_pojavlaunch_utils_JREUtils_setupExitMethod(JNIEnv *env, jclass clazz,
165
jobject context) {
166
exitTrap_ctx = (*env)->NewGlobalRef(env,context);
167
(*env)->GetJavaVM(env,&exitTrap_jvm);
168
exitTrap_exitClass = (*env)->NewGlobalRef(env,(*env)->FindClass(env,"net/kdt/pojavlaunch/ExitActivity"));
169
exitTrap_staticMethod = (*env)->GetStaticMethodID(env,exitTrap_exitClass,"showExitMessage","(Landroid/content/Context;IZ)V");
170
}
171