x/ref: syncbase cgo bridge ListenForAppPeers and ListenForInvites

We use the syncbase/discovery implementations of ListenForAppPeers
and ListenForInvites to surface them in the cgo bridge.

The next steps are to plumb this through in jni (and Swift).

Note: Untested. All I can tell is that it compiles. Other than doing
the JNI + high-level implementation, how can I check?

Note2: I've been hooking this up through JNI and high-level Java.
It compiles there and has approximately the correct format.

Change-Id: I00c6ff045073cc7c3d9b32769cf3f2858b7bd0f3
diff --git a/services/syncbase/bridge/cgo/impl.go b/services/syncbase/bridge/cgo/impl.go
index 2d2faf5..b7c232c 100644
--- a/services/syncbase/bridge/cgo/impl.go
+++ b/services/syncbase/bridge/cgo/impl.go
@@ -46,6 +46,8 @@
 	"v.io/v23/vom"
 	_ "v.io/x/ref/runtime/factories/roaming"
 	"v.io/x/ref/services/syncbase/bridge"
+	"v.io/x/ref/services/syncbase/bridge/cgo/refmap"
+	"v.io/x/ref/services/syncbase/discovery"
 	"v.io/x/ref/services/syncbase/syncbaselib"
 	"v.io/x/ref/test/testutil"
 )
@@ -66,6 +68,20 @@
 static void CallCollectionScanCallbacksOnDone(v23_syncbase_CollectionScanCallbacks cbs, v23_syncbase_VError err) {
   cbs.onDone(cbs.handle, err);
 }
+
+static void CallDbSyncgroupInvitesOnInvite(v23_syncbase_DbSyncgroupInvitesCallbacks cbs, v23_syncbase_Invite i) {
+  cbs.onInvite(cbs.handle, i);
+}
+static void CallDbSyncgroupInvitesOnDone(v23_syncbase_DbSyncgroupInvitesCallbacks cbs) {
+  cbs.onDone(cbs.handle);
+}
+
+static void CallNeighborhoodScanCallbacksOnPeer(v23_syncbase_NeighborhoodScanCallbacks cbs, v23_syncbase_AppPeer p) {
+  cbs.onPeer(cbs.handle, p);
+}
+static void CallNeighborhoodScanCallbacksOnDone(v23_syncbase_NeighborhoodScanCallbacks cbs) {
+  cbs.onDone(cbs.handle);
+}
 */
 import "C"
 
@@ -82,8 +98,13 @@
 	// testLogin indicates if the test login mode is used. This is triggered
 	// by using an empty identity provider.
 	testLogin bool
+
+	// neighborhoodAdStatus tracks the status of the neighborhood advertisement.
+	neighborhoodAdStatus *adStatus
 )
 
+var globalRefMap = refmap.NewRefMap()
+
 // TODO(razvanm): Replace the function arguments with an options struct.
 //export v23_syncbase_Init
 func v23_syncbase_Init(cClientUnderstandVom C.v23_syncbase_Bool, cRootDir C.v23_syncbase_String, cTestLogin C.v23_syncbase_Bool) {
@@ -104,6 +125,32 @@
 	}
 }
 
