| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Go Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style | |
| 3 // license that can be found in the LICENSE file. | |
| 4 | |
| 5 // +build android | |
| 6 | |
| 7 #include <android/asset_manager_jni.h> | |
| 8 #include <android/log.h> | |
| 9 #include <dlfcn.h> | |
| 10 #include <errno.h> | |
| 11 #include <fcntl.h> | |
| 12 #include <jni.h> | |
| 13 #include <pthread.h> | |
| 14 #include <stdlib.h> | |
| 15 #include <stdint.h> | |
| 16 #include <string.h> | |
| 17 #include "_cgo_export.h" | |
| 18 | |
| 19 #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, "Go", __VA_ARGS__) | |
| 20 #define LOG_FATAL(...) __android_log_print(ANDROID_LOG_FATAL, "Go", __VA_ARGS__) | |
| 21 | |
| 22 // Defined in the Go runtime. | |
| 23 static int (*_rt0_arm_linux1)(int argc, char** argv); | |
| 24 | |
| 25 jint JNI_OnLoad(JavaVM* vm, void* reserved) { | |
| 26 current_vm = vm; | |
| 27 current_ctx = NULL; | |
| 28 current_native_activity = NULL; | |
| 29 | |
| 30 JNIEnv* env; | |
| 31 if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) { | |
| 32 return -1; | |
| 33 } | |
| 34 | |
| 35 pthread_mutex_lock(&go_started_mu); | |
| 36 go_started = 0; | |
| 37 pthread_mutex_unlock(&go_started_mu); | |
| 38 pthread_cond_init(&go_started_cond, NULL); | |
| 39 | |
| 40 return JNI_VERSION_1_6; | |
| 41 } | |
| 42 | |
| 43 static jclass find_class(JNIEnv *env, const char *class_name) { | |
| 44 jclass clazz = (*env)->FindClass(env, class_name); | |
| 45 if (clazz == NULL) { | |
| 46 LOG_FATAL("cannot find %s", class_name); | |
| 47 return NULL; | |
| 48 } | |
| 49 return clazz; | |
| 50 } | |
| 51 | |
| 52 static jmethodID find_method(JNIEnv *env, jclass clazz, const char *name, const
char *sig) { | |
| 53 jmethodID m = (*env)->GetMethodID(env, clazz, name, sig); | |
| 54 if (m == 0) { | |
| 55 LOG_FATAL("cannot find method %s %s", name, sig); | |
| 56 return 0; | |
| 57 } | |
| 58 return m; | |
| 59 } | |
| 60 | |
| 61 static void init_from_context() { | |
| 62 if (current_ctx == NULL) { | |
| 63 return; | |
| 64 } | |
| 65 | |
| 66 int attached = 0; | |
| 67 JNIEnv* env; | |
| 68 switch ((*current_vm)->GetEnv(current_vm, (void**)&env, JNI_VERSION_1_6)
) { | |
| 69 case JNI_OK: | |
| 70 break; | |
| 71 case JNI_EDETACHED: | |
| 72 if ((*current_vm)->AttachCurrentThread(current_vm, &env, 0) != 0
) { | |
| 73 LOG_FATAL("cannot attach JVM"); | |
| 74 } | |
| 75 attached = 1; | |
| 76 break; | |
| 77 case JNI_EVERSION: | |
| 78 LOG_FATAL("bad JNI version"); | |
| 79 } | |
| 80 | |
| 81 // String path = context.getCacheDir().getAbsolutePath(); | |
| 82 jclass context_clazz = find_class(env, "android/content/Context"); | |
| 83 jmethodID getcachedir = find_method(env, context_clazz, "getCacheDir", "
()Ljava/io/File;"); | |
| 84 jobject file = (*env)->CallObjectMethod(env, current_ctx, getcachedir, N
ULL); | |
| 85 jclass file_clazz = find_class(env, "java/io/File"); | |
| 86 jmethodID getabsolutepath = find_method(env, file_clazz, "getAbsolutePat
h", "()Ljava/lang/String;"); | |
| 87 jstring jpath = (jstring)(*env)->CallObjectMethod(env, file, getabsolute
path, NULL); | |
| 88 const char* path = (*env)->GetStringUTFChars(env, jpath, NULL); | |
| 89 if (setenv("TMPDIR", path, 1) != 0) { | |
| 90 LOG_INFO("setenv(\"TMPDIR\", \"%s\", 1) failed: %d", path, errno
); | |
| 91 } | |
| 92 (*env)->ReleaseStringUTFChars(env, jpath, path); | |
| 93 | |
| 94 if (attached) { | |
| 95 (*current_vm)->DetachCurrentThread(current_vm); | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 // has_prefix_key returns 1 if s starts with prefix. | |
| 100 static int has_prefix(const char *s, const char* prefix) { | |
| 101 while (*prefix) { | |
| 102 if (*prefix++ != *s++) | |
| 103 return 0; | |
| 104 } | |
| 105 return 1; | |
| 106 } | |
| 107 | |
| 108 // getenv_raw searches environ for name prefix and returns the string pair. | |
| 109 // For example, getenv_raw("PATH=") returns "PATH=/bin". | |
| 110 // If no entry is found, the name prefix is returned. For example "PATH=". | |
| 111 static const char* getenv_raw(const char *name) { | |
| 112 extern char** environ; | |
| 113 char** env = environ; | |
| 114 | |
| 115 for (env = environ; *env; env++) { | |
| 116 if (has_prefix(*env, name)) { | |
| 117 return *env; | |
| 118 } | |
| 119 } | |
| 120 return name; | |
| 121 } | |
| 122 | |
| 123 static void* init_go_runtime(void* unused) { | |
| 124 init_from_context(); | |
| 125 | |
| 126 _rt0_arm_linux1 = (int (*)(int, char**))dlsym(RTLD_DEFAULT, "_rt0_arm_li
nux1"); | |
| 127 if (_rt0_arm_linux1 == NULL) { | |
| 128 LOG_FATAL("missing _rt0_arm_linux1"); | |
| 129 } | |
| 130 | |
| 131 // Defensively heap-allocate argv0, for setenv. | |
| 132 char* argv0 = strdup("gojni"); | |
| 133 | |
| 134 // Build argv, including the ELF auxiliary vector. | |
| 135 struct { | |
| 136 char* argv[2]; | |
| 137 const char* envp[4]; | |
| 138 uint32_t auxv[64]; | |
| 139 } x; | |
| 140 x.argv[0] = argv0; | |
| 141 x.argv[1] = NULL; | |
| 142 x.envp[0] = getenv_raw("TMPDIR="); | |
| 143 x.envp[1] = getenv_raw("PATH="); | |
| 144 x.envp[2] = getenv_raw("LD_LIBRARY_PATH="); | |
| 145 x.envp[3] = NULL; | |
| 146 | |
| 147 build_auxv(x.auxv, sizeof(x.auxv)/sizeof(uint32_t)); | |
| 148 int32_t argc = 1; | |
| 149 _rt0_arm_linux1(argc, x.argv); | |
| 150 return NULL; | |
| 151 } | |
| 152 | |
| 153 static void wait_go_runtime() { | |
| 154 pthread_mutex_lock(&go_started_mu); | |
| 155 while (go_started == 0) { | |
| 156 pthread_cond_wait(&go_started_cond, &go_started_mu); | |
| 157 } | |
| 158 pthread_mutex_unlock(&go_started_mu); | |
| 159 LOG_INFO("runtime started"); | |
| 160 } | |
| 161 | |
| 162 pthread_t nativeactivity_t; | |
| 163 | |
| 164 // Runtime entry point when embedding Go in other libraries. | |
| 165 void InitGoRuntime() { | |
| 166 pthread_mutex_lock(&go_started_mu); | |
| 167 go_started = 0; | |
| 168 pthread_mutex_unlock(&go_started_mu); | |
| 169 pthread_cond_init(&go_started_cond, NULL); | |
| 170 | |
| 171 pthread_attr_t attr; | |
| 172 pthread_attr_init(&attr); | |
| 173 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | |
| 174 pthread_create(&nativeactivity_t, NULL, init_go_runtime, NULL); | |
| 175 wait_go_runtime(); | |
| 176 } | |
| 177 | |
| 178 // Runtime entry point when using NativeActivity. | |
| 179 void ANativeActivity_onCreate(ANativeActivity *activity, void* savedState, size_
t savedStateSize) { | |
| 180 // Note that activity->clazz is mis-named. | |
| 181 current_vm = activity->vm; | |
| 182 current_ctx = (*activity->env)->NewGlobalRef(activity->env, activity->cl
azz); | |
| 183 current_native_activity = activity; | |
| 184 | |
| 185 InitGoRuntime(); | |
| 186 | |
| 187 // These functions match the methods on Activity, described at | |
| 188 // http://developer.android.com/reference/android/app/Activity.html | |
| 189 activity->callbacks->onStart = onStart; | |
| 190 activity->callbacks->onResume = onResume; | |
| 191 activity->callbacks->onSaveInstanceState = onSaveInstanceState; | |
| 192 activity->callbacks->onPause = onPause; | |
| 193 activity->callbacks->onStop = onStop; | |
| 194 activity->callbacks->onDestroy = onDestroy; | |
| 195 activity->callbacks->onWindowFocusChanged = onWindowFocusChanged; | |
| 196 activity->callbacks->onNativeWindowCreated = onNativeWindowCreated; | |
| 197 activity->callbacks->onNativeWindowResized = onNativeWindowResized; | |
| 198 activity->callbacks->onNativeWindowRedrawNeeded = onNativeWindowRedrawNe
eded; | |
| 199 activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed; | |
| 200 activity->callbacks->onInputQueueCreated = onInputQueueCreated; | |
| 201 activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed; | |
| 202 // TODO(crawshaw): Type mismatch for onContentRectChanged. | |
| 203 //activity->callbacks->onContentRectChanged = onContentRectChanged; | |
| 204 activity->callbacks->onConfigurationChanged = onConfigurationChanged; | |
| 205 activity->callbacks->onLowMemory = onLowMemory; | |
| 206 | |
| 207 onCreate(activity); | |
| 208 } | |
| 209 | |
| 210 // Runtime entry point when embedding Go in a Java App. | |
| 211 JNIEXPORT void JNICALL | |
| 212 Java_go_Go_run(JNIEnv* env, jclass clazz, jobject ctx) { | |
| 213 current_ctx = (*env)->NewGlobalRef(env, ctx); | |
| 214 | |
| 215 if (current_ctx != NULL) { | |
| 216 // Init asset_manager. | |
| 217 jclass context_clazz = find_class(env, "android/content/Context"
); | |
| 218 jmethodID getassets = find_method( | |
| 219 env, context_clazz, "getAssets", "()Landroid/content/res
/AssetManager;"); | |
| 220 // Prevent the java AssetManager from being GC'd | |
| 221 jobject asset_manager_ref = (*env)->NewGlobalRef( | |
| 222 env, (*env)->CallObjectMethod(env, current_ctx, getasset
s)); | |
| 223 asset_manager = AAssetManager_fromJava(env, asset_manager_ref); | |
| 224 } | |
| 225 | |
| 226 init_go_runtime(NULL); | |
| 227 } | |
| 228 | |
| 229 // Used by Java initialization code to know when it can use cgocall. | |
| 230 JNIEXPORT void JNICALL | |
| 231 Java_go_Go_waitForRun(JNIEnv* env, jclass clazz) { | |
| 232 wait_go_runtime(); | |
| 233 } | |
| OLD | NEW |