github.com/danbrough/mobile@v0.0.3-beta03/internal/mobileinit/ctx.go (about) 1 // Copyright 2015 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 package mobileinit 6 7 /* 8 #include <jni.h> 9 #include <stdlib.h> 10 #include <stdint.h> 11 12 static char* lockJNI(JavaVM *vm, uintptr_t* envp, int* attachedp) { 13 JNIEnv* env; 14 15 if (vm == NULL) { 16 return "no current JVM"; 17 } 18 19 *attachedp = 0; 20 switch ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6)) { 21 case JNI_OK: 22 break; 23 case JNI_EDETACHED: 24 if ((*vm)->AttachCurrentThread(vm, (void**) &env, 0) != 0) { 25 return "cannot attach to JVM"; 26 } 27 *attachedp = 1; 28 break; 29 case JNI_EVERSION: 30 return "bad JNI version"; 31 default: 32 return "unknown JNI error from GetEnv"; 33 } 34 35 *envp = (uintptr_t)env; 36 return NULL; 37 } 38 39 static char* checkException(uintptr_t jnienv) { 40 jthrowable exc; 41 JNIEnv* env = (JNIEnv*)jnienv; 42 43 if (!(*env)->ExceptionCheck(env)) { 44 return NULL; 45 } 46 47 exc = (*env)->ExceptionOccurred(env); 48 (*env)->ExceptionClear(env); 49 50 jclass clazz = (*env)->FindClass(env, "java/lang/Throwable"); 51 jmethodID toString = (*env)->GetMethodID(env, clazz, "toString", "()Ljava/lang/String;"); 52 jobject msgStr = (*env)->CallObjectMethod(env, exc, toString); 53 return (char*)(*env)->GetStringUTFChars(env, msgStr, 0); 54 } 55 56 static void unlockJNI(JavaVM *vm) { 57 (*vm)->DetachCurrentThread(vm); 58 } 59 */ 60 import "C" 61 62 import ( 63 "errors" 64 "runtime" 65 "unsafe" 66 ) 67 68 // currentVM is stored to initialize other cgo packages. 69 // 70 // As all the Go packages in a program form a single shared library, 71 // there can only be one JNI_OnLoad function for initialization. In 72 // OpenJDK there is JNI_GetCreatedJavaVMs, but this is not available 73 // on android. 74 var currentVM *C.JavaVM 75 76 // currentCtx is Android's android.context.Context. May be NULL. 77 var currentCtx C.jobject 78 79 // SetCurrentContext populates the global Context object with the specified 80 // current JavaVM instance (vm) and android.context.Context object (ctx). 81 // The android.context.Context object must be a global reference. 82 func SetCurrentContext(vm unsafe.Pointer, ctx uintptr) { 83 currentVM = (*C.JavaVM)(vm) 84 currentCtx = (C.jobject)(ctx) 85 } 86 87 // RunOnJVM runs fn on a new goroutine locked to an OS thread with a JNIEnv. 88 // 89 // RunOnJVM blocks until the call to fn is complete. Any Java 90 // exception or failure to attach to the JVM is returned as an error. 91 // 92 // The function fn takes vm, the current JavaVM*, 93 // env, the current JNIEnv*, and 94 // ctx, a jobject representing the global android.context.Context. 95 func RunOnJVM(fn func(vm, env, ctx uintptr) error) error { 96 errch := make(chan error) 97 go func() { 98 runtime.LockOSThread() 99 defer runtime.UnlockOSThread() 100 101 env := C.uintptr_t(0) 102 attached := C.int(0) 103 if errStr := C.lockJNI(currentVM, &env, &attached); errStr != nil { 104 errch <- errors.New(C.GoString(errStr)) 105 return 106 } 107 if attached != 0 { 108 defer C.unlockJNI(currentVM) 109 } 110 111 vm := uintptr(unsafe.Pointer(currentVM)) 112 if err := fn(vm, uintptr(env), uintptr(currentCtx)); err != nil { 113 errch <- err 114 return 115 } 116 117 if exc := C.checkException(env); exc != nil { 118 errch <- errors.New(C.GoString(exc)) 119 C.free(unsafe.Pointer(exc)) 120 return 121 } 122 errch <- nil 123 }() 124 return <-errch 125 }