+type adStatus struct {
+	isAdvertising bool
+	cancel        context.CancelFunc
+	done          <-chan struct{}
+}
+
+func newAdStatus() *adStatus {
+	return &adStatus{}
+}
+
+func (a *adStatus) store(cancel context.CancelFunc, done <-chan struct{}) {
+	a.isAdvertising = true
+	a.cancel = cancel
+	a.done = done
+}
+
+func (a *adStatus) stop() {
+	if a.isAdvertising {
+		a.cancel()
+		<-a.done
+		a.isAdvertising = false
+		a.cancel = nil
+		a.done = nil
+	}
+}
+
 //export v23_syncbase_Serve
 func v23_syncbase_Serve(cErr *C.v23_syncbase_VError) {
 	if !isLoggedIn() {
@@ -266,6 +313,77 @@
 }
 
 ////////////////////////////////////////
+// Service
+
+//export v23_syncbase_NeighborhoodStartAdvertising
+func v23_syncbase_NeighborhoodStartAdvertising(cVisibility C.v23_syncbase_Strings, cErr *C.v23_syncbase_VError) {
+	// Cancel an old ad, if necessary.
+	v23_syncbase_NeighborhoodStopAdvertising()
+
+	// Initialize app advertisement.
+	advCtx, cancel := context.WithCancel(b.Ctx)
+	visibility := cVisibility.extract()
+	visBlessingPatterns := make([]security.BlessingPattern, len(visibility))
+	for i, vis := range visibility {
+		visBlessingPatterns[i] = security.BlessingPattern(vis)
+	}
+	doneAd, err := discovery.AdvertiseApp(advCtx, visBlessingPatterns)
+	if err != nil {
+		cErr.init(err)
+		return
+	}
+
+	// Remember the ad's cancellation information.
+	neighborhoodAdStatus.store(cancel, doneAd)
+}
+
+//export v23_syncbase_NeighborhoodStopAdvertising
+func v23_syncbase_NeighborhoodStopAdvertising() {
+	neighborhoodAdStatus.stop()
+}
+
+//export v23_syncbase_NeighborhoodIsAdvertising
+func v23_syncbase_NeighborhoodIsAdvertising(cBool *C.v23_syncbase_Bool) {
+	cBool.init(neighborhoodAdStatus.isAdvertising)
+}
+
+//export v23_syncbase_NeighborhoodNewScan
+func v23_syncbase_NeighborhoodNewScan(cbs C.v23_syncbase_NeighborhoodScanCallbacks, cUint64 *C.uint64_t, cErr *C.v23_syncbase_VError) {
+	scanCtx, cancel := context.WithCancel(b.Ctx)
+	scanChan := make(chan discovery.AppPeer)
+	err := discovery.ListenForAppPeers(scanCtx, scanChan)
+	if err != nil {
+		cErr.init(err)
+		return
+	}
+
+	// Forward the scan results to the callback.
+	go func() {
+		for {
+			peer, ok := <-scanChan
+			if !ok {
+				C.CallNeighborhoodScanCallbacksOnDone(cbs)
+				break
+			}
+			cPeer := C.v23_syncbase_AppPeer{}
+			cPeer.init(peer)
+			C.CallNeighborhoodScanCallbacksOnPeer(cbs, cPeer)
+		}
+	}()
+
+	// Remember the scan's cancellation information and return the scan's id.
+	x := C.uint64_t(globalRefMap.Add(cancel))
+	cUint64 = &x
+}
+
+//export v23_syncbase_NeighborhoodStopScan
+func v23_syncbase_NeighborhoodStopScan(cUint64 C.uint64_t) {
+	if cancel, ok := globalRefMap.Remove(uint64(cUint64)).(context.CancelFunc); ok && cancel != nil {
+		cancel()
+	}
+}
+
+////////////////////////////////////////
 // Database
 
 //export v23_syncbase_DbCreate
@@ -626,6 +744,52 @@
 }
 
 ////////////////////////////////////////
+// Syncgroup Invites
+
+//export v23_syncbase_DbSyncgroupInvitesNewScan
+func v23_syncbase_DbSyncgroupInvitesNewScan(cName C.v23_syncbase_String, cbs C.v23_syncbase_DbSyncgroupInvitesCallbacks, cUint64 *C.uint64_t, cErr *C.v23_syncbase_VError) {
+	encodedId := cName.extract()
+	dbId, err := util.DecodeId(encodedId)
+	if err != nil {
+		cErr.init(err)
+		return
+	}
+
+	scanCtx, cancel := context.WithCancel(b.Ctx)
+	scanChan := make(chan discovery.Invite)
+	err = discovery.ListenForInvites(scanCtx, dbId, scanChan)
+	if err != nil {
+		cErr.init(err)
+		return
+	}
+
+	// Forward the scan results to the callback.
+	go func() {
+		for {
+			invite, ok := <-scanChan
+			if !ok {
+				C.CallDbSyncgroupInvitesOnDone(cbs)
+				break
+			}
+			cInvite := C.v23_syncbase_Invite{}
+			cInvite.init(invite)
+			C.CallDbSyncgroupInvitesOnInvite(cbs, cInvite)
+		}
+	}()
+
+	// Remember the scan's cancellation information and return the scan's id.
+	x := C.uint64_t(globalRefMap.Add(cancel))
+	cUint64 = &x
+}
+
+//export v23_syncbase_DbSyncgroupInvitesStopScan
+func v23_syncbase_DbSyncgroupInvitesStopScan(cUint64 C.uint64_t) {
+	if cancel, ok := globalRefMap.Remove(uint64(cUint64)).(context.CancelFunc); ok && cancel != nil {
+		cancel()
+	}
+}
+
+////////////////////////////////////////
 // Collection
 
 //export v23_syncbase_CollectionCreate
