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  }