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