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  }