discovery: android BLE driver

  Add android BLE driver.

MultiPart: 1/3
Change-Id: I05ef4a2bd5ee8caeda375f9c3ef873222a06d5c9
diff --git a/impl/google/discovery/plugins/ble/driver.go b/impl/google/discovery/plugins/ble/driver.go
new file mode 100644
index 0000000..0424cbf
--- /dev/null
+++ b/impl/google/discovery/plugins/ble/driver.go
@@ -0,0 +1,116 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build android
+
+package ble
+
+import (
+	"runtime"
+
+	"v.io/v23/context"
+
+	"v.io/x/ref/lib/discovery/plugins/ble"
+
+	jutil "v.io/x/jni/util"
+	jcontext "v.io/x/jni/v23/context"
+)
+
+// #include "jni.h"
+import "C"
+
+type driver struct {
+	jDriver jutil.Object
+}
+
+func (d *driver) AddService(uuid string, characteristics map[string][]byte) error {
+	env, freeFunc := jutil.GetEnv()
+	defer freeFunc()
+
+	csObjMap := make(map[jutil.Object]jutil.Object, len(characteristics))
+	for uuid, characteristic := range characteristics {
+		jUuid := jutil.JString(env, uuid)
+		jCharacteristic, err := jutil.JByteArray(env, characteristic)
+		if err != nil {
+			return err
+		}
+		csObjMap[jUuid] = jCharacteristic
+	}
+	jCharacteristics, err := jutil.JObjectMap(env, csObjMap)
+	if err != nil {
+		return err
+	}
+	return jutil.CallVoidMethod(env, d.jDriver, "addService", []jutil.Sign{jutil.StringSign, jutil.MapSign}, uuid, jCharacteristics)
+}
+
+func (d *driver) RemoveService(uuid string) {
+	env, freeFunc := jutil.GetEnv()
+	defer freeFunc()
+
+	jutil.CallVoidMethod(env, d.jDriver, "removeService", []jutil.Sign{jutil.StringSign}, uuid)
+}
+
+func (d *driver) StartScan(uuids []string, baseUuid, maskUuid string, handler ble.ScanHandler) error {
+	env, freeFunc := jutil.GetEnv()
+	defer freeFunc()
+
+	handlerRef := jutil.GoNewRef(&handler) // Un-refed when jNativeScanHandler is finalized.
+	jNativeScanHandler, err := jutil.NewObject(env, jNativeScanHandlerClass, []jutil.Sign{jutil.LongSign}, int64(handlerRef))
+	if err != nil {
+		jutil.GoDecRef(handlerRef)
+		return err
+	}
+	err = jutil.CallVoidMethod(env, d.jDriver, "startScan", []jutil.Sign{jutil.ArraySign(jutil.StringSign), jutil.StringSign, jutil.StringSign, scanHandlerSign}, uuids, baseUuid, maskUuid, jNativeScanHandler)
+	if err != nil {
+		jutil.GoDecRef(handlerRef)
+		return err
+	}
+	return nil
+}
+
+func (d *driver) StopScan() {
+	env, freeFunc := jutil.GetEnv()
+	defer freeFunc()
+
+	jutil.CallVoidMethod(env, d.jDriver, "stopScan", nil)
+}
+
+func (d *driver) DebugString() string {
+	env, freeFunc := jutil.GetEnv()
+	defer freeFunc()
+
+	s, _ := jutil.CallStringMethod(env, d.jDriver, "debugString", nil)
+	return s
+}
+
+func initDriverFactory(env jutil.Env) error {
+	jDriverClass, err := jutil.JFindClass(env, "io/v/android/impl/google/discovery/plugins/ble/Driver")
+	if err != nil {
+		return err
+	}
+	factory := func(ctx *context.T, _ string) (ble.Driver, error) {
+		env, freeFunc := jutil.GetEnv()
+		defer freeFunc()
+
+		jCtx, err := jcontext.JavaContext(env, ctx, nil)
+		if err != nil {
+			return nil, err
+		}
+		jDriver, err := jutil.NewObject(env, jDriverClass, []jutil.Sign{contextSign}, jCtx)
+		if err != nil {
+			return nil, err
+		}
+		// Reference the driver; it will be de-referenced when the driver is garbage-collected.
+		jDriver = jutil.NewGlobalRef(env, jDriver)
+		d := &driver{jDriver}
+		runtime.SetFinalizer(d, func(*driver) {
+			env, freeFunc := jutil.GetEnv()
+			jutil.DeleteGlobalRef(env, d.jDriver)
+			freeFunc()
+		})
+		return d, nil
+	}
+	ble.SetDriverFactory(factory)
+	return nil
+}
diff --git a/impl/google/discovery/plugins/ble/jni.go b/impl/google/discovery/plugins/ble/jni.go
new file mode 100644
index 0000000..f51b424
--- /dev/null
+++ b/impl/google/discovery/plugins/ble/jni.go
@@ -0,0 +1,57 @@
+// Copyright 2016 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build android
+
+package ble
+
+import (
+	"unsafe"
+
+	"v.io/x/ref/lib/discovery/plugins/ble"
+
+	jutil "v.io/x/jni/util"
+)
+
+// #include "jni.h"
+import "C"
+
+var (
+	contextSign     = jutil.ClassSign("io.v.v23.context.VContext")
+	scanHandlerSign = jutil.ClassSign("io.v.android.impl.google.discovery.plugins.ble.Driver$ScanHandler")
+
+	jNativeScanHandlerClass jutil.Class // io.v.android.impl.google.discovery.plugins.ble.NativeScanHandler
+)
+
+func Init(env jutil.Env) error {
+	var err error
+	jNativeScanHandlerClass, err = jutil.JFindClass(env, "io/v/android/impl/google/discovery/plugins/ble/NativeScanHandler")
+	if err != nil {
+		return err
+	}
+	return initDriverFactory(env)
+}
+
+//export Java_io_v_android_impl_google_discovery_plugins_ble_NativeScanHandler_nativeOnDiscovered
+func Java_io_v_android_impl_google_discovery_plugins_ble_NativeScanHandler_nativeOnDiscovered(jenv *C.JNIEnv, _ C.jobject, handlerRef C.jlong, jUuid C.jstring, jCharacteristics C.jobject, jRssi C.jint) {
+	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
+	scanHandler := (*(*ble.ScanHandler)(jutil.GoRefValue(jutil.Ref(handlerRef))))
+	uuid := jutil.GoString(env, jutil.Object(uintptr(unsafe.Pointer(jUuid))))
+	csObjMap, err := jutil.GoObjectMap(env, jutil.Object(uintptr(unsafe.Pointer(jCharacteristics))))
+	if err != nil {
+		jutil.JThrowV(env, err)
+		return
+	}
+	characteristics := make(map[string][]byte, len(csObjMap))
+	for jUuid, jCharacteristic := range csObjMap {
+		characteristics[jutil.GoString(env, jUuid)] = jutil.GoByteArray(env, jCharacteristic)
+	}
+	rssi := int(jRssi)
+	scanHandler.OnDiscovered(uuid, characteristics, rssi)
+}
+
+//export Java_io_v_android_impl_google_discovery_plugins_ble_NativeScanHandler_nativeFinalize
+func Java_io_v_android_impl_google_discovery_plugins_ble_NativeScanHandler_nativeFinalize(_ *C.JNIEnv, _ C.jobject, handlerRef C.jlong) {
+	jutil.GoDecRef(jutil.Ref(handlerRef))
+}
diff --git a/impl/google/discovery/plugins/jni.go b/impl/google/discovery/plugins/jni.go
index 9f171c6..48f6706 100644
--- a/impl/google/discovery/plugins/jni.go
+++ b/impl/google/discovery/plugins/jni.go
@@ -7,55 +7,11 @@
 package plugins
 
 import (
-	"unsafe"
-
-	idiscovery "v.io/x/ref/lib/discovery"
+	"v.io/x/jni/impl/google/discovery/plugins/ble"
 
 	jutil "v.io/x/jni/util"
 )
 
