blob: 24f8ed4c487bb0b5f497d236f00238465db06dbd [file] [log] [blame]
giolekva313ee2b2021-12-15 15:17:29 +04001// Copyright (c) 2020 Tailscale Inc & 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 jni implements various helper functions for communicating with the Android JVM
6// though JNI.
7package jni
8
9import (
10 "errors"
11 "fmt"
12 "reflect"
13 "runtime"
14 "sync"
15 "unicode/utf16"
16 "unsafe"
17)
18
19/*
20#cgo CFLAGS: -Wall
21
22#include <jni.h>
23#include <stdlib.h>
24
25static jint jni_AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args) {
26 return (*vm)->AttachCurrentThread(vm, p_env, thr_args);
27}
28
29static jint jni_DetachCurrentThread(JavaVM *vm) {
30 return (*vm)->DetachCurrentThread(vm);
31}
32
33static jint jni_GetEnv(JavaVM *vm, JNIEnv **env, jint version) {
34 return (*vm)->GetEnv(vm, (void **)env, version);
35}
36
37static jclass jni_FindClass(JNIEnv *env, const char *name) {
38 return (*env)->FindClass(env, name);
39}
40
41static jthrowable jni_ExceptionOccurred(JNIEnv *env) {
42 return (*env)->ExceptionOccurred(env);
43}
44
45static void jni_ExceptionClear(JNIEnv *env) {
46 (*env)->ExceptionClear(env);
47}
48
49static jclass jni_GetObjectClass(JNIEnv *env, jobject obj) {
50 return (*env)->GetObjectClass(env, obj);
51}
52
53static jmethodID jni_GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
54 return (*env)->GetMethodID(env, clazz, name, sig);
55}
56
57static jmethodID jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) {
58 return (*env)->GetStaticMethodID(env, clazz, name, sig);
59}
60
61static jsize jni_GetStringLength(JNIEnv *env, jstring str) {
62 return (*env)->GetStringLength(env, str);
63}
64
65static const jchar *jni_GetStringChars(JNIEnv *env, jstring str) {
66 return (*env)->GetStringChars(env, str, NULL);
67}
68
69static jstring jni_NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) {
70 return (*env)->NewString(env, unicodeChars, len);
71}
72
73static jboolean jni_IsSameObject(JNIEnv *env, jobject ref1, jobject ref2) {
74 return (*env)->IsSameObject(env, ref1, ref2);
75}
76
77static jobject jni_NewGlobalRef(JNIEnv *env, jobject obj) {
78 return (*env)->NewGlobalRef(env, obj);
79}
80
81static void jni_DeleteGlobalRef(JNIEnv *env, jobject obj) {
82 (*env)->DeleteGlobalRef(env, obj);
83}
84
85static void jni_CallStaticVoidMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
86 (*env)->CallStaticVoidMethodA(env, cls, method, args);
87}
88
89static jint jni_CallStaticIntMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
90 return (*env)->CallStaticIntMethodA(env, cls, method, args);
91}
92
93static jobject jni_CallStaticObjectMethodA(JNIEnv *env, jclass cls, jmethodID method, jvalue *args) {
94 return (*env)->CallStaticObjectMethodA(env, cls, method, args);
95}
96
97static jobject jni_CallObjectMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
98 return (*env)->CallObjectMethodA(env, obj, method, args);
99}
100
101static jboolean jni_CallBooleanMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
102 return (*env)->CallBooleanMethodA(env, obj, method, args);
103}
104
105static jint jni_CallIntMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
106 return (*env)->CallIntMethodA(env, obj, method, args);
107}
108
109static void jni_CallVoidMethodA(JNIEnv *env, jobject obj, jmethodID method, jvalue *args) {
110 (*env)->CallVoidMethodA(env, obj, method, args);
111}
112
113static jbyteArray jni_NewByteArray(JNIEnv *env, jsize length) {
114 return (*env)->NewByteArray(env, length);
115}
116
117static jboolean *jni_GetBooleanArrayElements(JNIEnv *env, jbooleanArray arr) {
118 return (*env)->GetBooleanArrayElements(env, arr, NULL);
119}
120
121static void jni_ReleaseBooleanArrayElements(JNIEnv *env, jbooleanArray arr, jboolean *elems, jint mode) {
122 (*env)->ReleaseBooleanArrayElements(env, arr, elems, mode);
123}
124
125static jbyte *jni_GetByteArrayElements(JNIEnv *env, jbyteArray arr) {
126 return (*env)->GetByteArrayElements(env, arr, NULL);
127}
128
129static jint *jni_GetIntArrayElements(JNIEnv *env, jintArray arr) {
130 return (*env)->GetIntArrayElements(env, arr, NULL);
131}
132
133static void jni_ReleaseIntArrayElements(JNIEnv *env, jintArray arr, jint *elems, jint mode) {
134 (*env)->ReleaseIntArrayElements(env, arr, elems, mode);
135}
136
137static jlong *jni_GetLongArrayElements(JNIEnv *env, jlongArray arr) {
138 return (*env)->GetLongArrayElements(env, arr, NULL);
139}
140
141static void jni_ReleaseLongArrayElements(JNIEnv *env, jlongArray arr, jlong *elems, jint mode) {
142 (*env)->ReleaseLongArrayElements(env, arr, elems, mode);
143}
144
145static void jni_ReleaseByteArrayElements(JNIEnv *env, jbyteArray arr, jbyte *elems, jint mode) {
146 (*env)->ReleaseByteArrayElements(env, arr, elems, mode);
147}
148
149static jsize jni_GetArrayLength(JNIEnv *env, jarray arr) {
150 return (*env)->GetArrayLength(env, arr);
151}
152
153static void jni_DeleteLocalRef(JNIEnv *env, jobject localRef) {
154 return (*env)->DeleteLocalRef(env, localRef);
155}
156
157static jobject jni_GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index) {
158 return (*env)->GetObjectArrayElement(env, array, index);
159}
160
161static jboolean jni_IsInstanceOf(JNIEnv *env, jobject obj, jclass clazz) {
162 return (*env)->IsInstanceOf(env, obj, clazz);
163}
164*/
165import "C"
166
167type JVM C.JavaVM
168
169type Env C.JNIEnv
170
171type (
172 Class C.jclass
173 Object C.jobject
174 MethodID C.jmethodID
175 String C.jstring
176 ByteArray C.jbyteArray
177 ObjectArray C.jobjectArray
178 BooleanArray C.jbooleanArray
179 LongArray C.jlongArray
180 IntArray C.jintArray
181 Boolean C.jboolean
182 Value uint64 // All JNI types fit into 64-bits.
183)
184
185// Cached class handles.
186var classes struct {
187 once sync.Once
188 stringClass, integerClass Class
189
190 integerIntValue MethodID
191}
192
193func env(e *Env) *C.JNIEnv {
194 return (*C.JNIEnv)(unsafe.Pointer(e))
195}
196
197func javavm(vm *JVM) *C.JavaVM {
198 return (*C.JavaVM)(unsafe.Pointer(vm))
199}
200
201// Do invokes a function with a temporary JVM environment. The
202// environment is not valid after the function returns.
203func Do(vm *JVM, f func(env *Env) error) error {
204 runtime.LockOSThread()
205 defer runtime.UnlockOSThread()
206 var env *C.JNIEnv
207 if res := C.jni_GetEnv(javavm(vm), &env, C.JNI_VERSION_1_6); res != C.JNI_OK {
208 if res != C.JNI_EDETACHED {
209 panic(fmt.Errorf("JNI GetEnv failed with error %d", res))
210 }
211 if C.jni_AttachCurrentThread(javavm(vm), &env, nil) != C.JNI_OK {
212 panic(errors.New("runInJVM: AttachCurrentThread failed"))
213 }
214 defer C.jni_DetachCurrentThread(javavm(vm))
215 }
216
217 return f((*Env)(unsafe.Pointer(env)))
218}
219
220func Bool(b bool) Boolean {
221 if b {
222 return C.JNI_TRUE
223 }
224 return C.JNI_FALSE
225}
226
227func varArgs(args []Value) *C.jvalue {
228 if len(args) == 0 {
229 return nil
230 }
231 return (*C.jvalue)(unsafe.Pointer(&args[0]))
232}
233
234func IsSameObject(e *Env, ref1, ref2 Object) bool {
235 same := C.jni_IsSameObject(env(e), C.jobject(ref1), C.jobject(ref2))
236 return same == C.JNI_TRUE
237}
238
239func CallStaticIntMethod(e *Env, cls Class, method MethodID, args ...Value) (int, error) {
240 res := C.jni_CallStaticIntMethodA(env(e), C.jclass(cls), C.jmethodID(method), varArgs(args))
241 return int(res), exception(e)
242}
243
244func CallStaticVoidMethod(e *Env, cls Class, method MethodID, args ...Value) error {
245 C.jni_CallStaticVoidMethodA(env(e), C.jclass(cls), C.jmethodID(method), varArgs(args))
246 return exception(e)
247}
248
249func CallVoidMethod(e *Env, obj Object, method MethodID, args ...Value) error {
250 C.jni_CallVoidMethodA(env(e), C.jobject(obj), C.jmethodID(method), varArgs(args))
251 return exception(e)
252}
253
254func CallStaticObjectMethod(e *Env, cls Class, method MethodID, args ...Value) (Object, error) {
255 res := C.jni_CallStaticObjectMethodA(env(e), C.jclass(cls), C.jmethodID(method), varArgs(args))
256 return Object(res), exception(e)
257}
258
259func CallObjectMethod(e *Env, obj Object, method MethodID, args ...Value) (Object, error) {
260 res := C.jni_CallObjectMethodA(env(e), C.jobject(obj), C.jmethodID(method), varArgs(args))
261 return Object(res), exception(e)
262}
263
264func CallBooleanMethod(e *Env, obj Object, method MethodID, args ...Value) (bool, error) {
265 res := C.jni_CallBooleanMethodA(env(e), C.jobject(obj), C.jmethodID(method), varArgs(args))
266 return res == C.JNI_TRUE, exception(e)
267}
268
269func CallIntMethod(e *Env, obj Object, method MethodID, args ...Value) (int32, error) {
270 res := C.jni_CallIntMethodA(env(e), C.jobject(obj), C.jmethodID(method), varArgs(args))
271 return int32(res), exception(e)
272}
273
274// GetByteArrayElements returns the contents of the byte array.
275func GetByteArrayElements(e *Env, jarr ByteArray) []byte {
276 if jarr == 0 {
277 return nil
278 }
279 size := C.jni_GetArrayLength(env(e), C.jarray(jarr))
280 elems := C.jni_GetByteArrayElements(env(e), C.jbyteArray(jarr))
281 defer C.jni_ReleaseByteArrayElements(env(e), C.jbyteArray(jarr), elems, 0)
282 backing := (*(*[1 << 30]byte)(unsafe.Pointer(elems)))[:size:size]
283 s := make([]byte, len(backing))
284 copy(s, backing)
285 return s
286}
287
288// GetBooleanArrayElements returns the contents of the boolean array.
289func GetBooleanArrayElements(e *Env, jarr BooleanArray) []bool {
290 if jarr == 0 {
291 return nil
292 }
293 size := C.jni_GetArrayLength(env(e), C.jarray(jarr))
294 elems := C.jni_GetBooleanArrayElements(env(e), C.jbooleanArray(jarr))
295 defer C.jni_ReleaseBooleanArrayElements(env(e), C.jbooleanArray(jarr), elems, 0)
296 backing := (*(*[1 << 30]C.jboolean)(unsafe.Pointer(elems)))[:size:size]
297 r := make([]bool, len(backing))
298 for i, b := range backing {
299 r[i] = b == C.JNI_TRUE
300 }
301 return r
302}
303
304// GetStringArrayElements returns the contents of the String array.
305func GetStringArrayElements(e *Env, jarr ObjectArray) []string {
306 var strings []string
307 iterateObjectArray(e, jarr, func(e *Env, idx int, item Object) {
308 s := GoString(e, String(item))
309 strings = append(strings, s)
310 })
311 return strings
312}
313
314// GetIntArrayElements returns the contents of the int array.
315func GetIntArrayElements(e *Env, jarr IntArray) []int {
316 if jarr == 0 {
317 return nil
318 }
319 size := C.jni_GetArrayLength(env(e), C.jarray(jarr))
320 elems := C.jni_GetIntArrayElements(env(e), C.jintArray(jarr))
321 defer C.jni_ReleaseIntArrayElements(env(e), C.jintArray(jarr), elems, 0)
322 backing := (*(*[1 << 27]C.jint)(unsafe.Pointer(elems)))[:size:size]
323 r := make([]int, len(backing))
324 for i, l := range backing {
325 r[i] = int(l)
326 }
327 return r
328}
329
330// GetLongArrayElements returns the contents of the long array.
331func GetLongArrayElements(e *Env, jarr LongArray) []int64 {
332 if jarr == 0 {
333 return nil
334 }
335 size := C.jni_GetArrayLength(env(e), C.jarray(jarr))
336 elems := C.jni_GetLongArrayElements(env(e), C.jlongArray(jarr))
337 defer C.jni_ReleaseLongArrayElements(env(e), C.jlongArray(jarr), elems, 0)
338 backing := (*(*[1 << 27]C.jlong)(unsafe.Pointer(elems)))[:size:size]
339 r := make([]int64, len(backing))
340 for i, l := range backing {
341 r[i] = int64(l)
342 }
343 return r
344}
345
346func iterateObjectArray(e *Env, jarr ObjectArray, f func(e *Env, idx int, item Object)) {
347 if jarr == 0 {
348 return
349 }
350 size := C.jni_GetArrayLength(env(e), C.jarray(jarr))
351 for i := 0; i < int(size); i++ {
352 item := C.jni_GetObjectArrayElement(env(e), C.jobjectArray(jarr), C.jint(i))
353 f(e, i, Object(item))
354 C.jni_DeleteLocalRef(env(e), item)
355 }
356}
357
358// NewByteArray allocates a Java byte array with the content. It
359// panics if the allocation fails.
360func NewByteArray(e *Env, content []byte) ByteArray {
361 jarr := C.jni_NewByteArray(env(e), C.jsize(len(content)))
362 if jarr == 0 {
363 panic(fmt.Errorf("jni: NewByteArray(%d) failed", len(content)))
364 }
365 elems := C.jni_GetByteArrayElements(env(e), jarr)
366 defer C.jni_ReleaseByteArrayElements(env(e), jarr, elems, 0)
367 backing := (*(*[1 << 30]byte)(unsafe.Pointer(elems)))[:len(content):len(content)]
368 copy(backing, content)
369 return ByteArray(jarr)
370}
371
372// ClassLoader returns a reference to the Java ClassLoader associated
373// with obj.
374func ClassLoaderFor(e *Env, obj Object) Object {
375 cls := GetObjectClass(e, obj)
376 getClassLoader := GetMethodID(e, cls, "getClassLoader", "()Ljava/lang/ClassLoader;")
377 clsLoader, err := CallObjectMethod(e, Object(obj), getClassLoader)
378 if err != nil {
379 // Class.getClassLoader should never fail.
380 panic(err)
381 }
382 return Object(clsLoader)
383}
384
385// LoadClass invokes the underlying ClassLoader's loadClass method and
386// returns the class.
387func LoadClass(e *Env, loader Object, class string) (Class, error) {
388 cls := GetObjectClass(e, loader)
389 loadClass := GetMethodID(e, cls, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;")
390 name := JavaString(e, class)
391 loaded, err := CallObjectMethod(e, loader, loadClass, Value(name))
392 if err != nil {
393 return 0, err
394 }
395 return Class(loaded), exception(e)
396}
397
398// exception returns an error corresponding to the pending
399// exception, and clears it. exceptionError returns nil if no
400// exception is pending.
401func exception(e *Env) error {
402 thr := C.jni_ExceptionOccurred(env(e))
403 if thr == 0 {
404 return nil
405 }
406 C.jni_ExceptionClear(env(e))
407 cls := GetObjectClass(e, Object(thr))
408 toString := GetMethodID(e, cls, "toString", "()Ljava/lang/String;")
409 msg, err := CallObjectMethod(e, Object(thr), toString)
410 if err != nil {
411 return err
412 }
413 return errors.New(GoString(e, String(msg)))
414}
415
416// GetObjectClass returns the Java Class for an Object.
417func GetObjectClass(e *Env, obj Object) Class {
418 if obj == 0 {
419 panic("null object")
420 }
421 cls := C.jni_GetObjectClass(env(e), C.jobject(obj))
422 if err := exception(e); err != nil {
423 // GetObjectClass should never fail.
424 panic(err)
425 }
426 return Class(cls)
427}
428
429// GetStaticMethodID returns the id for a static method. It panics if the method
430// wasn't found.
431func GetStaticMethodID(e *Env, cls Class, name, signature string) MethodID {
432 mname := C.CString(name)
433 defer C.free(unsafe.Pointer(mname))
434 msig := C.CString(signature)
435 defer C.free(unsafe.Pointer(msig))
436 m := C.jni_GetStaticMethodID(env(e), C.jclass(cls), mname, msig)
437 if err := exception(e); err != nil {
438 panic(err)
439 }
440 return MethodID(m)
441}
442
443// GetMethodID returns the id for a method. It panics if the method
444// wasn't found.
445func GetMethodID(e *Env, cls Class, name, signature string) MethodID {
446 mname := C.CString(name)
447 defer C.free(unsafe.Pointer(mname))
448 msig := C.CString(signature)
449 defer C.free(unsafe.Pointer(msig))
450 m := C.jni_GetMethodID(env(e), C.jclass(cls), mname, msig)
451 if err := exception(e); err != nil {
452 panic(err)
453 }
454 return MethodID(m)
455}
456
457func NewGlobalRef(e *Env, obj Object) Object {
458 return Object(C.jni_NewGlobalRef(env(e), C.jobject(obj)))
459}
460
461func DeleteGlobalRef(e *Env, obj Object) {
462 C.jni_DeleteGlobalRef(env(e), C.jobject(obj))
463}
464
465// JavaString converts the string to a JVM jstring.
466func JavaString(e *Env, str string) String {
467 if str == "" {
468 return 0
469 }
470 utf16Chars := utf16.Encode([]rune(str))
471 res := C.jni_NewString(env(e), (*C.jchar)(unsafe.Pointer(&utf16Chars[0])), C.int(len(utf16Chars)))
472 return String(res)
473}
474
475// GoString converts the JVM jstring to a Go string.
476func GoString(e *Env, str String) string {
477 if str == 0 {
478 return ""
479 }
480 strlen := C.jni_GetStringLength(env(e), C.jstring(str))
481 chars := C.jni_GetStringChars(env(e), C.jstring(str))
482 var utf16Chars []uint16
483 hdr := (*reflect.SliceHeader)(unsafe.Pointer(&utf16Chars))
484 hdr.Data = uintptr(unsafe.Pointer(chars))
485 hdr.Cap = int(strlen)
486 hdr.Len = int(strlen)
487 utf8 := utf16.Decode(utf16Chars)
488 return string(utf8)
489}