github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/bind/java/seq_android.go (about)

     1  // Copyright 2014 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 java // import "github.com/c-darwin/mobile/bind/java"
     6  
     7  //#cgo LDFLAGS: -llog
     8  //#include <android/log.h>
     9  //#include <jni.h>
    10  //#include <stdint.h>
    11  //#include <string.h>
    12  //#include "seq_android.h"
    13  import "C"
    14  import (
    15  	"fmt"
    16  	"sync"
    17  	"unsafe"
    18  
    19  	"github.com/c-darwin/mobile/bind/seq"
    20  	"github.com/c-darwin/mobile/internal/mobileinit"
    21  )
    22  
    23  const maxSliceLen = 1<<31 - 1
    24  
    25  const debug = false
    26  
    27  // Send is called by Java to send a request to run a Go function.
    28  //export Send
    29  func Send(descriptor string, code int, req *C.uint8_t, reqlen C.size_t, res **C.uint8_t, reslen *C.size_t) {
    30  	fn := seq.Registry[descriptor][code]
    31  	if fn == nil {
    32  		panic(fmt.Sprintf("invalid descriptor(%s) and code(0x%x)", descriptor, code))
    33  	}
    34  	in := new(seq.Buffer)
    35  	if reqlen > 0 {
    36  		in.Data = (*[maxSliceLen]byte)(unsafe.Pointer(req))[:reqlen]
    37  	}
    38  	out := new(seq.Buffer)
    39  	fn(out, in)
    40  	// BUG(hyangah): the function returning a go byte slice (so fn writes a pointer into 'out') is unsafe.
    41  	// After fn is complete here, Go runtime is free to collect or move the pointed byte slice
    42  	// contents. (Explicitly calling runtime.GC here will surface the problem?)
    43  	// Without pinning support from Go side, it will be hard to fix it without extra copying.
    44  
    45  	seqToBuf(res, reslen, out)
    46  }
    47  
    48  // DestroyRef is called by Java to inform Go it is done with a reference.
    49  //export DestroyRef
    50  func DestroyRef(refnum C.int32_t) {
    51  	seq.Delete(int32(refnum))
    52  }
    53  
    54  type request struct {
    55  	ref    *seq.Ref
    56  	handle int32
    57  	code   int
    58  	in     *seq.Buffer
    59  }
    60  
    61  var recv struct {
    62  	sync.Mutex
    63  	cond sync.Cond // signals req is not empty
    64  	req  []request
    65  	next int32 // next handle value
    66  }
    67  
    68  var res struct {
    69  	sync.Mutex
    70  	cond sync.Cond             // signals a response is filled in
    71  	out  map[int32]*seq.Buffer // handle -> output
    72  }
    73  
    74  func init() {
    75  	recv.cond.L = &recv.Mutex
    76  	recv.next = 411 // arbitrary starting point distinct from Go and Java obj ref nums
    77  
    78  	res.cond.L = &res.Mutex
    79  	res.out = make(map[int32]*seq.Buffer)
    80  }
    81  
    82  func seqToBuf(bufptr **C.uint8_t, lenptr *C.size_t, buf *seq.Buffer) {
    83  	if debug {
    84  		fmt.Printf("seqToBuf tag 1, len(buf.Data)=%d, *lenptr=%d\n", len(buf.Data), *lenptr)
    85  	}
    86  	if len(buf.Data) == 0 {
    87  		*lenptr = 0
    88  		return
    89  	}
    90  	if len(buf.Data) > int(*lenptr) {
    91  		// TODO(crawshaw): realloc
    92  		C.free(unsafe.Pointer(*bufptr))
    93  		m := C.malloc(C.size_t(len(buf.Data)))
    94  		if uintptr(m) == 0 {
    95  			panic(fmt.Sprintf("malloc failed, size=%d", len(buf.Data)))
    96  		}
    97  		*bufptr = (*C.uint8_t)(m)
    98  		*lenptr = C.size_t(len(buf.Data))
    99  	}
   100  	C.memcpy(unsafe.Pointer(*bufptr), unsafe.Pointer(&buf.Data[0]), C.size_t(len(buf.Data)))
   101  }
   102  
   103  // Recv is called by Java in a loop and blocks until Go requests a callback
   104  // be executed by the JVM. Then a request object is returned, along with a
   105  // handle for the host to respond via RecvRes.
   106  //export Recv
   107  func Recv(in **C.uint8_t, inlen *C.size_t) (ref, code, handle C.int32_t) {
   108  	recv.Lock()
   109  	for len(recv.req) == 0 {
   110  		recv.cond.Wait()
   111  	}
   112  	req := recv.req[0]
   113  	recv.req = recv.req[1:]
   114  	seqToBuf(in, inlen, req.in)
   115  	recv.Unlock()
   116  
   117  	return C.int32_t(req.ref.Num), C.int32_t(req.code), C.int32_t(req.handle)
   118  }
   119  
   120  // RecvRes is called by JNI to return the result of a requested callback.
   121  //export RecvRes
   122  func RecvRes(handle C.int32_t, out *C.uint8_t, outlen C.size_t) {
   123  	outBuf := &seq.Buffer{
   124  		Data: make([]byte, outlen),
   125  	}
   126  	copy(outBuf.Data, (*[maxSliceLen]byte)(unsafe.Pointer(out))[:outlen])
   127  
   128  	res.Lock()
   129  	res.out[int32(handle)] = outBuf
   130  	res.Unlock()
   131  	res.cond.Broadcast()
   132  }
   133  
   134  // transact calls a method on a Java object instance.
   135  // It blocks until the call is complete.
   136  func transact(ref *seq.Ref, _ string, code int, in *seq.Buffer) *seq.Buffer {
   137  	recv.Lock()
   138  	if recv.next == 1<<31-1 {
   139  		panic("recv handle overflow")
   140  	}
   141  	handle := recv.next
   142  	recv.next++
   143  	recv.req = append(recv.req, request{
   144  		ref:    ref,
   145  		code:   code,
   146  		in:     in,
   147  		handle: handle,
   148  	})
   149  	recv.Unlock()
   150  	recv.cond.Signal()
   151  
   152  	res.Lock()
   153  	for res.out[handle] == nil {
   154  		res.cond.Wait()
   155  	}
   156  	out := res.out[handle]
   157  	delete(res.out, handle)
   158  	res.Unlock()
   159  
   160  	return out
   161  }
   162  
   163  func encodeString(out *seq.Buffer, v string) {
   164  	out.WriteUTF16(v)
   165  }
   166  
   167  func decodeString(in *seq.Buffer) string {
   168  	return in.ReadUTF16()
   169  }
   170  
   171  func init() {
   172  	seq.FinalizeRef = func(ref *seq.Ref) {
   173  		if ref.Num < 0 {
   174  			panic(fmt.Sprintf("not a Java ref: %d", ref.Num))
   175  		}
   176  		transact(ref, "", -1, new(seq.Buffer))
   177  	}
   178  
   179  	seq.Transact = transact
   180  	seq.EncString = encodeString
   181  	seq.DecString = decodeString
   182  }
   183  
   184  //export setContext
   185  func setContext(vm *C.JavaVM, ctx C.jobject) {
   186  	mobileinit.SetCurrentContext(unsafe.Pointer(vm), unsafe.Pointer(ctx))
   187  }