-// #include "jni.h"
-import "C"
-
-var (
-	contextSign     = jutil.ClassSign("io.v.v23.context.VContext")
-	adInfoSign      = jutil.ClassSign("io.v.x.ref.lib.discovery.AdInfo")
-	scanHandlerSign = jutil.ClassSign("io.v.impl.google.lib.discovery.Plugin$ScanHandler")
-
-	jAdInfoClass            jutil.Class // io.v.x.ref.lib.discovery.AdInfo
-	jNativeScanHandlerClass jutil.Class // io.v.android.impl.google.discovery.plugins.NativeScanHandler
-)
-
 func Init(env jutil.Env) error {
-	var err error
-	jAdInfoClass, err = jutil.JFindClass(env, "io/v/x/ref/lib/discovery/AdInfo")
-	if err != nil {
-		return err
-	}
-	jNativeScanHandlerClass, err = jutil.JFindClass(env, "io/v/android/impl/google/discovery/plugins/NativeScanHandler")
-	if err != nil {
-		return err
-	}
-
-	return initPluginFactories(env)
-}
-
-//export Java_io_v_android_impl_google_discovery_plugins_NativeScanHandler_nativeHandleUpdate
-func Java_io_v_android_impl_google_discovery_plugins_NativeScanHandler_nativeHandleUpdate(jenv *C.JNIEnv, _ C.jobject, goRef C.jlong, jAdInfoObj C.jobject) {
-	env := jutil.Env(uintptr(unsafe.Pointer(jenv)))
-	ch := (*(*chan<- *idiscovery.AdInfo)(jutil.GoRefValue(jutil.Ref(goRef))))
-
-	jAdInfo := jutil.Object(uintptr(unsafe.Pointer(jAdInfoObj)))
-
-	var adInfo idiscovery.AdInfo
-	if err := jutil.GoVomCopy(env, jAdInfo, jAdInfoClass, &adInfo); err != nil {
-		jutil.JThrowV(env, err)
-		return
-	}
-	ch <- &adInfo
-}
-
-//export Java_io_v_android_impl_google_discovery_plugins_NativeScanHandler_nativeFinalize
-func Java_io_v_android_impl_google_discovery_plugins_NativeScanHandler_nativeFinalize(jenv *C.JNIEnv, _ C.jobject, goRef C.jlong) {
-	jutil.GoDecRef(jutil.Ref(goRef))
+	return ble.Init(env)
 }
