Get android running its sandbox in a seprate, isolated service process. So that we support not extracting the native code from the APK, so that we support distributing as an .aab file, so that we may one day release on the app store.
This commit is contained in:
parent
71268636df
commit
ed6bef6d24
@ -20,5 +20,10 @@
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service
|
||||
android:name=".TildeFriendsSandboxService"
|
||||
android:exported="false"
|
||||
android:isolatedProcess="true"
|
||||
android:process=":sandbox"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
@ -3,15 +3,18 @@ package com.unprompted.tildefriends;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.StrictMode;
|
||||
import android.os.SystemClock;
|
||||
import android.os.strictmode.Violation;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
@ -29,19 +32,13 @@ import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.InputStream;
|
||||
import java.lang.Process;
|
||||
import java.lang.Thread;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
@ -51,12 +48,13 @@ import java.nio.file.WatchService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class TildeFriendsActivity extends Activity {
|
||||
static TildeFriendsActivity s_activity;
|
||||
TildeFriendsWebView web_view;
|
||||
String base_url;
|
||||
String port_file_path;
|
||||
Process process;
|
||||
Thread thread;
|
||||
Thread server_thread;
|
||||
ServiceConnection service_connection;
|
||||
|
||||
private ValueCallback<Uri[]> upload_message;
|
||||
private final static int FILECHOOSER_RESULT = 1;
|
||||
@ -68,10 +66,12 @@ public class TildeFriendsActivity extends Activity {
|
||||
Log.w("tildefriends", "system.loadLibrary() completed.");
|
||||
}
|
||||
|
||||
static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path);
|
||||
public static native int tf_server_main(String files_dir, String apk_path, String out_port_file_path);
|
||||
public static native int tf_sandbox_main(int pipe_fd);
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
s_activity = this;
|
||||
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
|
||||
.detectLeakedClosableObjects()
|
||||
.penaltyLog()
|
||||
@ -274,13 +274,8 @@ public class TildeFriendsActivity extends Activity {
|
||||
@Override
|
||||
protected void onDestroy()
|
||||
{
|
||||
if (process != null) {
|
||||
Log.w("tildefriends", "Killing process.");
|
||||
process.destroyForcibly();
|
||||
Log.w("tildefriends", "Process killed.");
|
||||
process = null;
|
||||
}
|
||||
super.onDestroy();
|
||||
s_activity = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -382,4 +377,49 @@ public class TildeFriendsActivity extends Activity {
|
||||
web_view.setVisibility(View.VISIBLE);
|
||||
text_view.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public static void start_sandbox(int pipe_fd) {
|
||||
Log.w("tildefriends", "starting service with fd: " + pipe_fd);
|
||||
Intent intent = new Intent(s_activity, TildeFriendsSandboxService.class);
|
||||
s_activity.service_connection = new ServiceConnection() {
|
||||
@Override
|
||||
public void onBindingDied(ComponentName name) {
|
||||
Log.w("tildefriends", "onBindingDied");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNullBinding(ComponentName name) {
|
||||
Log.w("tildefriends", "onNullBinding");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder binder) {
|
||||
Log.w("tildefriends", "onServiceConnected");
|
||||
Parcel data = Parcel.obtain();
|
||||
ParcelFileDescriptor pfd = ParcelFileDescriptor.adoptFd(pipe_fd);
|
||||
data.writeParcelable(pfd, 0);
|
||||
try {
|
||||
binder.transact(TildeFriendsSandboxService.START_CALL, data, null, IBinder.FLAG_ONEWAY);
|
||||
} catch (RemoteException e) {
|
||||
Log.w("tildefriends", "RemoteException");
|
||||
} finally {
|
||||
data.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected(ComponentName name) {
|
||||
Log.w("tildefriends", "onServiceDisconnected");
|
||||
}
|
||||
};
|
||||
s_activity.bindService(intent, s_activity.service_connection, BIND_AUTO_CREATE);
|
||||
}
|
||||
|
||||
public static void stop_sandbox() {
|
||||
Log.w("tildefriends", "stop_sandbox");
|
||||
if (s_activity.service_connection != null) {
|
||||
s_activity.unbindService(s_activity.service_connection);
|
||||
s_activity.service_connection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
package com.unprompted.tildefriends;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
|
||||
public class TildeFriendsSandboxService extends Service {
|
||||
public static final int START_CALL = IBinder.FIRST_CALL_TRANSACTION;
|
||||
|
||||
Thread thread;
|
||||
|
||||
public int onStartCommand(Intent intent, int flags, int start_id) {
|
||||
Log.w("tildefriends", "TildeFriendsSandboxService: onStartCommand");
|
||||
return super.onStartCommand(intent, flags, start_id);
|
||||
}
|
||||
|
||||
public void onDestroy() {
|
||||
Log.w("tildefriends", "TildeFriendsSandboxService: onDestroy");
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
private void start_thread(int pipe_fd) {
|
||||
thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Log.w("tildefriends", "Calling tf_sandbox_main.");
|
||||
int result = TildeFriendsActivity.tf_sandbox_main(pipe_fd);
|
||||
Log.w("tildefriends", "tf_sandbox_main returned " + result + ".");
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return new Binder() {
|
||||
@Override
|
||||
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) {
|
||||
if (code == START_CALL) {
|
||||
ParcelFileDescriptor pfd = data.readParcelable(ParcelFileDescriptor.class.getClassLoader(), ParcelFileDescriptor.class);
|
||||
if (pfd != null) {
|
||||
Log.w("tildefriends", "fd is " + pfd.getFd());
|
||||
start_thread(pfd.detachFd());
|
||||
try {
|
||||
pfd.close();
|
||||
} catch (java.io.IOException e) {
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package com.unprompted.tildefriends;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
|
||||
public class TildeFriendsWebView extends android.webkit.WebView {
|
||||
boolean overscrolledY = false;
|
||||
|
71
src/main.c
71
src/main.c
@ -607,14 +607,16 @@ static int _tf_command_run(const char* file, int argc, char* argv[])
|
||||
static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
{
|
||||
bool show_usage = false;
|
||||
int fd = STDIN_FILENO;
|
||||
|
||||
while (!show_usage)
|
||||
{
|
||||
static const struct option k_options[] = {
|
||||
{ "fd", required_argument, NULL, 'f' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ 0 },
|
||||
};
|
||||
int c = getopt_long(argc, argv, "h", k_options, NULL);
|
||||
int c = getopt_long(argc, argv, "f:h", k_options, NULL);
|
||||
if (c == -1)
|
||||
{
|
||||
break;
|
||||
@ -626,6 +628,10 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
default:
|
||||
show_usage = true;
|
||||
break;
|
||||
case 'f':
|
||||
tf_printf("got -f %s\n", optarg);
|
||||
fd = atoi(optarg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -634,6 +640,7 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
tf_printf("\nUsage: %s sandbox [options]\n\n", file);
|
||||
tf_printf("options:\n");
|
||||
tf_printf(" -h, --help Show this usage information.\n");
|
||||
tf_printf(" -f, --fd File descriptor with which to communicate with parent process.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -641,7 +648,7 @@ static int _tf_command_sandbox(const char* file, int argc, char* argv[])
|
||||
prctl(PR_SET_PDEATHSIG, SIGHUP);
|
||||
#endif
|
||||
tf_task_t* task = tf_task_create();
|
||||
tf_task_configure_from_fd(task, STDIN_FILENO);
|
||||
tf_task_configure_from_fd(task, fd);
|
||||
_shed_privileges();
|
||||
/* The caller will trigger tf_task_activate with a message. */
|
||||
tf_task_run(task);
|
||||
@ -689,10 +696,6 @@ static void _startup(int argc, char* argv[])
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
setenv("UV_USE_IO_URING", "0", 1);
|
||||
#endif
|
||||
|
||||
tf_mem_startup(tracking);
|
||||
g_backtrace_state = backtrace_create_state(argv[0], 0, _backtrace_error, NULL);
|
||||
|
||||
@ -720,7 +723,7 @@ static void _startup(int argc, char* argv[])
|
||||
{
|
||||
if (
|
||||
#if !defined(_WIN32)
|
||||
signal(SIGSYS, _error_handler) == SIG_ERR ||
|
||||
signal(SIGSYS, _error_handler) == SIG_ERR || signal(SIGABRT, _error_handler) == SIG_ERR ||
|
||||
#endif
|
||||
signal(SIGSEGV, _error_handler) == SIG_ERR)
|
||||
{
|
||||
@ -730,8 +733,28 @@ static void _startup(int argc, char* argv[])
|
||||
}
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
static JNIEnv* s_jni_env;
|
||||
|
||||
static void _tf_service_start(int pipe_fd)
|
||||
{
|
||||
tf_printf("_tf_service_start\n");
|
||||
jclass c = (*s_jni_env)->FindClass(s_jni_env, "com/unprompted/tildefriends/TildeFriendsActivity");
|
||||
jmethodID start_sandbox = (*s_jni_env)->GetStaticMethodID(s_jni_env, c, "start_sandbox", "(I)V");
|
||||
(*s_jni_env)->CallStaticVoidMethod(s_jni_env, c, start_sandbox, pipe_fd);
|
||||
}
|
||||
|
||||
static void _tf_service_stop()
|
||||
{
|
||||
tf_printf("_tf_service_stop\n");
|
||||
jclass c = (*s_jni_env)->FindClass(s_jni_env, "com/unprompted/tildefriends/TildeFriendsActivity");
|
||||
jmethodID stop_sandbox = (*s_jni_env)->GetStaticMethodID(s_jni_env, c, "stop_sandbox", "()V");
|
||||
(*s_jni_env)->CallStaticVoidMethod(s_jni_env, c, stop_sandbox);
|
||||
}
|
||||
|
||||
static jint _tf_server_main(JNIEnv* env, jobject this_object, jstring files_dir, jstring apk_path, jstring out_port_file_path)
|
||||
{
|
||||
s_jni_env = env;
|
||||
|
||||
tf_printf("This is tf_server_main main.\n");
|
||||
_startup(0, (char*[]) { NULL });
|
||||
tf_printf("That was startup.\n");
|
||||
@ -754,24 +777,51 @@ static jint _tf_server_main(JNIEnv* env, jobject this_object, jstring files_dir,
|
||||
char* port_file_arg = alloca(port_file_arg_length);
|
||||
snprintf(port_file_arg, port_file_arg_length, "out_http_port_file=%s", out_port_file);
|
||||
|
||||
const char* args[] =
|
||||
{
|
||||
const char* args[] = {
|
||||
"run",
|
||||
"-z",
|
||||
apk,
|
||||
"-a",
|
||||
port_file_arg,
|
||||
"-p",
|
||||
"0",
|
||||
"-o", /* HACK! FIXME! */
|
||||
};
|
||||
|
||||
tf_task_set_android_service_callbacks(_tf_service_start, _tf_service_stop);
|
||||
result = _tf_command_run(apk, _countof(args), (char**)args);
|
||||
tf_task_set_android_service_callbacks(NULL, NULL);
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, files_dir, files);
|
||||
(*env)->ReleaseStringUTFChars(env, apk_path, apk);
|
||||
(*env)->ReleaseStringUTFChars(env, out_port_file_path, out_port_file);
|
||||
|
||||
tf_printf("tf_server_main finished with %d.", result);
|
||||
|
||||
s_jni_env = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
static jint _tf_sandbox_main(JNIEnv* env, jobject this_object, int pipe_fd)
|
||||
{
|
||||
s_jni_env = env;
|
||||
|
||||
tf_printf("This is tf_sandbox_main main (fd=%d).\n", pipe_fd);
|
||||
_startup(0, (char*[]) { NULL });
|
||||
tf_printf("That was startup.\n");
|
||||
|
||||
char fd[32] = { 0 };
|
||||
snprintf(fd, sizeof(fd), "%d", pipe_fd);
|
||||
const char* args[] = {
|
||||
"sandbox",
|
||||
"-f",
|
||||
fd,
|
||||
};
|
||||
|
||||
int result = _tf_command_sandbox(NULL, _countof(args), (char**)args);
|
||||
|
||||
tf_printf("tf_sandbox_main finished with %d.", result);
|
||||
|
||||
s_jni_env = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -796,6 +846,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||
tf_printf("Registering method.\n");
|
||||
static const JNINativeMethod methods[] = {
|
||||
{ "tf_server_main", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", _tf_server_main },
|
||||
{ "tf_sandbox_main", "(I)I", _tf_sandbox_main },
|
||||
};
|
||||
int result = (*env)->RegisterNatives(env, c, methods, (int)_countof(methods));
|
||||
if (result != JNI_OK)
|
||||
|
19
src/task.c
19
src/task.c
@ -50,6 +50,9 @@
|
||||
static JSClassID _import_class_id;
|
||||
static int _count;
|
||||
|
||||
static tf_android_start_service_t* s_android_start_service;
|
||||
static tf_android_stop_service_t* s_android_stop_service;
|
||||
|
||||
extern struct backtrace_state* g_backtrace_state;
|
||||
|
||||
typedef struct _export_record_t export_record_t;
|
||||
@ -2160,3 +2163,19 @@ static JSValue _tf_task_pokeSandbox(JSContext* context, JSValueConst this_val, i
|
||||
#endif
|
||||
return JS_NewInt32(context, WEXITSTATUS(result));
|
||||
}
|
||||
|
||||
void tf_task_set_android_service_callbacks(tf_android_start_service_t* start_service, tf_android_stop_service_t* stop_service)
|
||||
{
|
||||
s_android_start_service = start_service;
|
||||
s_android_stop_service = stop_service;
|
||||
}
|
||||
|
||||
tf_android_start_service_t* tf_task_get_android_start_service()
|
||||
{
|
||||
return s_android_start_service;
|
||||
}
|
||||
|
||||
tf_android_stop_service_t* tf_task_get_android_stop_service()
|
||||
{
|
||||
return s_android_stop_service;
|
||||
}
|
||||
|
31
src/task.h
31
src/task.h
@ -333,4 +333,35 @@ char* tf_task_get_debug(tf_task_t* task);
|
||||
*/
|
||||
char* tf_task_get_hitches(tf_task_t* task);
|
||||
|
||||
/**
|
||||
** A callback used to start an Android service.
|
||||
** @param pipe_fd A file descriptor with which to communicate with the invoking
|
||||
** task.
|
||||
*/
|
||||
typedef void(tf_android_start_service_t)(int pipe_fd);
|
||||
|
||||
/**
|
||||
** A callback used to stop an Android service.
|
||||
*/
|
||||
typedef void(tf_android_stop_service_t)();
|
||||
|
||||
/**
|
||||
** Set Android service callbacks.
|
||||
** @param start_service Start service callback.
|
||||
** @param stop_service Stop service callback.
|
||||
*/
|
||||
void tf_task_set_android_service_callbacks(tf_android_start_service_t* start_service, tf_android_stop_service_t* stop_service);
|
||||
|
||||
/**
|
||||
** Get the callback registered for starting an Android service.
|
||||
** @return the callback.
|
||||
*/
|
||||
tf_android_start_service_t* tf_task_get_android_start_service();
|
||||
|
||||
/**
|
||||
** Get the callback registered for stopping an Android service.
|
||||
** @return the callback.
|
||||
*/
|
||||
tf_android_stop_service_t* tf_task_get_android_stop_service();
|
||||
|
||||
/** @} */
|
||||
|
@ -153,6 +153,38 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
|
||||
result = taskObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
tf_android_start_service_t* start_service = tf_task_get_android_start_service();
|
||||
if (start_service)
|
||||
{
|
||||
uv_os_sock_t fds[2];
|
||||
int socketpair_result = uv_socketpair(SOCK_STREAM, 0, fds, 0, 0);
|
||||
if (socketpair_result)
|
||||
{
|
||||
tf_printf("uv_socketpair: %s\n", uv_strerror(socketpair_result));
|
||||
}
|
||||
|
||||
uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream);
|
||||
*pipe = (uv_pipe_t) { 0 };
|
||||
if (uv_pipe_init(tf_task_get_loop(parent), pipe, 1) != 0)
|
||||
{
|
||||
tf_printf("uv_pipe_init failed\n");
|
||||
}
|
||||
|
||||
int pipe_result = uv_pipe_open(pipe, fds[0]);
|
||||
if (pipe_result)
|
||||
{
|
||||
tf_printf("uv_pipe_open: %s\n", uv_strerror(pipe_result));
|
||||
}
|
||||
|
||||
start_service(fds[1]);
|
||||
|
||||
stub->_process.data = stub;
|
||||
tf_packetstream_set_on_receive(stub->_stream, tf_task_on_receive_packet, stub);
|
||||
tf_packetstream_start(stub->_stream);
|
||||
result = taskObject;
|
||||
}
|
||||
else
|
||||
{
|
||||
uv_pipe_t* pipe = tf_packetstream_get_pipe(stub->_stream);
|
||||
memset(pipe, 0, sizeof(*pipe));
|
||||
@ -191,6 +223,7 @@ static JSValue _taskstub_create(JSContext* context, JSValueConst this_val, int a
|
||||
JS_FreeValue(context, taskObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
return JS_DupValue(context, result);
|
||||
}
|
||||
|
||||
@ -442,9 +475,17 @@ JSValue tf_taskstub_kill(tf_taskstub_t* stub)
|
||||
{
|
||||
JSValue result = JS_UNDEFINED;
|
||||
if (!tf_task_get_one_proc(stub->_owner))
|
||||
{
|
||||
tf_android_stop_service_t* stop_service = tf_task_get_android_stop_service();
|
||||
if (stop_service)
|
||||
{
|
||||
stop_service();
|
||||
}
|
||||
else
|
||||
{
|
||||
uv_process_kill(&stub->_process, SIGKILL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
promiseid_t promise = -1;
|
||||
|
Loading…
Reference in New Issue
Block a user