diff --git a/services/syncbase/bridge/cgo/jni.go b/services/syncbase/bridge/cgo/jni.go
index dd7fd2a..eda5486 100644
--- a/services/syncbase/bridge/cgo/jni.go
+++ b/services/syncbase/bridge/cgo/jni.go
@@ -10,7 +10,6 @@
 import (
 	"fmt"
 	"unsafe"
-	"v.io/x/ref/services/syncbase/bridge/cgo/refmap"
 )
 
 /*
@@ -63,8 +62,6 @@
 	watchChangeClass            jWatchChange
 )
 
-var globalRefMap = refmap.NewRefMap()
-
 // JNI_OnLoad is called when System.loadLibrary is called. We need to cache the
 // *JavaVM because that's the only way to get hold of a JNIEnv that is needed
 // for any JNI operation.
diff --git a/services/syncbase/bridge/cgo/lib.h b/services/syncbase/bridge/cgo/lib.h
index d216df6..748661d 100644
--- a/services/syncbase/bridge/cgo/lib.h
+++ b/services/syncbase/bridge/cgo/lib.h
@@ -128,6 +128,19 @@
   int n;
 } v23_syncbase_SyncgroupMemberInfoMap;
 
+// syncbase.discovery.Invite
+typedef struct {
+  v23_syncbase_Id syncgroup;
+  v23_syncbase_Strings addresses;
+} v23_syncbase_Invite;
+
+// syncbase.discovery.AppPeer
+typedef struct {
+  v23_syncbase_String appName;
+  v23_syncbase_String blessings;
+  bool isLost;
+} v23_syncbase_AppPeer;
+
 ////////////////////////////////////////
 // Functions
 
@@ -145,4 +158,16 @@
   void (*onDone)(v23_syncbase_Handle handle, v23_syncbase_VError);
 } v23_syncbase_CollectionScanCallbacks;
 
+typedef struct {
+  v23_syncbase_Handle handle;
+  void (*onInvite)(v23_syncbase_Handle handle, v23_syncbase_Invite);
+  void (*onDone)(v23_syncbase_Handle handle);
+} v23_syncbase_DbSyncgroupInvitesCallbacks;
+
+typedef struct {
+  v23_syncbase_Handle handle;
+  void (*onPeer)(v23_syncbase_Handle handle, v23_syncbase_AppPeer);
+  void (*onDone)(v23_syncbase_Handle handle);
+} v23_syncbase_NeighborhoodScanCallbacks;
+
 #endif  // V23_SYNCBASE_LIB_H_
diff --git a/services/syncbase/bridge/cgo/types.go b/services/syncbase/bridge/cgo/types.go
index 26907ab..0188d17 100644
--- a/services/syncbase/bridge/cgo/types.go
+++ b/services/syncbase/bridge/cgo/types.go
@@ -13,6 +13,7 @@
 	"v.io/v23/syncbase"
 	"v.io/v23/verror"
 	"v.io/v23/vom"
+	"v.io/x/ref/services/syncbase/discovery"
 )
 
 // All "x.extract" methods return a native Go type and leave x in the same state
@@ -436,3 +437,20 @@
 	x.continued = C.bool(wc.Continued)
 	return nil
 }
+
+////////////////////////////////////////////////////////////
+// C.v23_syncbase_Invite
+
+func (x *C.v23_syncbase_Invite) init(invite discovery.Invite) {
+	x.syncgroup.init(invite.Syncgroup)
+	x.addresses.init(invite.Addresses)
+}
+
+////////////////////////////////////////////////////////////
+// C.v23_syncbase_AppPeer
+
+func (x *C.v23_syncbase_AppPeer) init(peer discovery.AppPeer) {
+	x.appName.init(peer.AppName)
+	x.blessings.init(peer.Blessings)
+	x.isLost = C.bool(peer.Lost)
+}