diff --git a/impl/google/discovery/plugins/plugin.go b/impl/google/discovery/plugins/plugin.go
deleted file mode 100644
index a4b9609..0000000
--- a/impl/google/discovery/plugins/plugin.go
+++ /dev/null
@@ -1,131 +0,0 @@
-// Copyright 2016 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build android
-
-package plugins
-
-import (
-	"runtime"
-
-	"v.io/v23/context"
-
-	idiscovery "v.io/x/ref/lib/discovery"
-	dfactory "v.io/x/ref/lib/discovery/factory"
-
-	jutil "v.io/x/jni/util"
-	jcontext "v.io/x/jni/v23/context"
-)
-
-// #include "jni.h"
-import "C"
-
-type plugin struct {
-	jPlugin jutil.Object
-	trigger *idiscovery.Trigger
-}
-
-func (p *plugin) Advertise(ctx *context.T, adinfo *idiscovery.AdInfo, done func()) error {
-	env, freeFunc := jutil.GetEnv()
-	defer freeFunc()
-
-	jAdInfo, err := jutil.JVomCopy(env, adinfo, jAdInfoClass)
-	if err != nil {
-		done()
-		return err
-	}
-	err = jutil.CallVoidMethod(env, p.jPlugin, "startAdvertising", []jutil.Sign{adInfoSign}, jAdInfo)
-	if err != nil {
-		done()
-		return err
-	}
-
-	jAdInfo = jutil.NewGlobalRef(env, jAdInfo)
-	stop := func() {
-		env, freeFunc := jutil.GetEnv()
-		defer freeFunc()
-
-		jutil.CallVoidMethod(env, p.jPlugin, "stopAdvertising", []jutil.Sign{adInfoSign}, jAdInfo)
-		jutil.DeleteGlobalRef(env, jAdInfo)
-		done()
-	}
-	p.trigger.Add(stop, ctx.Done())
-	return nil
-}
-
-func (p *plugin) Scan(ctx *context.T, interfaceName string, ch chan<- *idiscovery.AdInfo, done func()) error {
-	env, freeFunc := jutil.GetEnv()
-	defer freeFunc()
-
-	chRef := jutil.GoNewRef(&ch) // Un-refed when jNativeScanHandler is finalized.
-	jNativeScanHandler, err := jutil.NewObject(env, jNativeScanHandlerClass, []jutil.Sign{jutil.LongSign}, int64(chRef))
-	if err != nil {
-		jutil.GoDecRef(chRef)
-		done()
-		return err
-	}
-	err = jutil.CallVoidMethod(env, p.jPlugin, "startScan", []jutil.Sign{jutil.StringSign, scanHandlerSign}, interfaceName, jNativeScanHandler)
-	if err != nil {
-		done()
-		return err
-	}
-
-	jNativeScanHandler = jutil.NewGlobalRef(env, jNativeScanHandler)
-	stop := func() {
-		env, freeFunc := jutil.GetEnv()
-		defer freeFunc()
-
-		jutil.CallVoidMethod(env, p.jPlugin, "stopScan", []jutil.Sign{scanHandlerSign}, jNativeScanHandler)
-		jutil.DeleteGlobalRef(env, jNativeScanHandler)
-		done()
-	}
-	p.trigger.Add(stop, ctx.Done())
-	return nil
-}
-
-func (p *plugin) Close() {
-	env, freeFunc := jutil.GetEnv()
-	defer freeFunc()
-
-	jutil.CallVoidMethod(env, p.jPlugin, "close", nil)
-}
-
-func newPluginFactory(env jutil.Env, jPluginClass jutil.Class) func(*context.T, string) (idiscovery.Plugin, error) {
-	return func(ctx *context.T, host string) (idiscovery.Plugin, error) {
-		env, freeFunc := jutil.GetEnv()
-		defer freeFunc()
-
-		// We pass the global context of the android vanadium runtime, since the context of the discovery
-		// factory does not have an Android context.
-		jCtx, err := jcontext.JavaContext(env, ctx, nil)
-		if err != nil {
-			return nil, err
-		}
-		jPlugin, err := jutil.NewObject(env, jPluginClass, []jutil.Sign{contextSign, jutil.StringSign}, jCtx, host)
-		if err != nil {
-			return nil, err
-		}
-		// Reference Plugin; it will be de-referenced when the plugin is garbage-collected.
-		jPlugin = jutil.NewGlobalRef(env, jPlugin)
-		p := &plugin{
-			jPlugin: jPlugin,
-			trigger: idiscovery.NewTrigger(),
-		}
-		runtime.SetFinalizer(p, func(*plugin) {
-			env, freeFunc := jutil.GetEnv()
-			jutil.DeleteGlobalRef(env, p.jPlugin)
-			freeFunc()
-		})
-		return p, nil
-	}
-}
-
-func initPluginFactories(env jutil.Env) error {
-	jPluginClass, err := jutil.JFindClass(env, "io/v/android/impl/google/discovery/plugins/ble/BlePlugin")
-	if err != nil {
-		return err
-	}
-	dfactory.SetPluginFactory("ble", newPluginFactory(env, jPluginClass))
-	return nil
-}