2
0
mirror of synced 2025-02-23 23:08:14 +00:00
mobile/app/android.c
David Crawshaw d77bba0a9f app: call main.main on app startup
There is some cleanup to be done in this package now, but I'm
deferring it until later in the cycle.

We should probably also change the semantics slightly: main should
be called for all-Go apps (that is, apps that use NativeActivity),
but it would be more consistent with buildmode=c-shared and c-archive
if we did not call main for Go shared libraries being included in
Java android apps.

Change-Id: I13ca797a478edb22b0c602c1ee6e616fe4fea1e6
Reviewed-on: https://go-review.googlesource.com/9016
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2015-04-17 16:08:58 +00:00

210 lines
6.4 KiB
C

// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build android
#include <android/asset_manager_jni.h>
#include <android/log.h>
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <jni.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "_cgo_export.h"
#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__)
#define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__)
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
current_vm = vm;
current_ctx = NULL;
current_native_activity = NULL;
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
pthread_mutex_lock(&go_started_mu);
go_started = 0;
pthread_mutex_unlock(&go_started_mu);
pthread_cond_init(&go_started_cond, NULL);
return JNI_VERSION_1_6;
}
static jclass find_class(JNIEnv *env, const char *class_name) {
jclass clazz = (*env)->FindClass(env, class_name);
if (clazz == NULL) {
LOG_FATAL("cannot find %s", class_name);
return NULL;
}
return clazz;
}
static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
jmethodID m = (*env)->GetMethodID(env, clazz, name, sig);
if (m == 0) {
LOG_FATAL("cannot find method %s %s", name, sig);
return 0;
}
return m;
}
static void init_from_context() {
if (current_ctx == NULL) {
return;
}
int attached = 0;
JNIEnv* env;
switch ((*current_vm)->GetEnv(current_vm, (void**)&env, JNI_VERSION_1_6)) {
case JNI_OK:
break;
case JNI_EDETACHED:
if ((*current_vm)->AttachCurrentThread(current_vm, &env, 0) != 0) {
LOG_FATAL("cannot attach JVM");
}
attached = 1;
break;
case JNI_EVERSION:
LOG_FATAL("bad JNI version");
}
// String path = context.getCacheDir().getAbsolutePath();
jclass context_clazz = find_class(env, "android/content/Context");
jmethodID getcachedir = find_method(env, context_clazz, "getCacheDir", "()Ljava/io/File;");
jobject file = (*env)->CallObjectMethod(env, current_ctx, getcachedir, NULL);
jclass file_clazz = find_class(env, "java/io/File");
jmethodID getabsolutepath = find_method(env, file_clazz, "getAbsolutePath", "()Ljava/lang/String;");
jstring jpath = (jstring)(*env)->CallObjectMethod(env, file, getabsolutepath, NULL);
const char* path = (*env)->GetStringUTFChars(env, jpath, NULL);
if (setenv("TMPDIR", path, 1) != 0) {
LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", path, errno);
}
(*env)->ReleaseStringUTFChars(env, jpath, path);
if (attached) {
(*current_vm)->DetachCurrentThread(current_vm);
}
}
// has_prefix_key returns 1 if s starts with prefix.
static int has_prefix(const char *s, const char* prefix) {
while (*prefix) {
if (*prefix++ != *s++)
return 0;
}
return 1;
}
// getenv_raw searches environ for name prefix and returns the string pair.
// For example, getenv_raw("PATH=") returns "PATH=/bin".
// If no entry is found, the name prefix is returned. For example "PATH=".
static const char* getenv_raw(const char *name) {
extern char** environ;
char** env = environ;
for (env = environ; *env; env++) {
if (has_prefix(*env, name)) {
return *env;
}
}
return name;
}
static void* init_go_runtime(void* unused) {
init_from_context();
uintptr_t mainPC = (uintptr_t)dlsym(RTLD_DEFAULT, "main.main");
if (!mainPC) {
LOG_FATAL("missing main.main");
}
callMain(mainPC);
}
static void wait_go_runtime() {
pthread_mutex_lock(&go_started_mu);
while (go_started == 0) {
pthread_cond_wait(&go_started_cond, &go_started_mu);
}
pthread_mutex_unlock(&go_started_mu);
LOG_INFO("runtime started");
}
pthread_t nativeactivity_t;
// Runtime entry point when embedding Go in other libraries.
void InitGoRuntime() {
pthread_mutex_lock(&go_started_mu);
go_started = 0;
pthread_mutex_unlock(&go_started_mu);
pthread_cond_init(&go_started_cond, NULL);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&nativeactivity_t, NULL, init_go_runtime, NULL);
wait_go_runtime();
}
// Runtime entry point when using NativeActivity.
void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_t savedStateSize) {
// Note that activity->clazz is mis-named.
current_vm = activity->vm;
current_ctx = (*activity->env)->NewGlobalRef(activity->env, activity->clazz);
current_native_activity = activity;
InitGoRuntime();
// These functions match the methods on Activity, described at
// http://developer.android.com/reference/android/app/Activity.html
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowResized = onNativeWindowResized;
activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNeeded;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
// TODO(crawshaw): Type mismatch for onContentRectChanged.
//activity->callbacks->onContentRectChanged = onContentRectChanged;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
onCreate(activity);
}
// Runtime entry point when embedding Go in a Java App.
JNIEXPORT void JNICALL
Java_go_Go_run(JNIEnv* env, jclass clazz, jobject ctx) {
current_ctx = (*env)->NewGlobalRef(env, ctx);
if (current_ctx != NULL) {
// Init asset_manager.
jclass context_clazz = find_class(env, "android/content/Context");
jmethodID getassets = find_method(
env, context_clazz, "getAssets", "()Landroid/content/res/AssetManager;");
// Prevent the java AssetManager from being GC'd
jobject asset_manager_ref = (*env)->NewGlobalRef(
env, (*env)->CallObjectMethod(env, current_ctx, getassets));
asset_manager = AAssetManager_fromJava(env, asset_manager_ref);
}
init_go_runtime(NULL);
}
// Used by Java initialization code to know when it can use cgocall.
JNIEXPORT void JNICALL
Java_go_Go_waitForRun(JNIEnv* env, jclass clazz) {
wait_go_runtime();
}