Merge "Fix bug trying to access wrong method over JNI & support multiple services in arg getter"
diff --git a/runtimes/google/ipc/jni/arg_getter.go b/runtimes/google/ipc/jni/arg_getter.go
index 7540bd3..fc6bf3b 100644
--- a/runtimes/google/ipc/jni/arg_getter.go
+++ b/runtimes/google/ipc/jni/arg_getter.go
@@ -24,8 +24,8 @@
 	registerInterface((*proximity.ProximityAnnouncerService)(nil))
 }
 
-// A list of all registered argGetter-s.
-var register map[string]*argGetter = make(map[string]*argGetter)
+// A list of all registered serviceArgGetter-s.
+var register map[string]*serviceArgGetter = make(map[string]*serviceArgGetter)
 
 // registerInterface registers the provided VDL client or server interface
 // so that its methods' arguments can be created on-the-fly.
@@ -81,7 +81,7 @@
 		methods[m.Name] = append(methods[m.Name], &mArgs)
 	}
 	path := path.Join(t.PkgPath(), t.Name())
-	register[path] = &argGetter{
+	register[path] = &serviceArgGetter{
 		methods: methods,
 		vdlPath: path,
 	}
@@ -115,19 +115,47 @@
 	return nil
 }
 
-// newArgGetter returns the argument getter for the provided VDL interface.
-func newArgGetter(vdlIfacePath string) *argGetter {
-	return register[vdlIfacePath]
-}
-
-// argGetter serves method arguments for a specific interface.
-type argGetter struct {
+// serviceArgGetter serves method arguments for a specific service.
+type serviceArgGetter struct {
 	methods map[string][]*methodArgs
 	vdlPath string
 }
 
+func (sag *serviceArgGetter) String() (ret string) {
+	ret = "VDLPath: " + sag.vdlPath
+	for k, v := range sag.methods {
+		for _, m := range v {
+			ret += "; "
+			ret += fmt.Sprintf("Method: %s, Args: %v", k, m)
+		}
+	}
+	return
+}
+
+// argGetter serves method arguments for a service object.
+// (which may implement multiple services)
+type argGetter struct {
+	methods map[string][]*methodArgs
+}
+
+// newArgGetter returns the argument getter for the provided service object.
+func newArgGetter(paths []string) (*argGetter, error) {
+	ag := &argGetter{
+		methods: make(map[string][]*methodArgs),
+	}
+	for _, path := range paths {
+		sag := register[path]
+		if sag == nil {
+			return nil, fmt.Errorf("unknown service %s", path)
+		}
+		for method, args := range sag.methods {
+			ag.methods[method] = args
+		}
+	}
+	return ag, nil
+}
+
 func (ag *argGetter) String() (ret string) {
-	ret = "VDLPath: " + ag.vdlPath
 	for k, v := range ag.methods {
 		for _, m := range v {
 			ret += "; "
@@ -154,9 +182,6 @@
 	return m
 }
 
-// argGetters is a cache of created argument getters, keyed by VDL interface path.
-var argGetters map[string]*argGetter = make(map[string]*argGetter)
-
 // method contains argument type information for a method belonging to an interface.
 type methodArgs struct {
 	inTypes           []reflect.Type
diff --git a/runtimes/google/ipc/jni/client.go b/runtimes/google/ipc/jni/client.go
index 58b446c..c55adff 100644
--- a/runtimes/google/ipc/jni/client.go
+++ b/runtimes/google/ipc/jni/client.go
@@ -38,13 +38,13 @@
 	}
 	// Get argument instances that correspond to the provided method.
 	vdlPackagePath := strings.Join(strings.Split(goString(env, jPath), ".")[1:], "/")
-	getter := newArgGetter(vdlPackagePath)
-	if getter == nil {
-		return nil, fmt.Errorf("couldn't find VDL interface corresponding to path %q", vdlPackagePath)
+	getter, err := newArgGetter([]string{vdlPackagePath})
+	if err != nil {
+		return nil, err
 	}
 	mArgs := getter.FindMethod(method, len(argStrs))
 	if mArgs == nil {
-		return nil, fmt.Errorf("couldn't find method %s with %d args in VDL interface at path %q, getter: %v", method, len(argStrs), goString(env, jPath), getter)
+		return nil, fmt.Errorf("couldn't find method %s with %d args in VDL interface at path %q", method, len(argStrs), goString(env, jPath))
 	}
 	argptrs := mArgs.InPtrs()
 	if len(argptrs) != len(argStrs) {
diff --git a/runtimes/google/ipc/jni/invoker.go b/runtimes/google/ipc/jni/invoker.go
index 12d8c57..5835d86 100644
--- a/runtimes/google/ipc/jni/invoker.go
+++ b/runtimes/google/ipc/jni/invoker.go
@@ -6,7 +6,6 @@
 	"encoding/json"
 	"fmt"
 	"runtime"
-	"strings"
 
 	"veyron2/ipc"
 	"veyron2/security"
@@ -42,12 +41,12 @@
 		return nil, fmt.Errorf("error creating Java VDLInvoker object: %v", err)
 	}
 	// Fetch the argGetter for the object.
-	pid := jMethodID(env, jVDLInvokerClass, "getInterfacePath", fmt.Sprintf("()%s", stringSign))
-	jPath := C.jstring(C.CallGetInterfacePath(env, jInvoker, pid))
-	vdlPackagePath := strings.Join(strings.Split(goString(env, jPath), ".")[1:], "/")
-	getter := newArgGetter(vdlPackagePath)
-	if getter == nil {
-		return nil, fmt.Errorf("couldn't find VDL interface corresponding to path %q", vdlPackagePath)
+	pid := jMethodID(env, jVDLInvokerClass, "getImplementedServices", fmt.Sprintf("()%s", arraySign(stringSign)))
+	jPathArray := C.jobjectArray(C.CallGetInterfacePath(env, jInvoker, pid))
+	paths := goStringArray(env, jPathArray)
+	getter, err := newArgGetter(paths)
+	if err != nil {
+		return nil, err
 	}
 	// Reference Java invoker; it will be de-referenced when the go invoker
 	// created below is garbage-collected (through the finalizer callback we
diff --git a/runtimes/google/ipc/jni/util.go b/runtimes/google/ipc/jni/util.go
index b122926..d87f3bd 100644
--- a/runtimes/google/ipc/jni/util.go
+++ b/runtimes/google/ipc/jni/util.go
@@ -35,6 +35,10 @@
 	objectSign = "Ljava/lang/Object;"
 )
 
+func arraySign(sign string) string {
+	return "[" + sign
+}
+
 // refs stores references to instances of various Go types, namely instances
 // that are referenced only by the Java code.  The only purpose of this store
 // is to prevent Go runtime from garbage collecting those instances.
@@ -96,6 +100,19 @@
 	return C.NewStringUTF(env, cString)
 }
 
+// goStringArray converts a Java string array to a go string array.
+func goStringArray(env *C.JNIEnv, jStrArray C.jobjectArray) []string {
+	if jStrArray == nil {
+		return nil
+	}
+	length := C.GetArrayLength(env, C.jarray(jStrArray))
+	ret := make([]string, int(length))
+	for i := 0; i < int(length); i++ {
+		ret[i] = goString(env, C.jstring(C.GetObjectArrayElement(env, jStrArray, C.jsize(i))))
+	}
+	return ret
+}
+
 // jThrow throws a new Java exception of the provided type with the given message.
 func jThrow(env *C.JNIEnv, class C.jclass, msg string) {
 	s := C.CString(msg)
@@ -165,15 +182,7 @@
 	defer C.free(unsafe.Pointer(cSig))
 	fid := C.GetFieldID(env, C.GetObjectClass(env, obj), cField, cSig)
 	jStrArray := C.jobjectArray(C.GetObjectField(env, obj, fid))
-	if jStrArray == nil {
-		return nil
-	}
-	length := C.GetArrayLength(env, C.jarray(jStrArray))
-	ret := make([]string, int(length))
-	for i := 0; i < int(length); i++ {
-		ret[i] = goString(env, C.jstring(C.GetObjectArrayElement(env, jStrArray, C.jsize(i))))
-	}
-	return ret
+	return goStringArray(env, jStrArray)
 }
 
 // jMethodID returns the Java method ID for the given method.