Got everything to compile.

Change-Id: I9c001c55b51f4cd1b7c180216d2e75837072a17f
diff --git a/services/mounttable/mounttablelib/neighborhood_ble.go b/services/mounttable/mounttablelib/neighborhood_ble.go
new file mode 100644
index 0000000..478043d
--- /dev/null
+++ b/services/mounttable/mounttablelib/neighborhood_ble.go
@@ -0,0 +1,231 @@
+package mounttablelib
+
+import (
+	"strings"
+	"sync"
+	"time"
+
+	"v.io/v23/context"
+
+	"github.com/paypal/gatt"
+	"github.com/paypal/gatt/linux/cmd"
+)
+
+var mounttableServiceUUID = gatt.MustParseUUID("f7d47ad1-c344-4ad1-b43a-5be7570d3ffd")
+var addressesUUID = gatt.MustParseUUID("32719e70-5da5-4d40-9a4a-866f491b2f2d")
+
+func newMountTableService(addresses []string) *gatt.Service {
+	addressString := strings.Join(addresses, "@@@@")
+	s := gatt.NewService(mounttableServiceUUID)
+	s.AddCharacteristic(addressesUUID).HandleReadFunc(
+		func(rsp gatt.ResponseWriter, req *gatt.ReadRequest) {
+			if req.Offset > len(addressString) {
+				return
+			}
+			end := req.Offset + req.Cap
+			if end > len(addressString) {
+				end = len(addressString)
+			}
+			rsp.Write([]byte(addressString[req.Offset:end]))
+		})
+	return s
+}
+
+var (
+	attrGAPUUID = gatt.UUID16(0x1800)
+
+	attrDeviceNameUUID        = gatt.UUID16(0x2A00)
+	attrAppearanceUUID        = gatt.UUID16(0x2A01)
+	attrPeripheralPrivacyUUID = gatt.UUID16(0x2A02)
+	attrReconnectionAddrUUID  = gatt.UUID16(0x2A03)
+	attrPeferredParamsUUID    = gatt.UUID16(0x2A04)
+
+	attrGATTUUID           = gatt.UUID16(0x1801)
+	attrServiceChangedUUID = gatt.UUID16(0x2A05)
+)
+
+// https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml
+var gapCharAppearanceGenericComputer = []byte{0x00, 0x80}
+
+func newGapService(name string) *gatt.Service {
+	s := gatt.NewService(attrGAPUUID)
+	s.AddCharacteristic(attrDeviceNameUUID).SetValue([]byte(name))
+	s.AddCharacteristic(attrAppearanceUUID).SetValue(gapCharAppearanceGenericComputer)
+	s.AddCharacteristic(attrPeripheralPrivacyUUID).SetValue([]byte{0x00})
+	s.AddCharacteristic(attrReconnectionAddrUUID).SetValue([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
+	s.AddCharacteristic(attrPeferredParamsUUID).SetValue([]byte{0x06, 0x00, 0x06, 0x00, 0x00, 0x00, 0xd0, 0x07})
+	return s
+}
+
+func newGattService() *gatt.Service {
+	s := gatt.NewService(attrGATTUUID)
+	s.AddCharacteristic(attrServiceChangedUUID).HandleNotifyFunc(
+		func(r gatt.Request, n gatt.Notifier) {})
+	return s
+}
+
+var gattServerOptions = []gatt.Option{
+	gatt.LnxMaxConnections(100),
+	gatt.LnxDeviceID(-1, true),
+	gatt.LnxSetAdvertisingParameters(&cmd.LESetAdvertisingParameters{
+		// Set an advertising rate of 150ms.  This value is multipled by
+		// 0.625ms to get the actual rate.
+		AdvertisingIntervalMin: 0x00f4,
+		AdvertisingIntervalMax: 0x00f4,
+		AdvertisingChannelMap:  0x7,
+	}),
+}
+
+type bleCacheEntry struct {
+	id        string
+	name      string
+	endpoints []string
+	lastSeen  time.Time
+}
+type bleNeighborHood struct {
+	mu                 sync.Mutex
+	neighborsIdCache   map[string]*bleCacheEntry
+	neighborsNameCache map[string]*bleCacheEntry
+	name               string
+	ctx                *context.T
+	serverDevice       gatt.Device
+	clientDevice       gatt.Device
+}
+
+func newBleNeighborhood(ctx *context.T, name string, eps []string) (*bleNeighborHood, error) {
+	b := &bleNeighborHood{
+		neighborsIdCache:   make(map[string]*bleCacheEntry),
+		neighborsNameCache: make(map[string]*bleCacheEntry),
+		name:               name,
+		ctx:                ctx,
+	}
+	return b, b.startBLEService(eps)
+}
+
+func (b *bleNeighborHood) startBLEService(eps []string) error {
+	d, err := gatt.NewDevice(gattServerOptions...)
+	if err != nil {
+		return err
+	}
+	d.Handle(
+		gatt.CentralConnected(func(c gatt.Central) { b.ctx.VI(0).Infof("Connect: %v", c.ID()) }),
+		gatt.CentralDisconnected(func(c gatt.Central) { b.ctx.VI(0).Infof("Disconnected: %v", c.ID()) }),
+	)
+
+	onStateChanged := func(d gatt.Device, s gatt.State) {
+		b.ctx.VI(0).Infof("State: %s", s)
+		switch s {
+		case gatt.StatePoweredOn:
+			d.AddService(newGapService(b.name))
+			d.AddService(newGattService())
+
+			s1 := newMountTableService(eps)
+			d.AddService(s1)
+			d.AdvertiseNameAndServices(b.name, []gatt.UUID{s1.UUID()})
+		default:
+		}
+	}
+
+	d.Init(onStateChanged)
+	b.serverDevice = d
+	return nil
+}
+
+var gattClientOptions = []gatt.Option{
+	gatt.LnxMaxConnections(1),
+	gatt.LnxDeviceID(-1, true),
+}
+
+func (b *bleNeighborHood) scanForNeighbors() {
+	onStateChanged := func(d gatt.Device, s gatt.State) {
+		switch s {
+		case gatt.StatePoweredOn:
+			// We should limit it to only mounttable service, but this
+			// isn't implemented in the library
+			d.Scan([]gatt.UUID{}, false)
+		default:
+			d.StopScanning()
+		}
+	}
+
+	onPeriphDiscovered := func(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
+		// TODO(bjornick): Handle error
+		p.Device().Connect(p)
+	}
+
+	onPeriphConnected := func(p gatt.Peripheral, err error) {
+		defer p.Device().CancelConnection(p)
+		if err := p.SetMTU(500); err != nil {
+			b.ctx.Errorf("Failed to set MTU, err: %s", err)
+			return
+		}
+
+		ss, err := p.DiscoverServices(nil)
+
+		if err != nil {
+			b.ctx.Errorf("Failed to discover services, err: %s", err)
+			return
+		}
+
+		for _, s := range ss {
+			if !s.UUID().Equal(mounttableServiceUUID) {
+				b.ctx.Infof("Skipping non-mt service %s", s.Name())
+				continue
+			}
+
+			cs, err := p.DiscoverCharacteristics(nil, s)
+			if err != nil {
+				b.ctx.Errorf("Failed to discover characteristics: %s", err)
+				continue
+			}
+
+			for _, c := range cs {
+				if !c.UUID().Equal(addressesUUID) {
+					b.ctx.Infof("Skipping non-address characteristic: %s", c.Name())
+					continue
+				}
+				eps, err := p.ReadCharacteristicBlob(c)
+				if err != nil {
+					b.ctx.Errorf("Failed to read the addresses: %v", err)
+					continue
+				}
+				b.saveAddress(p.ID(), p.Name(), string(eps))
+			}
+
+		}
+	}
+	var err error
+	b.clientDevice, err = gatt.NewDevice(gattClientOptions...)
+	if err != nil {
+		b.ctx.Errorf("Failed to open device, err: %s\n", err)
+		return
+	}
+	b.clientDevice.Handle(
+		gatt.PeripheralDiscovered(onPeriphDiscovered),
+		gatt.PeripheralConnected(onPeriphConnected),
+	)
+
+	b.clientDevice.Init(onStateChanged)
+}
+
+func (b *bleNeighborHood) saveAddress(id string, name string, eps string) {
+	b.mu.Lock()
+	defer b.mu.Unlock()
+	b.ctx.VI(0).Infof("Saving endpoints (%s) for (%s, %s)", eps, id, name)
+	entry, found := b.neighborsIdCache[id]
+	if !found {
+		entry = &bleCacheEntry{
+			id:   id,
+			name: name,
+		}
+		b.neighborsIdCache[id] = entry
+		b.neighborsNameCache[name] = entry
+	}
+
+	if entry.name != name {
+		b.ctx.Errorf("Name of the neighbor changed. was %s, now %s", entry.name, name)
+		return
+	}
+	entry.endpoints = strings.Split(eps, "@@@")
+	entry.lastSeen = time.Now()
+}
diff --git a/services/mounttable/mounttablelib/servers.go b/services/mounttable/mounttablelib/servers.go
index 2d919d1..3e9a295 100644
--- a/services/mounttable/mounttablelib/servers.go
+++ b/services/mounttable/mounttablelib/servers.go
@@ -67,6 +67,12 @@
 			return "", nil, err
 		}
 		stopFuncs = append(stopFuncs, nhServer.Stop)
+
+		// For now let's leak this server.  This is for ble support
+		_, err = newBleNeighborhood(ctx, nhName[0], mtServer.ServerStatus().Endpoints())
+		if err != nil {
+			return "", nil, err
+		}
 	}
 	return mtName, stop, nil
 }