blob: e7e2df7a5a81c6d2e16fe423566f9f5440388002 [file] [log] [blame]
// Copyright 2015 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 discovery
import (
"bytes"
"runtime"
"unsafe"
"v.io/v23/context"
"v.io/x/ref/lib/discovery"
"v.io/x/ref/runtime/factories/android"
jcontext "v.io/x/jni/v23/context"
jutil "v.io/x/jni/util"
)
// #include "jni.h"
import "C"
var (
androidContextSign = jutil.ClassSign("android.content.Context")
contextSign = jutil.ClassSign("io.v.v23.Context")
advertisementSign = jutil.ClassSign("io.v.x.ref.lib.discovery.Advertisement")
uuidSign = jutil.ClassSign("java.util.UUID")
scanHandlerSign = jutil.ClassSign("io.v.impl.google.lib.discovery.ScanHandler")
)
func NewPluginCreator(env jutil.Env, context C.jobject) func(string) (discovery.Plugin, error) {
// Reference Java VPrincipal; it will be de-referenced when the Go Principal
// created below is garbage-collected (through the finalizer callback we
// setup just below).
jContext = jutil.NewGlobalRef(env, context)
return func(host string) (discovery.Plugin, error) {
env, freeFunc := jutil.GetEnv()
defer freeFunc()
jPlugin, err := jutil.NewObject(env, jBlePluginClass, []jutil.Sign{androidContextSign}, context)
jutil.DeleteGlobalRef(env, jContext)
if err != nil {
return nil, err
}
jplugin = jutil.NewGlobalRef(env, jPlugin)
p := &plugin{
trigger: discovery.NewTrigger(),
jPlugin: jPlugin,
}
runtime.SetFinalizer(p, func(p *plugin) {
env, freeFunc := jutil.GetEnv()
defer freeFunc()
jutil.DeleteGlobalRef(env, p.jPlugin)
})
return p, nil
}
}
type plugin struct {
trigger *discovery.Trigger
jPlugin C.jobject
}
// Plugin is the basic interface for a plugin to discovery service.
// All implementation should be goroutine-safe.
type Plugin interface {
// Advertise advertises the advertisement. Advertising will continue until
// the context is canceled or exceeds its deadline. done should be called
// once when advertising is done or canceled.
Advertise(ctx *context.T, ad Advertisement, done func()) error
// Scan scans services that match the service uuid and returns scanned
// advertisements to the channel. A zero-value service uuid means any service.
// Scanning will continue until the context is canceled or exceeds its
// deadline. done should be called once when scanning is done or canceled.
//
// TODO(jhahn): Pass a filter on service attributes.
Scan(ctx *context.T, serviceUuid Uuid, ch chan<- Advertisement, done func()) error
}
func (p *plugin) Advertise(ctx *context.T, ad discovery.Advertisement, done func()) error {
env, freeFunc := jutil.GetEnv()
defer freeFunc()
jContext, err := jcontext.NewJavaContext(env, ctx)
if err != nil {
return err
}
jAdv, err := jutil.JVomCopy(env, ad, jAdvertisementClass)
if err != nil {
return err
}
err := jutil.CallVoidMethod(env, p.jPlugin, "addAdvertisesment", []jutil.Sign{contextSign, advertisementSign}, jContext, jAdv)
p.trigger.Add(done, ctx.Done())
return err
}
func (p *plugin) Scan(ctx *context.T, serviceUuid discovery.Uuid, ch chan<- discovery.Advertisement, done func()) error {
env, freeFunc := jutil.GetEnv()
defer freeFunc()
jContext, err := jcontext.NewJavaContext(env, ctx)
if err != nil {
return err
}
jUuid, err := convertToJavaUUID(env, serviceUuid)
if err != nil {
return err
}
chPtr := &ch
jutil.GoRef(chPtr)
jNativeScanHandler, err := jutil.NewObject(env, jNativeScanHandlerClass, []jutil.Sign{jutil.LongSign}, int64(jutil.PtrValue(chPtr)))
if err != nil {
return err
}
err = jutil.CallVoidMethod(env, p.jPlugin, "addScanner", []jutil.Sign{contextSign, uuidSign, scanHandlerSign},
jContext, jUuid, jNativeScanHandler)
if err != nil {
return err
}
p.trigger.Add(done, ctx.Done())
return nil
}
func convertToJavaUUID(env jutil.Env, uuid discovery.Uuid) (C.jobject, error) {
buf := bytes.NewBuffer(uuid)
var high, low int64
binary.Read(buf, binary.BigEndian, &high)
binary.Read(buf, binary.BigEndian, &low)
jUUID, err := jutil.NewObject(env, jUUIDClass, []jutil.Sign{jutil.LongSign, jutil.LongSign}, high, low)
}