github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/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  // current_vm is stored to initialize other cgo packages.
    12  //
    13  // As all the Go packages in a program form a single shared library,
    14  // there can only be one JNI_OnLoad function for initialization. In
    15  // OpenJDK there is JNI_GetCreatedJavaVMs, but this is not available
    16  // on android.
    17  JavaVM* current_vm;
    18  
    19  // current_ctx is Android's android.context.Context. May be NULL.
    20  jobject current_ctx;
    21  
    22  char* lockJNI(uintptr_t* envp, int* attachedp) {
    23  	JNIEnv* env;
    24  
    25  	if (current_vm == NULL) {
    26  		return "no current JVM";
    27  	}
    28  
    29  	*attachedp = 0;
    30  	switch ((*current_vm)->GetEnv(current_vm, (void**)&env, JNI_VERSION_1_6)) {
    31  	case JNI_OK:
    32  		break;
    33  	case JNI_EDETACHED:
    34  		if ((*current_vm)->AttachCurrentThread(current_vm, &env, 0) != 0) {
    35  			return "cannot attach to JVM";
    36  		}
    37  		*attachedp = 1;
    38  		break;
    39  	case JNI_EVERSION:
    40  		return "bad JNI version";
    41  	default:
    42  		return "unknown JNI error from GetEnv";
    43  	}
    44  
    45  	*envp = (uintptr_t)env;
    46  	return NULL;
    47  }
    48  
    49  char* checkException(uintptr_t jnienv) {
    50  	jthrowable exc;
    51  	JNIEnv* env = (JNIEnv*)jnienv;
    52  
    53  	if (!(*env)->ExceptionCheck(env)) {
    54  		return NULL;
    55  	}
    56  
    57  	exc = (*env)->ExceptionOccurred(env);
    58  	(*env)->ExceptionClear(env);
    59  
    60  	jclass clazz = (*env)->FindClass(env, "java/lang/Throwable");
    61  	jmethodID toString = (*env)->GetMethodID(env, clazz, "toString", "()Ljava/lang/String;");
    62  	jobject msgStr = (*env)->CallObjectMethod(env, exc, toString);
    63  	return (char*)(*env)->GetStringUTFChars(env, msgStr, 0);
    64  }
    65  
    66  void unlockJNI() {
    67  	(*current_vm)->DetachCurrentThread(current_vm);
    68  }
    69  */
    70  import "C"
    71  
    72  import (
    73  	"errors"
    74  	"runtime"
    75  	"unsafe"
    76  )
    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, ctx unsafe.Pointer) {
    82  	C.current_vm = (*C.JavaVM)(vm)
    83  	C.current_ctx = (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(&env, &attached); errStr != nil {
   103  			errch <- errors.New(C.GoString(errStr))
   104  			return
   105  		}
   106  		if attached != 0 {
   107  			defer C.unlockJNI()
   108  		}
   109  
   110  		vm := uintptr(unsafe.Pointer(C.current_vm))
   111  		if err := fn(vm, uintptr(env), uintptr(C.current_ctx)); 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  }