Historically, the app package implemented Go runtime initialization. This was convoluted, so the package was used both by all-Go apps (currently based on Android's NativeActivity) and bind-based apps. With Go 1.5 we have -buildmode=c-shared, which does a lot of the work of the old app package. That code was removed a while back, but both all-Go and gobind-based apps still used package app. The intermingled initialization processes led to some strange states. This CL separates gobind-based apps completely from the app package. As part of that users are now expected to use System.loadLibrary themselves. (A future CL may want to make the loadLibrary call part of the .aar generated by gomobile bind.) Delete the libhello example, which has been replaced by gomobile bind, which could do with its own example at some point. Also delete the libhellojni example, which now has nothing to do with the x/mobile repository. Change-Id: I444397f246dbafe81e5c53532eb482c197d26f70 Reviewed-on: https://go-review.googlesource.com/11654 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
181 lines
4.4 KiB
Go
181 lines
4.4 KiB
Go
// Copyright 2014 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package java // import "golang.org/x/mobile/bind/java"
|
|
|
|
//#cgo LDFLAGS: -llog
|
|
//#include <android/log.h>
|
|
//#include <stdint.h>
|
|
//#include <string.h>
|
|
//#include "seq_android.h"
|
|
import "C"
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"unsafe"
|
|
|
|
"golang.org/x/mobile/bind/seq"
|
|
)
|
|
|
|
const maxSliceLen = 1<<31 - 1
|
|
|
|
const debug = false
|
|
|
|
// Send is called by Java to send a request to run a Go function.
|
|
//export Send
|
|
func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) {
|
|
fn := seq.Registry[descriptor][code]
|
|
if fn == nil {
|
|
panic(fmt.Sprintf("invalid descriptor(%s) and code(0x%x)", descriptor, code))
|
|
}
|
|
in := new(seq.Buffer)
|
|
if reqlen > 0 {
|
|
in.Data = (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen]
|
|
}
|
|
out := new(seq.Buffer)
|
|
fn(out, in)
|
|
// BUG(hyangah): the function returning a go byte slice (so fn writes a pointer into 'out') is unsafe.
|
|
// After fn is complete here, Go runtime is free to collect or move the pointed byte slice
|
|
// contents. (Explicitly calling runtime.GC here will surface the problem?)
|
|
// Without pinning support from Go side, it will be hard to fix it without extra copying.
|
|
|
|
seqToBuf(res, reslen, out)
|
|
}
|
|
|
|
// DestroyRef is called by Java to inform Go it is done with a reference.
|
|
//export DestroyRef
|
|
func DestroyRef(refnum C.int32_t) {
|
|
seq.Delete(int32(refnum))
|
|
}
|
|
|
|
type request struct {
|
|
ref *seq.Ref
|
|
handle int32
|
|
code int
|
|
in *seq.Buffer
|
|
}
|
|
|
|
var recv struct {
|
|
sync.Mutex
|
|
cond sync.Cond // signals req is not empty
|
|
req []request
|
|
next int32 // next handle value
|
|
}
|
|
|
|
var res struct {
|
|
sync.Mutex
|
|
cond sync.Cond // signals a response is filled in
|
|
out map[int32]*seq.Buffer // handle -> output
|
|
}
|
|
|
|
func init() {
|
|
recv.cond.L = &recv.Mutex
|
|
recv.next = 411 // arbitrary starting point distinct from Go and Java obj ref nums
|
|
|
|
res.cond.L = &res.Mutex
|
|
res.out = make(map[int32]*seq.Buffer)
|
|
}
|
|
|
|
func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) {
|
|
if debug {
|
|
fmt.Printf("seqToBuf tag 1, len(buf.Data)=%d, *lenptr=%d\n", len(buf.Data), *lenptr)
|
|
}
|
|
if len(buf.Data) == 0 {
|
|
*lenptr = 0
|
|
return
|
|
}
|
|
if len(buf.Data) > int(*lenptr) {
|
|
// TODO(crawshaw): realloc
|
|
C.free(unsafe.Pointer(*bufptr))
|
|
m := C.malloc(C.size_t(len(buf.Data)))
|
|
if uintptr(m) == 0 {
|
|
panic(fmt.Sprintf("malloc failed, size=%d", len(buf.Data)))
|
|
}
|
|
*bufptr = (*C.uint8_t)(m)
|
|
*lenptr = C.size_t(len(buf.Data))
|
|
}
|
|
C.memcpy(unsafe.Pointer(*bufptr), unsafe.Pointer(&buf.Data[0]), C.size_t(len(buf.Data)))
|
|
}
|
|
|
|
// Recv is called by Java in a loop and blocks until Go requests a callback
|
|
// be executed by the JVM. Then a request object is returned, along with a
|
|
// handle for the host to respond via RecvRes.
|
|
//export Recv
|
|
func Recv(in **C.uint8_t, inlen *C.size_t) (ref, code, handle C.int32_t) {
|
|
recv.Lock()
|
|
for len(recv.req) == 0 {
|
|
recv.cond.Wait()
|
|
}
|
|
req := recv.req[0]
|
|
recv.req = recv.req[1:]
|
|
seqToBuf(in, inlen, req.in)
|
|
recv.Unlock()
|
|
|
|
return C.int32_t(req.ref.Num), C.int32_t(req.code), C.int32_t(req.handle)
|
|
}
|
|
|
|
// RecvRes is called by JNI to return the result of a requested callback.
|
|
//export RecvRes
|
|
func RecvRes(handle C.int32_t, out *C.uint8_t, outlen C.size_t) {
|
|
outBuf := &seq.Buffer{
|
|
Data: make([]byte, outlen),
|
|
}
|
|
copy(outBuf.Data, (*[maxSliceLen]byte)(unsafe.Pointer(out))[:outlen])
|
|
|
|
res.Lock()
|
|
res.out[int32(handle)] = outBuf
|
|
res.Unlock()
|
|
res.cond.Broadcast()
|
|
}
|
|
|
|
// transact calls a method on a Java object instance.
|
|
// It blocks until the call is complete.
|
|
func transact(ref *seq.Ref, code int, in *seq.Buffer) *seq.Buffer {
|
|
recv.Lock()
|
|
if recv.next == 1<<31-1 {
|
|
panic("recv handle overflow")
|
|
}
|
|
handle := recv.next
|
|
recv.next++
|
|
recv.req = append(recv.req, request{
|
|
ref: ref,
|
|
code: code,
|
|
in: in,
|
|
handle: handle,
|
|
})
|
|
recv.Unlock()
|
|
recv.cond.Signal()
|
|
|
|
res.Lock()
|
|
for res.out[handle] == nil {
|
|
res.cond.Wait()
|
|
}
|
|
out := res.out[handle]
|
|
delete(res.out, handle)
|
|
res.Unlock()
|
|
|
|
return out
|
|
}
|
|
|
|
func encodeString(out *seq.Buffer, v string) {
|
|
out.WriteUTF16(v)
|
|
}
|
|
|
|
func decodeString(in *seq.Buffer) string {
|
|
return in.ReadUTF16()
|
|
}
|
|
|
|
func init() {
|
|
seq.FinalizeRef = func(ref *seq.Ref) {
|
|
if ref.Num < 0 {
|
|
panic(fmt.Sprintf("not a Java ref: %d", ref.Num))
|
|
}
|
|
transact(ref, -1, new(seq.Buffer))
|
|
}
|
|
|
|
seq.Transact = transact
|
|
seq.EncString = encodeString
|
|
seq.DecString = decodeString
|
|
}
|