proximity: factoring the proximity code out to a separate repository
This CL depends on https://veyron-review.googlesource.com/4012
Change-Id: I2de6b553ccd64c79c7d99c864fe7eb845f6830f6
diff --git a/lib/bluetooth/addr_linux.go b/lib/bluetooth/addr_linux.go
deleted file mode 100644
index 0b9bc8c..0000000
--- a/lib/bluetooth/addr_linux.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import (
- "bytes"
- "fmt"
- "net"
- "strconv"
- "strings"
-)
-
-// addr represents an RFCOMM over bluetooth address in the <MAC-channelID>
-// format, where channelID denotes one of the available RFCOMM channels.
-//
-// It implements the net.Addr interface.
-type addr struct {
- mac net.HardwareAddr
- channel int
-}
-
-// anyMAC is a MAC address "00:00:00:00:00:00", which means first available
-// (bluetooth) device.
-var anyMAC net.HardwareAddr
-
-func init() {
- var err error
- if anyMAC, err = net.ParseMAC("00:00:00:00:00:00"); err != nil {
- panic("can't parse address 00:00:00:00:00:00")
- }
-}
-
-// parseAddress parses an address string in the <MAC-channelID> format (e.g.,
-// "01:23:45:67:89:AB-1"). It returns an error if the address is in the wrong
-// format. It is legal for a MAC address sub-part to be empty, in which case
-// it will be treated as anyMAC (i.e., "00:00:00:00:00:00").
-func parseAddress(address string) (*addr, error) {
- parts := strings.Split(address, "-")
- if len(parts) != 2 {
- return nil, fmt.Errorf("too many or too few \"-\" in address: %s", address)
- }
- ms := parts[0]
- ps := parts[1]
- if len(ms) == 0 {
- channel, err := strconv.ParseInt(ps, 0, 32)
- if err != nil {
- return nil, err
- }
- return &addr{anyMAC, int(channel)}, nil
- } else {
- mac, err := net.ParseMAC(ms)
- if err != nil {
- return nil, err
- }
- channel, err := strconv.ParseInt(ps, 0, 32)
- if err != nil {
- return nil, err
- }
- return &addr{mac, int(channel)}, nil
- }
-}
-
-// Implements the net.Addr interface.
-func (a *addr) Network() string {
- return Network
-}
-
-// Implements the net.Addr interface.
-func (a *addr) String() string {
- return fmt.Sprintf("%s-%d", a.mac, a.channel)
-}
-
-// isAnyMAC returns true iff the mac address is "any" (i.e.,
-// "00:00:00:00:00:00")
-func (a *addr) isAnyMAC() bool {
- return bytes.Equal(a.mac, anyMAC)
-}
diff --git a/lib/bluetooth/addr_linux_test.go b/lib/bluetooth/addr_linux_test.go
deleted file mode 100644
index 303764e..0000000
--- a/lib/bluetooth/addr_linux_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import "testing"
-
-func TestParseValidAddr(t *testing.T) {
- testdata := []struct{ before, after string }{
- {"-1", "00:00:00:00:00:00-1"},
- {"0A:0B:33:55:C9:9a-8", "0a:0b:33:55:c9:9a-8"},
- }
- for _, d := range testdata {
- addr, err := parseAddress(d.before)
- if err != nil {
- t.Errorf("failed to parse %q: %v", d.before, err)
- continue
- }
- if got, want := addr.String(), d.after; got != want {
- t.Errorf("Got %q, want %q", got, want)
- }
- }
-}
-
-func TestParseInvalidAddr(t *testing.T) {
- testdata := []string{
- "00:01:02:03:04:05", // missing channel
- "00:01:02:03:04:05:06-1", // invalid MAC addr
- }
- for _, d := range testdata {
- addr, err := parseAddress(d)
- if err == nil {
- t.Errorf("Got %q, want error for parseAddress(%q)", addr, d)
- }
- }
-}
diff --git a/lib/bluetooth/bluetooth_common.go b/lib/bluetooth/bluetooth_common.go
deleted file mode 100644
index d782914..0000000
--- a/lib/bluetooth/bluetooth_common.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package bluetooth
-
-import (
- "net"
- "time"
-
- "veyron/lib/unit"
-)
-
-// Network string for net.Addr implementations used by the bluetooth
-// package.
-const Network = "bluetooth"
-
-// ScanReading holds a single reading of a Low-Energy scan on the Bluetooth device.
-type ScanReading struct {
- // Name represents a local name of the remote device. It can also store
- // arbitrary application-specific data.
- Name string
- // MAC is the hardware address of the remote device.
- MAC net.HardwareAddr
- // Distance represents the (power-estimated) distance to the remote device.
- Distance unit.Distance
- // Time is the time the advertisement packed was received/scanned.
- Time time.Time
-}
-
-// Device is a struct representing an opened Bluetooth device. It consists of
-// a device name, MAC address, id (e.g., 0 for hci0, 1 for hci1 etc.) and a
-// device descriptor.
-// It is not safe to invoke this type's methods concurrently from multiple
-// goroutines.
-type Device struct {
- Name string
- MAC net.HardwareAddr
- id int
- descriptor int
- leScanChan chan ScanReading
-}
diff --git a/lib/bluetooth/bluetooth_linux.go b/lib/bluetooth/bluetooth_linux.go
deleted file mode 100644
index 248b40a..0000000
--- a/lib/bluetooth/bluetooth_linux.go
+++ /dev/null
@@ -1,357 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import (
- "fmt"
- "math"
- "net"
- "syscall"
- "time"
- "unsafe"
-
- "veyron/lib/unit"
- "veyron2/vlog"
-)
-
-// // Explicitly link libbluetooth and other libraries as "go build" cannot
-// // figure out these dependencies..
-// #cgo LDFLAGS: -lbluetooth
-// #include <bluetooth/bluetooth.h>
-// #include <bluetooth/hci.h>
-// #include <bluetooth/hci_lib.h>
-// #include <stdlib.h>
-// #include <unistd.h>
-// #include "bt_linux.h"
-import "C"
-
-var (
- // MaxLEAdvertisingPayloadSize denotes the maximum size for the
- // LE advertising payload.
- // See the Bluetooth 4.0 spec for more info on Bluetooth LE payload
- // structure:
- // https://www.bluetooth.org/en-us/specification/adopted-specifications
- MaxLEAdvertisingPayloadSize = int(C.kMaxLEPayloadSize)
-
- // MaxChannel represents the highest channel id that can be used
- // for establishing RFCOMM connections.
- MaxChannel = int(C.kMaxChannel)
-
- // maxDevices denotes a maximum number of local devices to scan over
- // when a particular device isn't explicitly specified (e.g.,
- // OpenFirstAvailableDevice).
- maxDevices = int(C.kMaxDevices)
-)
-
-// OpenDevice opens the Bluetooth device with a specified id, returning an
-// error if the device couldn't be opened, or nil otherwise.
-func OpenDevice(deviceID int) (*Device, error) {
- if deviceID < 0 {
- return nil, fmt.Errorf("negative device id, use OpenFirstAvailableDevice() instead")
- }
- var descriptor C.int
- var name *C.char
- var localMAC *C.char
- if es := C.bt_open_device(C.int(deviceID), &descriptor, &name, &localMAC); es != nil {
- defer C.free(unsafe.Pointer(es))
- return nil, fmt.Errorf("error opening device %d: %s", deviceID, C.GoString(es))
- }
- defer C.free(unsafe.Pointer(name))
- defer C.free(unsafe.Pointer(localMAC))
- mac, err := net.ParseMAC(C.GoString(localMAC))
- if err != nil {
- return nil, fmt.Errorf("illegal hardware address %q for device %d: %s", C.GoString(localMAC), deviceID, err)
- }
- return &Device{
- Name: C.GoString(name),
- MAC: mac,
- id: deviceID,
- descriptor: int(descriptor),
- }, nil
-}
-
-// OpenFirstAvailableDevice() opens the first available bluetooth device,
-// returning an error if no device could be opened, or nil otherwise.
-func OpenFirstAvailableDevice() (*Device, error) {
- for devID := 0; devID < maxDevices; devID++ {
- if d, err := OpenDevice(devID); err == nil {
- return d, nil
- }
- }
- return nil, fmt.Errorf("can't find an available bluetooth device")
-}
-
-// Listen creates a new listener for RFCOMM connections on the provided
-// local address, specified in the <MAC-Channel> format (e.g.,
-// "01:23:45:67:89:AB-1"). Channel number 0 means pick the first available
-// channel. Empty MAC address means pick the first available bluetooth device.
-// Error is returned if a listener cannot be created.
-// Note that the returned net.Listener won't use the runtime network poller
-// and hence a new OS thread will be created for every outstanding connection.
-func Listen(localAddr string) (net.Listener, error) {
- local, err := parseAddress(localAddr)
- if err != nil {
- return nil, fmt.Errorf("listen error: invalid local address format %s, error: %s", localAddr, err)
- }
- if local.channel > MaxChannel {
- return nil, fmt.Errorf("listen error: channel %d too large - max: %d", local.channel, MaxChannel)
- }
-
- // Open a new local bluetooth socket.
- socket := C.socket(C.AF_BLUETOOTH, C.SOCK_STREAM, C.BTPROTO_RFCOMM)
- if socket < 0 {
- return nil, fmt.Errorf("listen error: can't open new RFCOMM socket")
- }
-
- // Bind to the local socket.
- var localMAC *C.char
- if !local.isAnyMAC() {
- localMAC = C.CString(local.mac.String())
- }
- defer C.free(unsafe.Pointer(localMAC))
- localChannel := C.int(local.channel)
- if es := C.bt_bind(socket, &localMAC, &localChannel); es != nil {
- defer C.free(unsafe.Pointer(es))
- syscall.Close(int(socket))
- return nil, fmt.Errorf("listen error: %v", C.GoString(es))
- }
- // Re-parse the address as it may have changed.
- if local.mac, err = net.ParseMAC(C.GoString(localMAC)); err != nil {
- syscall.Close(int(socket))
- return nil, fmt.Errorf("listen error: invalid local MAC address: %s, err: %v", C.GoString(localMAC), err)
- }
- local.channel = int(localChannel)
-
- // Create a listener for incoming connections.
- const maxPendingConnections = 100
- if err = syscall.Listen(int(socket), maxPendingConnections); err != nil {
- syscall.Close(int(socket))
- return nil, fmt.Errorf("listen error: %v", err)
- }
- return newListener(int(socket), local)
-}
-
-// Dial creates a new RFCOMM connection with the remote address, specified in
-// the <MAC/Channel> format (e.g., "01:23:45:67:89:AB-1"). It returns an error
-// if the connection cannot be established.
-// Note that the returned net.Conn won't use the runtime network poller and
-// hence a new OS thread will be created for every outstanding connection.
-func Dial(remoteAddr string) (net.Conn, error) {
- remote, err := parseAddress(remoteAddr)
- if err != nil {
- return nil, fmt.Errorf("dial error: invalid remote address format %s, error %s", remoteAddr, err)
- }
- if remote.isAnyMAC() {
- return nil, fmt.Errorf("dial error: must specify remote MAC address: %s", remoteAddr)
- }
- if remote.channel > MaxChannel {
- return nil, fmt.Errorf("dial error: channel %d too large - max: %d", remote.channel, MaxChannel)
- }
-
- // Open a new local bluetooth socket.
- socket := C.socket(C.AF_BLUETOOTH, C.SOCK_STREAM, C.BTPROTO_RFCOMM)
- if socket < 0 {
- return nil, fmt.Errorf("dial error: can't open new RFCOMM socket")
- }
-
- // Bind to the local socket.
- var localMAC *C.char // bind to first available device
- localChannel := C.int(0) // bind to first available channel
- if es := C.bt_bind(socket, &localMAC, &localChannel); es != nil {
- defer C.free(unsafe.Pointer(es))
- syscall.Close(int(socket))
- return nil, fmt.Errorf("dial error: %v", C.GoString(es))
- }
- defer C.free(unsafe.Pointer(localMAC))
- // Parse the local address.
- var local addr
- if local.mac, err = net.ParseMAC(C.GoString(localMAC)); err != nil {
- return nil, fmt.Errorf("dial error: invalid local MAC address: %s, err: %s", C.GoString(localMAC), err)
- }
- local.channel = int(localChannel)
-
- // Connect to the remote address.
- remoteMAC := C.CString(remote.mac.String())
- defer C.free(unsafe.Pointer(remoteMAC))
- remoteChannel := C.int(remote.channel)
- if es := C.bt_connect(socket, remoteMAC, remoteChannel); es != nil {
- defer C.free(unsafe.Pointer(es))
- return nil, fmt.Errorf("dial error: error connecting to remote address: %s, error: %s", remoteAddr, C.GoString(es))
- }
- return newConn(int(socket), &local, remote)
-}
-
-func (d *Device) String() string {
- return fmt.Sprintf("BT_DEVICE(%s, %v)", d.Name, d.MAC)
-}
-
-// StartAdvertising starts LE advertising on the Bluetooth device, sending
-// one advertising packet after every tick of the provided time interval.
-// The payload sent with each advertising packet can be specified via the
-// SetAdvertisingPayload method.
-// This method may be called again even if advertising is currently enabled,
-// in order to adjust the advertising interval.
-func (d *Device) StartAdvertising(interval time.Duration) error {
- if es := C.bt_start_le_advertising(C.int(d.descriptor), C.int(int64(interval/time.Millisecond))); es != nil {
- defer C.free(unsafe.Pointer(es))
- return fmt.Errorf("error starting LE advertising on device: %v, error: %s", d, C.GoString(es))
- }
- return nil
-}
-
-// SetAdvertisingPayload sets the advertising payload that is sent with each
-// advertising packet. This function may be called at any time to adjust the
-// payload that is currently being advertised.
-func (d *Device) SetAdvertisingPayload(payload string) error {
- if es := C.bt_set_le_advertising_payload(C.int(d.descriptor), C.CString(payload)); es != nil {
- defer C.free(unsafe.Pointer(es))
- return fmt.Errorf("error setting advertising payload on device: %v, error: %s", d, C.GoString(es))
- }
- return nil
-}
-
-// StopAdvertising stops LE advertising on the Bluetooth device. If the
-// device is not advertising, this function will be a noop.
-func (d *Device) StopAdvertising() error {
- if es := C.bt_stop_le_advertising(C.int(d.descriptor)); es != nil {
- defer C.free(unsafe.Pointer(es))
- return fmt.Errorf("error stopping LE advertising on device: %v, error: %s", d, C.GoString(es))
- }
- return nil
-}
-
-// StartScan initiates a Low-Energy scan on the Bluetooth device. The scan
-// will proceed over many duration intervals; within each interval, scan will
-// be ON only for a given duration window. All scan readings encountered
-// during scan-ON periods are pushed onto the returned channel. If the scan
-// cannot be started, an error is returned.
-func (d *Device) StartScan(scanInterval, scanWindow time.Duration) (<-chan ScanReading, error) {
- if scanInterval < scanWindow {
- return nil, fmt.Errorf("invalid scan settings: scan interval %d must be greater or equal to scan window %d", scanInterval, scanWindow)
- }
- // Set scan params.
- const (
- passiveScan = 0x00
- publicAddress = 0x00
- acceptAllPackets = 0x00
- timeoutMS = 1000
- )
- if ret, err := C.hci_le_set_scan_parameters(C.int(d.descriptor), passiveScan, C.uint16_t(scanInterval/time.Millisecond), C.uint16_t(scanWindow/time.Millisecond), publicAddress, acceptAllPackets, timeoutMS); ret < 0 {
- return nil, fmt.Errorf("error setting LE scan parameters: %v", err)
- }
- // Enable scan.
- const (
- scanEnabled = 0x01
- disableDuplicateFiltering = 0x00
- )
- if ret, err := C.hci_le_set_scan_enable(C.int(d.descriptor), scanEnabled, disableDuplicateFiltering, timeoutMS); ret < 0 {
- return nil, fmt.Errorf("error enabling LE scan: %v", err)
- }
- // Set the event filter options. We're only interested in LE meta
- // events.
- var fopts C.struct_hci_filter
- C.hci_filter_clear(&fopts)
- C.hci_filter_set_ptype(C.HCI_EVENT_PKT, &fopts)
- C.hci_filter_set_event(C.EVT_LE_META_EVENT, &fopts)
- if ret, err := C.setsockopt(C.int(d.descriptor), C.SOL_HCI, C.HCI_FILTER, unsafe.Pointer(&fopts), C.socklen_t(unsafe.Sizeof(fopts))); ret < 0 {
- return nil, fmt.Errorf("couldn't set filter options on socket: %v", err)
- }
-
- // Start the reading go-routine.
- d.leScanChan = make(chan ScanReading, 10)
- go d.leScanLoop()
- return d.leScanChan, nil
-}
-
-func (d *Device) leScanLoop() {
- defer vlog.Info("LE scan reading goroutine exiting")
- buf := make([]byte, C.HCI_MAX_EVENT_SIZE)
- for {
- // Read one advertising meta event.
- n, err := syscall.Read(int(C.int(d.descriptor)), buf)
- if err != nil || n < 0 {
- vlog.Errorf("error getting scan readings: %v", err)
- return
- }
- // Get data of interest to us.
- var remoteMAC, remoteName *C.char
- var rssi, done C.int
- if es := C.bt_parse_le_meta_event(unsafe.Pointer(&buf[0]), &remoteMAC, &remoteName, &rssi, &done); es != nil {
- vlog.Errorf("couldn't parse LE meta event: %s", C.GoString(es))
- C.free(unsafe.Pointer(es))
- continue
- }
- if done != 0 { // Scan stopped.
- return
- }
- name := C.GoString(remoteName)
- C.free(unsafe.Pointer(remoteName))
- mac, err := net.ParseMAC(C.GoString(remoteMAC))
- C.free(unsafe.Pointer(remoteMAC))
- if err != nil {
- vlog.Errorf("invalid MAC address: %v", mac)
- continue
- }
- d.leScanChan <- ScanReading{
- Name: name,
- MAC: mac,
- Distance: distanceFromRSSI(int(rssi)),
- Time: time.Now(),
- }
- }
-}
-
-// StopScan stops any Low-Energy scan in progress on the Bluetooth device.
-// If the device is not scanning, this function will be a noop.
-func (d *Device) StopScan() error {
- // Disable scan. This will also stop the reading goroutine.
- const (
- scanDisabled = 0x00
- disableDuplicateFiltering = 0x00
- timeoutMS = 1000
- )
- if ret, err := C.hci_le_set_scan_enable(C.int(d.descriptor), scanDisabled, disableDuplicateFiltering, timeoutMS); ret < 0 {
- return fmt.Errorf("error disabling LE scan: %v", err)
- }
-
- close(d.leScanChan)
- return nil
-}
-
-// Close closes our handle on the Bluetooth device. Note that this call doesn't
-// stop any operations in progress on the device (e.g., LE advertising) - it
-// simply closes the handle to it.
-func (d *Device) Close() error {
- if es := C.bt_close_device(C.int(d.descriptor)); es != nil {
- defer C.free(unsafe.Pointer(es))
- return fmt.Errorf("error closing device %v, error: %s", d, C.GoString(es))
- }
- return nil
-}
-
-// distanceFromRSSI computes the distance to the neighboring device using
-// the RSSI of that device's advertising packet.
-func distanceFromRSSI(rssi int) unit.Distance {
- // We're using the formula (and observed constants) from the following
- // paper:
- // "Outdoor Localization System Using RSSI Measurement of Wireless
- // Sensor Network"
- // by: Oguejiofor O.S., Okorogu V.N., Adewale Abe, and Osuesu B.O
- // link: http://www.ijitee.org/attachments/File/v2i2/A0359112112.pdf
- //
- // Formula:
- //
- // RSSI [dBm] = -10n log10(d [m]) + A [dBm]
- //
- //, where:
- //
- // A = received signal strength at 1m (observed to be -44.8dBm)
- // n = propagation pathloss exponent (observed to be 2.2)
- // d = distance (in meters)
- //
- // The final formula for distance (in meters) therefore comes down to:
- //
- // d = 10^((RSSI / -22) - 2.036)
- //
- return unit.Distance(math.Pow(10.0, (float64(rssi)/-22.0)-2.036)) * unit.Meter
-}
diff --git a/lib/bluetooth/bluetooth_linux_test.go b/lib/bluetooth/bluetooth_linux_test.go
deleted file mode 100644
index 3b6a152..0000000
--- a/lib/bluetooth/bluetooth_linux_test.go
+++ /dev/null
@@ -1,32 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import (
- "math"
- "testing"
-
- "veyron/lib/unit"
-)
-
-func TestDistanceFromRSSI(t *testing.T) {
- testcases := []struct {
- rssi int
- dist unit.Distance
- }{
- {-45, 1 * unit.Meter},
- {-50, 1 * unit.Meter},
- {-55, 2 * unit.Meter},
- {-60, 4 * unit.Meter},
- {-65, 8 * unit.Meter},
- {-70, 13 * unit.Meter},
- {-80, 39 * unit.Meter},
- {-90, 113 * unit.Meter},
- }
- for _, tc := range testcases {
- d := distanceFromRSSI(tc.rssi)
- if math.Trunc(d.Meters()) != tc.dist.Meters() {
- t.Errorf("distanceFromRSSI(%d) = %v; want %v", tc.rssi, d, tc.dist)
- }
- }
-}
diff --git a/lib/bluetooth/bluetooth_other.go b/lib/bluetooth/bluetooth_other.go
deleted file mode 100644
index 5d5897e..0000000
--- a/lib/bluetooth/bluetooth_other.go
+++ /dev/null
@@ -1,38 +0,0 @@
-// +build !veyronbluetooth !linux android
-
-package bluetooth
-
-import (
- "errors"
- "net"
- "time"
-)
-
-var errNotSupported = errors.New("bluetooth is not supported on this platform")
-
-func (d *Device) StartScan(scanInterval, scanWindow time.Duration) (<-chan ScanReading, error) {
- return nil, errNotSupported
-}
-func (d *Device) StopScan() error { return errNotSupported }
-func (d *Device) Close() error { return errNotSupported }
-func (d *Device) StartAdvertising(advInterval time.Duration) error { return errNotSupported }
-func (d *Device) SetAdvertisingPayload(payload string) error { return errNotSupported }
-func (d *Device) StopAdvertising() error { return errNotSupported }
-
-// Dial always returns an error since bluetooth is not yet supported
-// on this platform.
-func Dial(remoteaddr string) (net.Conn, error) {
- return nil, errNotSupported
-}
-
-// Listen always returns an error since bluetooth is not yet supported
-// on this platform.
-func Listen(localaddr string) (net.Listener, error) {
- return nil, errNotSupported
-}
-
-// OpenFirstAvailableDevice always returns an error since bluetooth is
-// not yet supported on this platform.
-func OpenFirstAvailableDevice() (*Device, error) {
- return nil, errNotSupported
-}
diff --git a/lib/bluetooth/bt_linux.c b/lib/bluetooth/bt_linux.c
deleted file mode 100644
index f4ea910..0000000
--- a/lib/bluetooth/bt_linux.c
+++ /dev/null
@@ -1,336 +0,0 @@
-// +build veyronbluetooth,!android
-
-#include "bt_linux.h"
-
-#include <assert.h>
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/ioctl.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-
-#define EIR_FLAGS 0x01 /* flags */
-#define EIR_UUID16_SOME 0x02 /* 16-bit UUID, more available */
-#define EIR_UUID16_ALL 0x03 /* 16-bit UUID, all listed */
-#define EIR_UUID32_SOME 0x04 /* 32-bit UUID, more available */
-#define EIR_UUID32_ALL 0x05 /* 32-bit UUID, all listed */
-#define EIR_UUID128_SOME 0x06 /* 128-bit UUID, more available */
-#define EIR_UUID128_ALL 0x07 /* 128-bit UUID, all listed */
-#define EIR_NAME_SHORT 0x08 /* shortened local name */
-#define EIR_NAME_COMPLETE 0x09 /* complete local name */
-#define EIR_TX_POWER 0x0A /* transmit power level */
-#define EIR_DEVICE_ID 0x10 /* device ID */
-
-// Timeout for all hci requests, in milliseconds.
-static const int kTimeoutMs = 1000;
-static const int kMaxAddrStrSize = 18;
-
-const int kMaxLEPayloadSize = 26;
-const int kMaxChannel = 30;
-const int kMaxDevices = 5;
-
-char* bt_open_device(int dev_id, int* dd, char** name, char** local_address) {
- char* err = NULL;
- int sock;
- struct hci_dev_req dev_req;
- struct hci_dev_info di;
- bdaddr_t loc_addr;
-
- if (dev_id < 0) {
- asprintf(err, "can't pass negative device id for bt_open_device().");
- return err;
- }
-
- // Open HCI socket.
- if ((sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
- asprintf(&err, "can't open HCI socket:%d[%s]", errno, strerror(errno));
- return err;
- }
-
- // Get device's name.
- di.dev_id = dev_id;
- if (ioctl(sock, HCIGETDEVINFO, (void *)&di) < 0) {
- asprintf(&err, "can't get device info:%d[%s]", errno, strerror(errno));
- close(sock);
- return err;
- }
- *name = (char*) malloc(strlen(di.name) * sizeof(char));
- strcpy(*name, di.name);
-
- // Try to open the specified device.
- ioctl(sock, HCIDEVUP, dev_id);
- *dd = hci_open_dev(dev_id);
- if (*dd < 0) {
- asprintf(&err, "can't open device %d:%d[%s]", dev_id, errno, strerror(errno));
- close(sock);
- return err;
- }
-
- // NOTE(spetrovic): We need to enable page scanning on the device for
- // RFCOMM connections to work. Since this requires root access, it will
- // probably need to be done elsewhere (e.g., 'sudo hciconfig hci0 pscan').
-
- // Get device's local MAC address.
- hci_devba(dev_id, &loc_addr);
- *local_address = (char*) malloc(kMaxAddrStrSize * sizeof(char));
- ba2str(&loc_addr, *local_address);
-
- return NULL;
-}
-
-char* bt_bind(int sock, char** local_address, int* channel) {
- char* err = NULL;
- int dev_id, dd;
- struct sockaddr_rc addr = { 0 };
-
- if (*local_address == NULL) {
- char* name = NULL;
- // Find the first available device.
- for (dev_id = 0; dev_id < kMaxDevices; dev_id++) {
- if ((err = bt_open_device(dev_id, &dd, &name, local_address)) != NULL) {
- free(err);
- continue;
- }
- bt_close_device(dd);
- assert(*local_address != NULL);
- if ((err = bt_bind(sock, local_address, channel)) != NULL) {
- free(err);
- free(*local_address);
- continue;
- }
- return NULL;
- }
- asprintf(&err, "can't find an available bluetooth device");
- return err;
- } else if (*channel == 0) {
- // Find the first available channel.
- for (*channel = 1; *channel < kMaxChannel; (*channel)++) {
- if ((err = bt_bind(sock, local_address, channel)) != NULL) {
- free(err);
- continue;
- }
- return NULL;
- }
- asprintf(&err, "can't find an available bluetooth channel");
- return err;
- } else { // *local_address != NULL && *channel > 0
- addr.rc_family = AF_BLUETOOTH;
- str2ba(*local_address, &addr.rc_bdaddr);
- addr.rc_channel = (uint8_t) *channel;
- if (bind(sock, (struct sockaddr*) &addr, sizeof(addr)) < 0) {
- asprintf(&err, "can't bind to socket %d, addr %s, channel %d, error: %d[%s]",
- sock, *local_address, *channel, errno, strerror(errno));
- return err;
- }
- return NULL;
- }
-}
-
-char* bt_accept(int sock, int* fd, char** remote_address) {
- char* err = NULL;
- struct sockaddr_rc remote_addr;
- socklen_t opt = sizeof(remote_addr);
-
- if ((*fd = accept(sock, (struct sockaddr *)&remote_addr, &opt)) < 0) {
- asprintf(&err, "error accepting connection on socket %d, error: %d[%s]",
- sock, errno, strerror(errno));
- return err;
- }
- *remote_address = (char*) malloc(kMaxAddrStrSize * sizeof(char));
- ba2str(&remote_addr.rc_bdaddr, *remote_address);
-
- return NULL;
-}
-
-char* bt_connect(int sock, const char* remote_address, int remote_channel) {
- char* err = NULL;
- struct sockaddr_rc remote_addr = { 0 };
-
- remote_addr.rc_family = AF_BLUETOOTH;
- str2ba(remote_address, &remote_addr.rc_bdaddr);
- remote_addr.rc_channel = (uint8_t) remote_channel;
- if (connect(sock, (struct sockaddr*) &remote_addr, sizeof(remote_addr)) < 0) {
- asprintf(&err, "can't connect to remote address %s and channel %d "
- "on socket %d: %d[%s]", remote_address, remote_channel,
- sock, errno, strerror(errno));
- return err;
- }
- return NULL;
-}
-
-char* bt_close_device(int dd) {
- char* err = NULL;
- if (hci_close_dev(dd) < 0) {
- asprintf(&err, "can't close device with dd: %d, error: %d[%s]", dd, errno, strerror(errno));
- return err;
- }
- return NULL;
-}
-
-char* bt_enable_le_advertising(int dd, int enable) {
- char* err = NULL;
- struct hci_request req;
- le_set_advertise_enable_cp adv_enable_cp;
- uint8_t status;
-
- memset(&adv_enable_cp, 0, sizeof(adv_enable_cp));
- adv_enable_cp.enable = enable;
-
- memset(&req, 0, sizeof(req));
- req.ogf = OGF_LE_CTL;
- req.ocf = OCF_LE_SET_ADVERTISE_ENABLE;
- req.cparam = &adv_enable_cp;
- req.clen = LE_SET_ADVERTISE_ENABLE_CP_SIZE;
- req.rparam = &status;
- req.rlen = 1;
-
- if (hci_send_req(dd, &req, kTimeoutMs) < 0) {
- asprintf(&err,
- "can't enable/disable advertising for dd: %d, status: %d, error: %d",
- dd, status, errno);
- return err;
- }
- return NULL;
-}
-
-char* bt_start_le_advertising(int dd, int adv_interval_ms) {
- char* err = NULL;
- struct hci_request req;
- le_set_advertising_parameters_cp adv_params_cp;
- le_set_advertise_enable_cp adv_enable_cp;
- uint8_t status;
-
- // Set advertising params.
- memset(&adv_params_cp, 0, sizeof(adv_params_cp));
- adv_params_cp.min_interval = adv_interval_ms;
- adv_params_cp.max_interval = adv_interval_ms;
- adv_params_cp.advtype = 0x00; // Connectable undirected advertising.
- adv_params_cp.chan_map = 7;
-
- memset(&req, 0, sizeof(req));
- req.ogf = OGF_LE_CTL;
- req.ocf = OCF_LE_SET_ADVERTISING_PARAMETERS;
- req.cparam = &adv_params_cp;
- req.clen = LE_SET_ADVERTISING_PARAMETERS_CP_SIZE;
- req.rparam = &status;
- req.rlen = 1;
-
- if (hci_send_req(dd, &req, kTimeoutMs) < 0) {
- asprintf(&err,
- "can't set advertising params for dd: %d, status: %d, error: %d",
- dd, status, errno);
- return err;
- }
-
- // Start advertising.
- return bt_enable_le_advertising(dd, 1);
-}
-
-char* bt_set_le_advertising_payload(int dd, char* adv_payload) {
- char* err = NULL;
- int idx;
- struct hci_request req;
- le_set_advertising_data_cp adv_data_cp;
- uint8_t status;
-
- if (strlen(adv_payload) > kMaxLEPayloadSize) {
- asprintf(&err, "payload too big");
- return err;
- }
-
- // Set advertising data.
- memset(&adv_data_cp, 0, sizeof(adv_data_cp));
- idx = 0;
- adv_data_cp.data[idx++] = 2;
- adv_data_cp.data[idx++] = EIR_FLAGS;
- adv_data_cp.data[idx++] = 0x06; // general discoverable+BR/EDR Not Supported
- adv_data_cp.data[idx++] = strlen(adv_payload) + 1;
- adv_data_cp.data[idx++] = EIR_NAME_COMPLETE;
- memcpy(&adv_data_cp.data[idx], adv_payload, strlen(adv_payload));
- idx += strlen(adv_payload);
- adv_data_cp.length = idx;
-
- memset(&req, 0, sizeof(req));
- req.ogf = OGF_LE_CTL;
- req.ocf = OCF_LE_SET_ADVERTISING_DATA;
- req.cparam = &adv_data_cp;
- req.clen = LE_SET_ADVERTISING_DATA_CP_SIZE;
- req.rparam = &status;
- req.rlen = 1;
-
- if (hci_send_req(dd, &req, kTimeoutMs) < 0) {
- asprintf(&err,
- "can't set advertising data for dd: %d, status: %d, error: %d\n",
- dd, status, errno);
- return err;
- }
- return NULL;
-}
-
-char* bt_stop_le_advertising(int dd) {
- return bt_enable_le_advertising(dd, 0);
-}
-
-char* bt_parse_le_meta_event(
- void* data, char** remote_addr, char** remote_name, int* rssi, int* done) {
- char *err, *ptr, *end;
- evt_le_meta_event* meta_event;
- le_advertising_info* adv_info;
-
- *done = 0;
- meta_event = (evt_le_meta_event*) (data + 1 + HCI_EVENT_HDR_SIZE);
- if (meta_event->subevent == 0x01 /* LE Connection Complete Event */) {
- // This event is triggered when scan is disabled.
- *done = 1;
- return NULL;
- } else if (meta_event->subevent != 0x02 /* LE Advertising Report Event */) {
- asprintf(&err, "wrong event type: %d", meta_event->subevent);
- return err;
- }
-
- adv_info = (le_advertising_info*) (meta_event->data + 1);
- *remote_addr = malloc(kMaxAddrStrSize * sizeof(char));
- ba2str(&adv_info->bdaddr, *remote_addr);
- *rssi = *((int8_t*) adv_info->data + adv_info->length);
-
- // Extract name.
- // Max possible advertising data length, as defined by the standard.
- // The actual maximum name length was observed to be less than that, but
- // this value will do.
- const int kMaxNameLength = 31;
- *remote_name = malloc(kMaxNameLength * sizeof(char));
- memset(*remote_name, 0, kMaxNameLength);
- ptr = adv_info->data;
- end = adv_info->data + adv_info->length;
-
- // Go through all advertising data packets in the response.
- while (ptr < end) {
- int adv_data_length = *(ptr++); // includes adv_data_type below.
- int adv_data_type;
- if (adv_data_length == 0) { // end of response.
- break;
- } else if ((ptr + adv_data_length) > end) {
- // Illegal adv_data length.
- break;
- }
- adv_data_type = *(ptr++);
- switch (adv_data_type) {
- case EIR_NAME_SHORT:
- case EIR_NAME_COMPLETE:
- if (adv_data_length - 1 > kMaxNameLength) {
- // Illegal name length.
- break;
- }
- memcpy(*remote_name, ptr, adv_data_length - 1);
- break;
- }
- ptr += adv_data_length - 1;
- }
- return NULL;
-}
diff --git a/lib/bluetooth/bt_linux.h b/lib/bluetooth/bt_linux.h
deleted file mode 100644
index 2e215d9..0000000
--- a/lib/bluetooth/bt_linux.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// +build veyronbluetooth,!android
-
-#include <stdlib.h>
-
-// Maximum allowed LE payload size. See the Bluetooth 4.0 spec for more info on
-// Bluetooth LE payload structure:
-// https://www.bluetooth.org/en-us/specification/adopted-specifications
-const int kMaxLEPayloadSize;
-// The highest bluetooth channel that can be used for establishing RFCOMM
-// connections.
-const int kMaxChannel;
-
-// Maximum number of local devices to scan over when a particular device isn't
-// explicitly specified.
-const int kMaxDevices;
-
-// Opens the bluetooth device with the provided id, storing its device
-// descriptor into '*dd', its device name into '*name', and its MAC address
-// into '*local_address'.
-// Returns an error string if any error is encoutered. If a non-NULL error
-// string is returned, the caller must free it.
-// The caller must free '*name' and '*local_address' strings whenever a NULL
-// error string is returned.
-// REQUIRES: dev_id >= 0
-char* bt_open_device(int dev_id, int* dd, char** name, char** local_address);
-
-// Closes the (previously opened) device with the given device descriptor.
-// Returns error string on failure, or NULL otherwise. If a non-NULL error
-// string is returned, the caller must free it.
-char* bt_close_device(int dd);
-
-// Binds the given socket to the provided MAC address/channel. If '*local_address'
-// is NULL, it will bind to the first available bluetooth device and overwrite
-// '*local_address' to contain that device's MAC address. If '*channel' is zero,
-// it will bind to the first available channel on a given device and overwrite
-// '*channel' to contain that channel value. (If both of the above are true, we will
-// find the first device/channel pair that works and overwrite both values.)
-// Returns an error string if any error is encoutered. If a non-NULL error
-// string is returned, the caller must free it.
-// The caller must free '*local_address' string whenever a NULL value was passed
-// in and a NULL error string is returned.
-char* bt_bind(int sock, char** local_address, int* channel);
-
-// Accepts the next connection on the provided socket. Stores the file
-// descriptor for the newly established connection into 'fd', and the MAC
-// address of the remote party into 'remote_address". Returns an error string
-// if any error is encountered. If a non-NULL error string is returned, the
-// caller must free it.
-// The caller must free '*remote_address' string whenever a NULL error string
-// is returned.
-char* bt_accept(int sock, int* fd, char** remote_address);
-
-// Connects to the remote address/channel pair, using the provided local socket.
-// Returns an error string if any error is encountered. If a non-NULL error
-// string is returned, the caller must free it.
-char* bt_connect(int sock, const char* remote_address, int remote_channel);
-
-// Starts bluetooth LE advertising on the provided device descriptor, sending
-// one advertising packet every 'adv_interval_ms' milliseconds.
-// Returns error string on failure, or NULL otherwise. If a non-NULL error
-// string is returned, the caller must free it.
-char* bt_start_le_advertising(int dd, int adv_interval_ms);
-
-// Sets the advertising payload that is sent with each advertising packet.
-// This function may be called at any time to adjust the payload that is
-// currently being advertised.
-// Returns error string on failure, or NULL otherwise. If a non-NULL error
-// string is returned, the caller must free it.
-char* bt_set_le_advertising_payload(int dd, char* adv_payload);
-
-// Stops bluetooth LE advertising on the provided device descriptor.
-// Returns error string on failure, or NULL otherwise. If a non-NULL error
-// string is returned, the caller must free it.
-char* bt_stop_le_advertising(int dd);
-
-// Parses the LE meta event, extracting remote address, name, and RSSI. It also
-// checks whether the event is "LE Connection Complete Event", which indicates
-// that the scan has stopped, and writes the result of that check into 'done'.
-// Returns error if data cannot be parsed. If a non-NULL error string
-// is returned, the caller must free it.
-char* bt_parse_le_meta_event(
- void* data, char** remote_addr, char** remote_name, int* rssi, int* done);
diff --git a/lib/bluetooth/conn_linux.go b/lib/bluetooth/conn_linux.go
deleted file mode 100644
index 9b5a573..0000000
--- a/lib/bluetooth/conn_linux.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import (
- "fmt"
- "net"
- "syscall"
- "time"
-)
-
-// conn represents one RFCOMM connection between two bluetooth devices.
-// It implements the net.Conn interface.
-type conn struct {
- fd *fd
- localAddr, remoteAddr net.Addr
- readDeadline time.Time
- writeDeadline time.Time
-}
-
-func newConn(sockfd int, local, remote net.Addr) (net.Conn, error) {
- fd, err := newFD(sockfd)
- if err != nil {
- syscall.Close(sockfd)
- return nil, err
- }
- return &conn{fd: fd, localAddr: local, remoteAddr: remote}, nil
-}
-
-func (c *conn) String() string {
- return fmt.Sprintf("Bluetooth (%s) <--> (%s)", c.localAddr, c.remoteAddr)
-}
-
-// net.Conn interface methods
-func (c *conn) Read(p []byte) (n int, err error) { return c.fd.Read(p) }
-func (c *conn) Write(p []byte) (n int, err error) { return c.fd.Write(p) }
-func (c *conn) Close() error { return c.fd.Close() }
-func (c *conn) LocalAddr() net.Addr { return c.localAddr }
-func (c *conn) RemoteAddr() net.Addr { return c.remoteAddr }
-func (c *conn) SetReadDeadline(t time.Time) error { return c.setSockoptTimeval(t, syscall.SO_RCVTIMEO) }
-func (c *conn) SetWriteDeadline(t time.Time) error { return c.setSockoptTimeval(t, syscall.SO_SNDTIMEO) }
-func (c *conn) SetDeadline(t time.Time) error {
- if err := c.SetReadDeadline(t); err != nil {
- return err
- }
- if err := c.SetWriteDeadline(t); err != nil {
- return err
- }
- return nil
-}
-
-func (c *conn) setSockoptTimeval(t time.Time, opt int) error {
- fd, err := c.fd.Reference()
- if err != nil {
- return err
- }
- defer c.fd.ReleaseReference()
- if timeout := getTimeout(t); timeout != nil {
- return syscall.SetsockoptTimeval(fd, syscall.SOL_SOCKET, opt, timeout)
- }
- return nil
-}
-
-// getTimeout returns timeout for socket read/write operations, given the
-// deadline specified as absolute time. Return value nil indicates no timeout.
-// Return value 0 indicates that the read/write operation should timeout
-// immediately.
-func getTimeout(deadline time.Time) *syscall.Timeval {
- if deadline.IsZero() {
- return nil
- }
- d := deadline.Sub(time.Now())
- if d < 0 {
- ret := syscall.NsecToTimeval(0)
- return &ret
- }
- ret := syscall.NsecToTimeval(d.Nanoseconds())
- return &ret
-}
diff --git a/lib/bluetooth/conn_linux_test.go b/lib/bluetooth/conn_linux_test.go
deleted file mode 100644
index e6803e8..0000000
--- a/lib/bluetooth/conn_linux_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import (
- "runtime"
- "syscall"
- "testing"
-)
-
-// TestConnConcurrency attempts to tests that methods on the *conn type be
-// friendly to concurrent invocation. Unable to figure out a clean way to do
-// this, the author has resorted to just firing up a bunch of goroutines and
-// hoping that failures will manifest often.
-func TestConnConcurrency(t *testing.T) {
- const (
- // These numbers were tuned to make the test fail "often"
- // without the accompanying change to conn.go in the commit
- // that added this test on the machine that the author was
- // using at the time.
- nConcurrentReaders = 30
- nConcurrentClosers = 10
- )
- mp := runtime.GOMAXPROCS(nConcurrentReaders)
- defer runtime.GOMAXPROCS(mp)
-
- pipe := func() (rfd, wfd int) {
- var fds [2]int
- if err := syscall.Pipe(fds[:]); err != nil {
- t.Fatal(err)
- }
- return fds[0], fds[1]
- }
- rfd, wfd := pipe()
- rConn, _ := newConn(rfd, nil, nil)
- wConn, _ := newConn(wfd, nil, nil)
- const (
- bugs = "bugs bunny"
- daffy = "daffy duck"
- )
- rchan := make(chan string)
- // Write a bunch of times
- for i := 0; i < nConcurrentReaders; i++ {
- go wConn.Write([]byte(bugs))
- }
- read := func() {
- buf := make([]byte, len(bugs))
- if n, err := rConn.Read(buf); err == nil {
- rchan <- string(buf[:n])
- return
- }
- rchan <- ""
- }
- // Fire up half the readers before Close
- for i := 0; i < nConcurrentReaders; i += 2 {
- go read()
- }
- // Fire up the closers (and attempt to reassign the file descriptors to
- // something new).
- for i := 0; i < nConcurrentClosers; i++ {
- go func() {
- rConn.Close()
- // Create new FDs, which may re-use the closed file descriptors
- // and write something else to them.
- rfd, wfd := pipe()
- syscall.Write(wfd, []byte(daffy))
- syscall.Close(wfd)
- syscall.Close(rfd)
- }()
- }
- // And then the remaining readers
- for i := 1; i < nConcurrentReaders; i += 2 {
- go read()
- }
- // Now read from the channel, should either see full bugs bunnies or empty strings.
- nEmpty := 0
- for i := 0; i < nConcurrentReaders; i++ {
- got := <-rchan
- switch {
- case len(got) == 0:
- nEmpty++
- case got != bugs:
- t.Errorf("Read %q, wanted %q or empty string", got, bugs)
- }
- }
- t.Logf("Read returned non-empty %d/%d times", (nConcurrentReaders - nEmpty), nConcurrentReaders)
-}
diff --git a/lib/bluetooth/doc.go b/lib/bluetooth/doc.go
deleted file mode 100644
index b1d0622..0000000
--- a/lib/bluetooth/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package bluetooth provides methods for interacting with attached bluetooth
-// devices.
-package bluetooth
diff --git a/lib/bluetooth/fd_linux.go b/lib/bluetooth/fd_linux.go
deleted file mode 100644
index 57b6e27..0000000
--- a/lib/bluetooth/fd_linux.go
+++ /dev/null
@@ -1,223 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-// #include <stddef.h>
-// #include <sys/eventfd.h>
-// #include <sys/select.h>
-//
-// int add_to_eventfd(int fd) {
-// uint64_t val = 1;
-// return write(fd, &val, 8);
-// }
-//
-// int wait(int eventfd, int readfd, int writefd) {
-// fd_set readfds, writefds;
-// FD_ZERO(&readfds);
-// FD_ZERO(&writefds);
-// fd_set* writefdsp = NULL;
-//
-// FD_SET(eventfd, &readfds);
-// int nfds = eventfd + 1;
-//
-// if (readfd >= 0) {
-// FD_SET(readfd, &readfds);
-// if (readfd >= nfds) {
-// nfds = readfd + 1;
-// }
-// }
-// if (writefd >= 0) {
-// FD_SET(writefd, &writefds);
-// if (writefd >= nfds) {
-// nfds = writefd + 1;
-// }
-// writefdsp = &writefds;
-// }
-// // TODO(ashankar): Should EINTR be handled by a retry?
-// // See "Select Law" section of "man 2 select_tut".
-// int nready = select(nfds, &readfds, writefdsp, NULL, NULL);
-// if (nready < 0) {
-// return nready;
-// }
-// return (readfd >= 0 && FD_ISSET(readfd, &readfds)) ||
-// (writefd >= 0 && FD_ISSET(writefd, &writefds));
-// }
-import "C"
-
-import (
- "fmt"
- "io"
- "sync"
- "syscall"
-)
-
-// An fd enables concurrent invocations of Read, Write and Close on a file
-// descriptor.
-//
-// It ensures that read, write and close operations do not conflict and thereby
-// avoids races between file descriptors being closed and re-used while a
-// read/write is being initiated.
-//
-// This is achieved by using an eventfd to signal the intention to close a
-// descriptor and a select over the eventfd and the file descriptor being
-// protected.
-type fd struct {
- mu sync.Mutex
- datafd, eventfd C.int
- closing bool // Whether Close has been or is being invoked.
- done *sync.Cond // Signaled when no Read or Writes are pending.
- refcnt int
-}
-
-// newFD creates an fd object providing read, write and close operations
-// over datafd that are not hostile to concurrent invocations.
-func newFD(datafd int) (*fd, error) {
- eventfd, err := C.eventfd(0, C.EFD_CLOEXEC)
- if err != nil {
- return nil, fmt.Errorf("failed to create eventfd: %v", err)
- }
- ret := &fd{datafd: C.int(datafd), eventfd: eventfd}
- ret.done = sync.NewCond(&ret.mu)
- return ret, nil
-}
-
-func (fd *fd) Read(p []byte) (int, error) {
- e, d, err := fd.prepare()
- if err != nil {
- return 0, err
- }
- defer fd.finish()
- if err := wait(e, d, -1); err != nil {
- return 0, err
- }
- return fd.rw(syscall.Read(int(fd.datafd), p))
-}
-
-func (fd *fd) Write(p []byte) (int, error) {
- e, d, err := fd.prepare()
- if err != nil {
- return 0, err
- }
- defer fd.finish()
- if err := wait(e, -1, d); err != nil {
- return 0, err
- }
- return fd.rw(syscall.Write(int(fd.datafd), p))
-}
-
-// RunWhenReadable invokes f(file descriptor) when the file descriptor is ready
-// to be read. It returns an error if the file descriptor has been closed
-// either before or while this method is being invoked.
-//
-// f must NOT close readfd.
-func (fd *fd) RunWhenReadable(f func(readfd int)) error {
- e, d, err := fd.prepare()
- if err != nil {
- return err
- }
- defer fd.finish()
- if err := wait(e, d, -1); err != nil {
- return err
- }
- f(int(d))
- return nil
-}
-
-// Reference returns the underlying file descriptor and ensures that calls to
-// Close will block until ReleaseReference has been called.
-//
-// Clients must NOT close the returned file descriptor.
-func (fd *fd) Reference() (int, error) {
- fd.mu.Lock()
- defer fd.mu.Unlock()
- if fd.closing {
- return -1, fmt.Errorf("closing")
- }
- if fd.datafd < 0 {
- return -1, fmt.Errorf("closed")
- }
- fd.refcnt++
- return int(fd.datafd), nil
-}
-
-// ReleaseReference returns a reference to the file descriptor grabbed by a
-// call to Reference, thereby unblocking any Close operations.
-func (fd *fd) ReleaseReference() { fd.finish() }
-
-// helper method for Read and Write that ensures:
-// - the returned 'n' is always >= 0, as per guidelines for the io.Reader and
-// io.Writer interfaces.
-func (fd *fd) rw(n int, err error) (int, error) {
- if n == 0 && err == nil {
- err = io.EOF
- }
- if n < 0 {
- n = 0
- }
- return n, err
-}
-
-func (fd *fd) prepare() (eventfd, datafd C.int, err error) {
- fd.mu.Lock()
- defer fd.mu.Unlock()
- if fd.closing {
- return 0, 0, fmt.Errorf("closing")
- }
- fd.refcnt++
- // returned file descriptors are guaranteed to be
- // valid till refcnt is reduced by at least 1, since
- // Close waits for the refcnt to go down to zero before
- // closing these file descriptors.
- return fd.eventfd, fd.datafd, nil
-}
-
-func wait(eventfd, readfd, writefd C.int) error {
- ok, err := C.wait(eventfd, readfd, writefd)
- if err != nil {
- return err
- }
- if ok <= 0 {
- return fmt.Errorf("closing")
- }
- return nil
-}
-
-func (fd *fd) finish() {
- fd.mu.Lock()
- fd.refcnt--
- if fd.closing && fd.refcnt == 0 {
- fd.done.Broadcast()
- }
- fd.mu.Unlock()
-}
-
-func (fd *fd) Close() error {
- fd.mu.Lock()
- defer fd.mu.Unlock()
- if !fd.closing {
- // Send an "event" to notify of closures.
- if _, err := C.add_to_eventfd(fd.eventfd); err != nil {
- return fmt.Errorf("failed to notify closure on eventfd: %v", err)
- }
- // Prevent any new Read/Write/RunWhenReadable calls from starting.
- fd.closing = true
- }
- for fd.refcnt > 0 {
- fd.done.Wait()
- }
- // At this point, there are no concurrent Read/Write/RunWhenReadable
- // calls that are using the file descriptors.
- if fd.eventfd > 0 {
- if err := syscall.Close(int(fd.eventfd)); err != nil {
- return fmt.Errorf("failed to close eventfd: %v", err)
- }
- fd.eventfd = -1
- }
- if fd.datafd > 0 {
- if err := syscall.Close(int(fd.datafd)); err != nil {
- return fmt.Errorf("failed to close underlying socket/filedescriptor: %v", err)
- }
- fd.datafd = -1
- }
- return nil
-}
diff --git a/lib/bluetooth/fd_linux_test.go b/lib/bluetooth/fd_linux_test.go
deleted file mode 100644
index 7b4e359..0000000
--- a/lib/bluetooth/fd_linux_test.go
+++ /dev/null
@@ -1,151 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import (
- "fmt"
- "io"
- "sort"
- "syscall"
- "testing"
- "time"
-)
-
-// mkfds returns two *fds, one on which Read can be called and one on which
-// Write can be called by using the pipe system call. This pipe is a cheap
-// approximation of a file descriptor backed by a network socket that the fd type
-// is really intended for.
-func mkfds() (readfd, writefd *fd, err error) {
- var fds [2]int
- if err = syscall.Pipe(fds[:]); err != nil {
- err = fmt.Errorf("syscall.Pipe failed: %v", err)
- return
- }
- if readfd, err = newFD(fds[0]); err != nil {
- err = fmt.Errorf("newFD failed for readfd: %v", err)
- return
- }
- if writefd, err = newFD(fds[1]); err != nil {
- err = fmt.Errorf("newFD failed for writefd: %v", err)
- return
- }
- return
-}
-
-// canClose calls fd.Close and returns true if fd.Close returns.
-// It returns false if fd.Close blocks.
-// This function uses time to guess whether fd.Close is blocked or
-// not, and is thus not the most accurate implementation. The author
-// welcomes advice on restructuring this function or tests involving
-// it to make the testing deterministically accurate.
-func canClose(fd *fd) bool {
- c := make(chan error)
- go func() {
- c <- fd.Close()
- }()
- select {
- case <-c:
- return true
- case <-time.After(time.Millisecond):
- return false
- }
-}
-
-func TestFDBasic(t *testing.T) {
- rfd, wfd, err := mkfds()
- if err != nil {
- t.Fatal(err)
- }
- const batman = "batman"
- if n, err := wfd.Write([]byte(batman)); n != 6 || err != nil {
- t.Errorf("Got (%d, %v) want (6, nil)", n, err)
- }
- var read [1024]byte
- if n, err := rfd.Read(read[:]); n != 6 || err != nil || string(read[:n]) != string(batman) {
- t.Errorf("Got (%d, %v) = %q, want (6, nil) = %q", n, err, read[:n], batman)
- }
- if err := rfd.Close(); err != nil {
- t.Error(err)
- }
- if err := wfd.Close(); err != nil {
- t.Error(err)
- }
-}
-
-func TestFDReference(t *testing.T) {
- fd, _, err := mkfds()
- if err != nil {
- t.Fatal(err)
- }
- if _, err := fd.Reference(); err != nil {
- t.Fatal(err)
- }
- if canClose(fd) {
- t.Errorf("Should not be able to close fd since there is an outstanding reference")
- }
- fd.ReleaseReference()
- if !canClose(fd) {
- t.Errorf("Should be able to close fd since there are no outstanding references")
- }
-}
-
-func TestFDReadEOF(t *testing.T) {
- rfd, wfd, err := mkfds()
- if err != nil {
- t.Fatal(err)
- }
- const (
- bugs = "bugs"
- bunny = "bunny"
- )
- if n, err := wfd.Write([]byte(bugs)); n != len(bugs) || err != nil {
- t.Fatalf("Got (%d, %v) want (%d, nil)", n, err, len(bugs))
- }
- if n, err := wfd.Write([]byte(bunny)); n != len(bunny) || err != nil {
- t.Fatalf("Got (%d, %v) want (%d, nil)", n, err, len(bunny))
- }
- if err := wfd.Close(); err != nil {
- t.Fatal(err)
- }
- var read [1024]byte
- if n, err := rfd.Read(read[:]); n != len(bugs)+len(bunny) || err != nil {
- t.Errorf("Got (%d, %v) = %q, want (%d, nil) = %q", n, err, read[:n], len(bugs)+len(bunny), "bugsbunny")
- }
- if n, err := rfd.Read(read[:]); n != 0 || err != io.EOF {
- t.Errorf("Got (%d, %v) = %q, want (0, EOF)", n, err, read[:n])
- }
-}
-
-func TestFDReadLessThanReady(t *testing.T) {
- rfd, wfd, err := mkfds()
- if err != nil {
- t.Fatal(err)
- }
- const nbytes = 20
- rchan := make(chan int, nbytes)
- written := make([]byte, nbytes)
- for i := 1; i <= nbytes; i++ {
- written[i-1] = byte(i)
- go func() {
- var buf [1]byte
- rfd.Read(buf[:])
- rchan <- int(buf[0])
- }()
- }
- if n, err := wfd.Write(written); n != nbytes || err != nil {
- t.Fatal("Got (%d, %v), want (%d, nil)", n, err, nbytes)
- }
- if err := wfd.Close(); err != nil {
- t.Fatal(err)
- }
- read := make([]int, nbytes)
- for i := 0; i < nbytes; i++ {
- read[i] = <-rchan
- }
- sort.Ints(read)
- for i, v := range read {
- if i != v-1 {
- t.Fatalf("Got %v, wanted it to be sorted", read)
- }
- }
-}
diff --git a/lib/bluetooth/listener_linux.go b/lib/bluetooth/listener_linux.go
deleted file mode 100644
index c951baa..0000000
--- a/lib/bluetooth/listener_linux.go
+++ /dev/null
@@ -1,79 +0,0 @@
-// +build veyronbluetooth,!android
-
-package bluetooth
-
-import (
- "fmt"
- "net"
- "unsafe"
-)
-
-// // Explicitly link libbluetooth and other libraries as "go build" cannot
-// // figure out these dependencies..
-// #cgo LDFLAGS: -lbluetooth
-// #include <stdlib.h>
-// #include <unistd.h>
-// #include "bt_linux.h"
-import "C"
-
-// listener waits for incoming RFCOMM connections on the provided socket.
-// It implements the net.Listener interface.
-type listener struct {
- fd *fd
- acceptChan chan (acceptResult)
- localAddr net.Addr
-}
-
-type acceptResult struct {
- conn net.Conn
- err error
-}
-
-func newListener(sockfd int, addr net.Addr) (net.Listener, error) {
- fd, err := newFD(sockfd)
- if err != nil {
- return nil, err
- }
- return &listener{fd: fd, acceptChan: make(chan acceptResult), localAddr: addr}, nil
-}
-
-// Implements the net.Listener interface.
-func (l *listener) Accept() (net.Conn, error) {
- go l.fd.RunWhenReadable(l.accept)
- r := <-l.acceptChan
- return r.conn, r.err
-}
-
-func (l *listener) accept(sockfd int) {
- var fd C.int
- var remoteMAC *C.char
- var result acceptResult
- defer func() { l.acceptChan <- result }()
- if es := C.bt_accept(C.int(sockfd), &fd, &remoteMAC); es != nil {
- defer C.free(unsafe.Pointer(es))
- result.err = fmt.Errorf("error accepting connection on %s, socket: %d, error: %s", l.localAddr, sockfd, C.GoString(es))
- return
- }
- defer C.free(unsafe.Pointer(remoteMAC))
-
- // Parse remote address.
- var remote addr
- var err error
- if remote.mac, err = net.ParseMAC(C.GoString(remoteMAC)); err != nil {
- result.err = fmt.Errorf("invalid remote MAC address: %s, err: %s", C.GoString(remoteMAC), err)
- return
- }
- // There's no way to get accurate remote channel number, so use 0.
- remote.channel = 0
- result.conn, result.err = newConn(int(fd), l.localAddr, &remote)
-}
-
-// Implements the net.Listener interface.
-func (l *listener) Close() error {
- return l.fd.Close()
-}
-
-// Implements the net.Listener interface.
-func (l *listener) Addr() net.Addr {
- return l.localAddr
-}
diff --git a/runtimes/google/ipc/jni/arg_getter.go b/runtimes/google/ipc/jni/arg_getter.go
index ef24b5d..5eb718e 100644
--- a/runtimes/google/ipc/jni/arg_getter.go
+++ b/runtimes/google/ipc/jni/arg_getter.go
@@ -5,12 +5,14 @@
"path"
"reflect"
+ "veyron.io/proximity/api/services/proximity"
+
// Imported VDLs. Please add a link to all VDLs you care about here,
// and add all interfaces you care about to the init() function below.
"veyron/examples/fortune"
+
ctx "veyron2/context"
"veyron2/ipc"
- "veyron2/services/proximity"
)
func init() {
diff --git a/runtimes/google/ipc/stream/manager/manager.go b/runtimes/google/ipc/stream/manager/manager.go
index 48824f3..1993a0f 100644
--- a/runtimes/google/ipc/stream/manager/manager.go
+++ b/runtimes/google/ipc/stream/manager/manager.go
@@ -8,7 +8,8 @@
"strings"
"sync"
- "veyron/lib/bluetooth"
+ "veyron.io/proximity/lib/bluetooth"
+
"veyron/runtimes/google/ipc/stream/crypto"
"veyron/runtimes/google/ipc/stream/vif"
"veyron/runtimes/google/ipc/version"
diff --git a/services/gateway/gatewayd/main_linux.go b/services/gateway/gatewayd/main_linux.go
deleted file mode 100644
index 3025812..0000000
--- a/services/gateway/gatewayd/main_linux.go
+++ /dev/null
@@ -1,53 +0,0 @@
-// Binary gatewayd provides TCP/IP connectivity to devices that don't have it
-// by using their alternate connections (e.g., Bluetooth). This binary
-// should be started on both connected and non-connected devices and it will
-// take an appropriate role depending on that device's configuration (i.e., it
-// will take on either a connectivity-giving or a connectivity-taking role).
-package main
-
-import (
- "flag"
- "fmt"
- "os"
-
- "veyron/lib/signals"
-
- "veyron/services/gateway/lib"
- "veyron2/rt"
- "veyron2/vlog"
-)
-
-// TODO(spetrovic): Once the mounttable is finalized, get this endpoint by
-// traversing the device-wide mounttable, looking for the proximity service
-// name.
-var (
- proximity = flag.String("proximity", "", "Endpoint string for the proximity service, running on this device")
- forceClient = flag.Bool("force_client", false, "Force this device to act as a client, for testing purposes")
-)
-
-func main() {
- // TODO(cnicolaou): rt.Init also calls flag.Parse and has its own
- // flags etc. We need to somehow expose those here so that a complete
- // usage description can be provided by apps that have their own flags.
- flag.Parse()
- flag.Usage = func() {
- fmt.Fprintf(os.Stderr, "%s: gateway daemon that brings TCP/IP connectivity to devices using their alternate connections (e.g., Bluetooth).", os.Args[0])
- flag.PrintDefaults()
- }
-
- // Get the runtime.
- r := rt.Init()
- defer r.Cleanup()
-
- // Create a new instance of the gateway service.
- vlog.Info("Connecting to proximity service: ", *proximity)
- g, err := gateway.New(*proximity, *forceClient)
- if err != nil {
- vlog.Errorf("couldn't create gateway service: %v", err)
- return
- }
- defer g.Stop()
-
- // Block until the process gets terminated (either by AppMgr or system).
- <-signals.ShutdownOnSignals()
-}
diff --git a/services/gateway/lib/client_linux.go b/services/gateway/lib/client_linux.go
deleted file mode 100644
index a70ffd1..0000000
--- a/services/gateway/lib/client_linux.go
+++ /dev/null
@@ -1,287 +0,0 @@
-package gateway
-
-import (
- "fmt"
- "math"
- "net"
- "os/exec"
- "strings"
- "time"
-
- "veyron/lib/unit"
- "veyron2/rt"
- "veyron2/services/proximity"
- "veyron2/vlog"
-
- dbus "github.com/guelfey/go.dbus"
-)
-
-// Bluetooth service UUID string for the NAP service.
-const napUUID = "0x1116"
-
-type device struct {
- mac net.HardwareAddr
- distance unit.Distance
- rtt time.Duration
-}
-
-func (d *device) String() string {
- return d.mac.String()
-}
-
-// affinity returns a value in the range [0, inf), indicating how beneficial
-// this device would be as a gateway. When choosing gateways, smaller affinity
-// values are preferred to larger ones.
-func (d *device) affinity() float64 {
- if d.rtt.Seconds() < 0 || d.distance.Millimeters() < 0 {
- return math.Inf(1)
- }
- if d.distance.Millimeters() == 0 {
- return 0
- }
- return d.rtt.Seconds() / d.distance.Millimeters()
-}
-
-type client struct {
- proximity proximity.Proximity
- conn *dbus.Conn
- done chan bool
-}
-
-func newClient(p proximity.Proximity) (*client, error) {
- c := &client{
- proximity: p,
- done: make(chan bool),
- }
-
- // Connect to DBus and export a bluetooth agent.
- var err error
- if c.conn, err = dbus.SystemBus(); err != nil {
- return nil, fmt.Errorf("couldn't access system dbus: %v", err)
- }
- if err = c.conn.Export(&yesAgent{}, agentPath, agentIface); err != nil {
- return nil, fmt.Errorf("error exporting agent to dbus: %v", err)
- }
-
- // Start background loops.
- go c.updateLoop()
-
- return c, nil
-}
-
-func (c *client) Stop() {
- c.conn.Export(nil, agentPath, agentIface)
- close(c.done)
-}
-
-// updateLoop obtains a list of nearby devices from proximity service
-// and updates the local gateway, if needed.
-func (c *client) updateLoop() {
- defer vlog.VI(1).Infof("device update loop exiting.")
- t := time.Tick(1 * time.Second)
- var gateway *net.HardwareAddr
- var gatewayIface string
- for {
- select {
- case <-c.done:
- return
- case <-t:
- }
-
- nearby, err := c.proximity.NearbyDevices(rt.R().TODOContext())
- if err != nil {
- vlog.Errorf("error getting nearby devices list: %v", err)
- continue
- }
-
- // Find the device with the smallest affinity.
- var d, g *device
- min := math.Inf(1)
- for _, n := range nearby {
- mac, err := net.ParseMAC(n.MAC)
- if err != nil {
- vlog.Errorf("error parsing MAC address %q: %v", n.MAC, err)
- continue
- }
- distance, err := unit.ParseDistance(n.Distance)
- if err != nil {
- vlog.Errorf("error parsing distance %q: %v", n.Distance, err)
- continue
- }
- rtt, found := rttFromNames(n.Names)
- if !found {
- continue
- }
- dev := &device{
- mac: mac,
- distance: distance,
- rtt: rtt,
- }
- if dev.affinity() < min {
- d = dev
- min = dev.affinity()
- }
- if gateway != nil && gateway.String() == dev.mac.String() {
- g = dev
- }
- }
-
- if shouldUpdateGateway(g, d) {
- vlog.VI(1).Infof("connecting new gateway %q", d.mac)
- if iface, err := c.connectDevice(d.mac); err == nil {
- vlog.VI(1).Infof("connected")
- if gateway != nil {
- vlog.VI(1).Infof("disconnecting (old) gateway %q", *gateway)
- c.disconnectDevice(*gateway)
- vlog.VI(1).Infof("disconnected")
- }
- gateway = &d.mac
- gatewayIface = iface
- } else {
- vlog.Errorf("couldn't connect to new gateway %q: %v", d, err)
- }
- }
-
- // Test if the gateway is (still) functional and if not
- // disconnect it.
- if len(gatewayIface) > 0 && !testInterface(gatewayIface) {
- // Gateway not functional - disconnect it.
- vlog.VI(1).Infof("disconnecting non-functional gateway %q", *gateway)
- c.disconnectDevice(*gateway)
- gateway = nil
- gatewayIface = ""
- }
- }
-}
-
-func (c *client) connectDevice(mac net.HardwareAddr) (string, error) {
- // Find the local bluetooth adapter.
- obj := c.conn.Object("org.bluez", dbus.ObjectPath("/"))
- var adapterPath dbus.ObjectPath
- if err := obj.Call("org.bluez.Manager.DefaultAdapter", 0).Store(&adapterPath); err != nil {
- return "", fmt.Errorf("couldn't find bluetooth adapter: %v", err)
- }
- adapter := c.conn.Object("org.bluez", adapterPath)
-
- // Create a new local device object corresponding to the remote device.
- var devicePath dbus.ObjectPath
- if err := adapter.Call("org.bluez.Adapter.FindDevice", 0, mac.String()).Store(&devicePath); err != nil {
- // Local device object doesn't exist - create it.
- if err := adapter.Call("org.bluez.Adapter.CreateDevice", 0, mac.String()).Store(&devicePath); err != nil {
- return "", fmt.Errorf("couldn't create local device object %q: %v", mac, err)
- }
- }
- // Re-discover the Network service for the remote device. This will
- // ensure that we have local bindings for the NAP-connect call below.
- dev := c.conn.Object("org.bluez", devicePath)
- if call := dev.Call("org.bluez.Device.DiscoverServices", 0, napUUID); call.Err != nil {
- vlog.Errorf("couldn't (re)discover Network service for device %q: %v", mac, call.Err)
- }
-
- // Pair with the device. We ignore any errors because an already-paired
- // device returns it as well. (Should the pairing legitimately fail,
- // the NAP-connect method below will fail.)
- adapter.Call("org.bluez.Adapter.CreatePairedDevice", 0, mac.String(), agentPath, "NoInputNoOutput")
-
- // Disconnect the device for good measure, in case we are connected but
- // don't know it. (This can happen if, for example, the server
- // terminated the connection and it didn't get cleaned up correctly.)
- c.disconnectDevice(mac)
-
- // Establish (a new) NAP connection to the device.
- var iface string
- if err := dev.Call("org.bluez.Network.Connect", 0).Store(&iface); err != nil {
- return "", fmt.Errorf("couldn't establish NAP connection to device %q: %v", mac, err)
- }
-
- // Start DHCP client.
- cmd := exec.Command("dhcpcd", "-4", "-C", "resolv.conf", "-C", "mtu", iface)
- if err := cmd.Run(); err != nil {
- c.disconnectDevice(mac)
- return "", fmt.Errorf("couldn't start dhcpcd for device %q: %v", mac, err)
- }
-
- return iface, nil
-}
-
-func (c *client) disconnectDevice(mac net.HardwareAddr) {
- obj := c.conn.Object("org.bluez", dbus.ObjectPath("/"))
- var adapterPath dbus.ObjectPath
- if err := obj.Call("org.bluez.Manager.DefaultAdapter", 0).Store(&adapterPath); err != nil {
- return
- }
- adapter := c.conn.Object("org.bluez", adapterPath)
-
- var devicePath dbus.ObjectPath
- if err := adapter.Call("org.bluez.Adapter.FindDevice", 0, mac.String()).Store(&devicePath); err != nil {
- return
- }
-
- device := c.conn.Object("org.bluez", devicePath)
- device.Call("org.bluez.Network.Disconnect", 0)
-}
-
-func shouldUpdateGateway(curg, newg *device) bool {
- if newg == nil {
- return false
- }
- if curg == nil {
- return true
- }
- return curg.mac.String() != newg.mac.String() && curg.affinity() > 4*newg.affinity()
-}
-
-// testInterface tests that the provided interface has an assigned IP address
-// and is functional.
-func testInterface(iface string) bool {
- vlog.VI(1).Infof("Testing interface %v", iface)
- ip, err := gatewayIP(iface)
- if err != nil {
- vlog.VI(1).Infof("couldn't get gateway IP for iface %q: %v", iface, err)
- return false
- }
-
- rtt, err := ping(ip)
- if err != nil || len(rtt) == 0 {
- vlog.VI(1).Infof("interface %q has no connectivity", iface)
- return false
- }
- return true
-}
-
-// gatewayIP finds the address on "the other side" of the provided interface.
-func gatewayIP(iface string) (string, error) {
- i, err := net.InterfaceByName(iface)
- if err != nil {
- return "", fmt.Errorf("couldn't find interface %q: %v", iface, err)
- }
- addrs, err := i.Addrs()
- if err != nil {
- return "", fmt.Errorf("couldn't get addresses for interface %q: %v", iface, err)
- }
- if len(addrs) == 0 {
- return "", fmt.Errorf("interface %q has no addresses", iface)
- }
- var ip net.IP
- for _, addr := range addrs {
- if ipa, ok := addr.(*net.IPAddr); ok {
- if ip4 := ipa.IP.To4(); ip4 != nil {
- ip = ip4
- break
- }
- }
- }
- if ip == nil {
- return "", fmt.Errorf("no IP4 address for interface %q", iface)
- }
- return strings.Join(append(strings.Split(ip.String(), ".")[:3], "1"), "."), nil
-}
-
-func rttFromNames(names []string) (time.Duration, bool) {
- for _, name := range names {
- if rtt, err := rttFromName(name); err == nil {
- return rtt, true
- }
- }
- return 0, false
-}
diff --git a/services/gateway/lib/doc_linux.go b/services/gateway/lib/doc_linux.go
deleted file mode 100644
index b9d9001..0000000
--- a/services/gateway/lib/doc_linux.go
+++ /dev/null
@@ -1,23 +0,0 @@
-// Package gateway implements client and server methods that (1) allow a device
-// to act as an IP gateway and (2) allow devices without an IP connection to
-// obtain it via a gateway device.
-//
-// The main gateway functionality is achieved through the Bluetooth BNEP
-// protocol. This protocol allows a virtual Ethernet connection to be
-// established between multiple Bluetooth devices. We plugin the remaining
-// functionality (IP forwarding, DHCP) using standard Linux tools (e.g.,
-// route/dhcpd). The current implementation is therefore platform-specific and
-// for now only supported on Linux.
-//
-// For the Bluetooth stack, we use Bluez 4.101 library along with standard
-// linux Bluetooth drivers. As Bluez libraries go through extensive
-// non-backward-compatible changes, we require that this exact library version
-// is installed on target systems. We use DBus to communicate with the Bluez
-// library.
-//
-// To configure the IP stack we use a number of standard Linux tools, namely:
-// awk, grep, cut, route, dhcpcd, and dhcpd. These tools must be installed on
-// target systems and must be present in system's $PATH environmental variable.
-// We also rely on IP4 addressing and must revisit the implementation should
-// IPv6 become the only standard.
-package gateway
diff --git a/services/gateway/lib/gateway_linux.go b/services/gateway/lib/gateway_linux.go
deleted file mode 100644
index d7700e2..0000000
--- a/services/gateway/lib/gateway_linux.go
+++ /dev/null
@@ -1,74 +0,0 @@
-package gateway
-
-import (
- "fmt"
-
- "veyron/lib/bluetooth"
- "veyron2/services/proximity"
- "veyron2/vlog"
-)
-
-var gatewayCmd = [][]string{
- {"route"},
- // TODO(spetrovic): Use Go's string libraries to reduce
- // dependence on these tools.
- {"grep", "UG[ \t]"},
- {"grep", "^default"},
- {"head", "-1"},
- {"awk", `{printf "%s", $NF}`},
-}
-
-// New creates a new instance of the gateway service given the name of the
-// local proximity service. Since the gateway server operates in two modes
-// (i.e., server and client) depending on whether it provides or obtains
-// internet connectivity, the provided boolean option lets us force a client
-// mode, which can be useful for testing.
-func New(proximityService string, forceClient bool) (*Service, error) {
- // See if we have bluetooth on this device.
- d, err := bluetooth.OpenFirstAvailableDevice()
- if err != nil {
- return nil, fmt.Errorf("no bluetooth devices found: %v", err)
- }
-
- // Find the default gateway interface.
- gateway, err := runPipedCmd(gatewayCmd)
- if err != nil {
- gateway = ""
- }
-
- p, err := proximity.BindProximity(proximityService)
- if err != nil {
- return nil, fmt.Errorf("error connecting to proximity service %q: %v", proximityService, err)
- }
-
- // If gateway is present, start the server; otherwise, start the client.
- s := new(Service)
- if gateway == "" || forceClient {
- vlog.Info("No IP interfaces detected: starting the gateway client.")
- var err error
- if s.client, err = newClient(p); err != nil {
- return nil, fmt.Errorf("couldn't start gateway client: %v", err)
- }
- } else {
- vlog.Infof("IP interface %q detected: starting the gateway server.", gateway)
- if s.server, err = newServer(p, gateway, d.Name); err != nil {
- return nil, fmt.Errorf("couldn't start gateway server: %v", err)
- }
- }
- return s, nil
-}
-
-type Service struct {
- client *client
- server *server
-}
-
-// Stop stops the gateway service.
-func (s *Service) Stop() {
- if s.client != nil {
- s.client.Stop()
- }
- if s.server != nil {
- s.server.Stop()
- }
-}
diff --git a/services/gateway/lib/server_linux.go b/services/gateway/lib/server_linux.go
deleted file mode 100644
index a295647..0000000
--- a/services/gateway/lib/server_linux.go
+++ /dev/null
@@ -1,336 +0,0 @@
-package gateway
-
-import (
- "errors"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "strconv"
- "text/template"
- "time"
-
- "veyron2/rt"
- "veyron2/services/proximity"
- "veyron2/vlog"
-
- dbus "github.com/guelfey/go.dbus"
-)
-
-const (
- ipFwdFile = "/proc/sys/net/ipv4/ip_forward"
- pingTarget = "www.google.com"
- bridgeName = "napbr0"
- // TODO(spetrovic): Instead of statically assigned addresses, scan
- // over all existing interfaces and find the one that's surely unused.
- bridgeIPAddr = "192.168.9.1"
- bridgeIPSubnetShort = "192.168.9.0"
- bridgeIPSubnetLong = "192.168.9.0/24"
- bridgeIPMask = "255.255.255.0"
- bridgeIPBroadcast = "192.168.9.255"
- bridgeDHCPStart = "192.168.9.2"
- bridgeDHCPEnd = "192.168.9.254"
- dhcpdTemplateStr = `
-# dhcpd config for the NAP bridge interface {{.Bridge}}
-#
-# {{.Bridge}} ip addr is expected to be {{.IPAddr}}
-# serves {{.IPAddrStart}} - {{.IPAddrEnd}}
-
-authoritative;
-
-subnet {{.Subnet}} netmask {{.Netmask}} {
- interface "{{.Bridge}}";
- range {{.IPAddrStart}} {{.IPAddrEnd}};
- option routers {{.IPAddr}};
- option broadcast-address {{.Broadcast}};
-}`
- bridgeUpTemplateStr = `
-#!/bin/bash
-# Script that creates and configures the bridge interface.
-
-brctl addbr {{.Bridge}}
-ifconfig {{.Bridge}} inet {{.BridgeAddr}} netmask {{.BridgeMask}} up
-`
- bridgeDownTemplateStr = `
-#!/bin/bash
-# Script that creates and configures the bridge interface.
-
-ifconfig {{.Bridge}} down
-brctl delbr {{.Bridge}}
-`
- natStartTemplateStr = `
-#!/bin/bash
-# Script that turns this server's NAT role on.
-cp -f {{.IPFwdFile}} {{.IPFwdFile}}.old
-echo "1" > {{.IPFwdFile}}
-iptables -t nat -A POSTROUTING -s {{.BridgeSubnet}} -j MASQUERADE
-iptables -A FORWARD -i {{.Bridge}} -o {{.Gateway}} -j ACCEPT
-iptables -A FORWARD -o {{.Bridge}} -i {{.Gateway}} -j ACCEPT
-`
- natStopTemplateStr = `
-#!/bin/bash
-# Script that turns this server's NAT role off.
-cp -f {{.IPFwdFile}}.old {{.IPFwdFile}}
-iptables -t nat -D POSTROUTING -s {{.BridgeSubnet}} -j MASQUERADE
-iptables -D FORWARD -i {{.Bridge}} -o {{.Gateway}} -j ACCEPT
-iptables -D FORWARD -o {{.Bridge}} -i {{.Gateway}} -j ACCEPT
-`
-)
-
-var (
- dhcpdConfig string
- dhcpdTemplate = template.Must(template.New("dhcpd").Parse(dhcpdTemplateStr))
- bridgeUpTemplate = template.Must(template.New("bridgeUp").Parse(bridgeUpTemplateStr))
- bridgeDownTemplate = template.Must(template.New("bridgeDown").Parse(bridgeDownTemplateStr))
- natStartTemplate = template.Must(template.New("natStart").Parse(natStartTemplateStr))
- natStopTemplate = template.Must(template.New("natStop").Parse(natStopTemplateStr))
-)
-
-func init() {
- d := struct {
- Bridge, IPAddr, IPAddrStart, IPAddrEnd, Subnet, Netmask, Broadcast string
- }{
- bridgeName, bridgeIPAddr, bridgeDHCPStart, bridgeDHCPEnd, bridgeIPSubnetShort, bridgeIPMask, bridgeIPBroadcast,
- }
- var err error
- if dhcpdConfig, err = fileFromTemplate(dhcpdTemplate, d, 0644); err != nil {
- panic(err)
- }
-}
-
-func newServer(p proximity.Proximity, gateway, device string) (*server, error) {
- s := &server{
- proximity: p,
- gateway: gateway,
- device: device,
- done: make(chan bool),
- }
- err := s.configureBluetooth()
- if err == nil {
- err = s.bridgeUp()
- }
- if err == nil {
- err = s.startNAT()
- }
- if err == nil {
- err = s.startDHCP()
- }
- if err == nil {
- err = s.startNAP()
- }
- if err != nil {
- s.Stop()
- return nil, err
- }
- go s.rttLoop()
- return s, nil
-}
-
-type server struct {
- proximity proximity.Proximity
- gateway, device string
- dhcpCmd *exec.Cmd
- oldFwdVal []byte
- conn *dbus.Conn
- done chan bool
- advertisedName string
-}
-
-func (s *server) Stop() {
- s.stopNAP()
- s.stopDHCP()
- s.stopNAT()
- s.bridgeDown()
- close(s.done) // terminates rttLoop
-}
-
-func (s *server) bridgeUp() error {
- d := struct {
- Bridge, BridgeAddr, BridgeMask string
- }{
- bridgeName, bridgeIPAddr, bridgeIPMask,
- }
- if err := runScript(bridgeUpTemplate, d); err != nil {
- return fmt.Errorf("couldn't create bridge: %v", err)
- }
- return nil
-}
-
-func (s *server) bridgeDown() {
- d := struct {
- Bridge string
- }{
- bridgeName,
- }
- runScript(bridgeDownTemplate, d)
-}
-
-func (s *server) startNAT() error {
- d := struct {
- IPFwdFile, Bridge, Gateway, BridgeSubnet string
- }{
- ipFwdFile, bridgeName, s.gateway, bridgeIPSubnetLong,
- }
- if err := runScript(natStartTemplate, d); err != nil {
- return fmt.Errorf("couldn't start NAT: %v", err)
- }
- return nil
-}
-
-func (s *server) stopNAT() {
- d := struct {
- IPFwdFile, Bridge, Gateway, BridgeSubnet string
- }{
- ipFwdFile, bridgeName, s.gateway, bridgeIPSubnetLong,
- }
- runScript(natStopTemplate, d)
-}
-
-func (s *server) startDHCP() error {
- s.dhcpCmd = exec.Command("dhcpd", "-cf", dhcpdConfig, "-f", bridgeName)
- if err := s.dhcpCmd.Start(); err != nil {
- return fmt.Errorf("couldn't start dhcpd on bridge %q: %v", bridgeName, err)
- }
- return nil
-}
-
-func (s *server) stopDHCP() {
- if s.dhcpCmd == nil || s.dhcpCmd.Process == nil {
- // dhcpd not started?
- return
- }
- s.dhcpCmd.Process.Kill()
-}
-
-func (s *server) configureBluetooth() error {
- if err := exec.Command("hciconfig", s.device, "piscan").Run(); err != nil {
- return fmt.Errorf("couldn't make Bluetooth device %q discoverable: %v", s.device, err)
- }
- return nil
-}
-
-func (s *server) startNAP() error {
- var err error
- if s.conn, err = dbus.SystemBus(); err != nil {
- return fmt.Errorf("couldn't access system dbus: %v", err)
- }
- // Get the adapter.
- obj := s.conn.Object("org.bluez", dbus.ObjectPath("/"))
- var adapterPath dbus.ObjectPath
- if err := obj.Call("org.bluez.Manager.DefaultAdapter", 0).Store(&adapterPath); err != nil {
- return fmt.Errorf("couldn't find device %q: %v", s.device, err)
- }
- adapter := s.conn.Object("org.bluez", adapterPath)
- // Add a bluetooth agent.
- agent := &yesAgent{}
- if err = s.conn.Export(agent, agentPath, agentIface); err != nil {
- return fmt.Errorf("error exporting agent to dbus: %v", err)
- }
- if call := adapter.Call("org.bluez.Adapter.RegisterAgent", 0, agentPath, "NoInputNoOutput"); call.Err != nil {
- return fmt.Errorf("couldn't register agent: %v", call.Err)
- }
- // Register the NAP server.
- if call := adapter.Call("org.bluez.NetworkServer.Register", 0, "nap", bridgeName); call.Err != nil {
- return fmt.Errorf("couldn't start NAP server: %v", call.Err)
- }
- return nil
-}
-
-func (s *server) stopNAP() {
- if s.conn == nil {
- return
- }
- // Unregister the NAP server.
- obj := s.conn.Object("org.bluez", dbus.ObjectPath(fmt.Sprintf("/org/bluez/%s", s.device)))
- obj.Call("org.bluez.NetworkServer.Unegister", 0, "nap")
-
- // Remove the bluetooth agent.
- obj.Call("org.bluez.Adapter.UnregisterAgent", 0, agentPath)
- s.conn.Export(nil, agentPath, agentIface)
-}
-
-func (s *server) rttLoop() {
- defer vlog.VI(1).Info("gateway server's RTT loop exiting")
- for {
- select {
- case <-s.done:
- return
- case <-time.After(1 * time.Second):
- }
-
- rtt, err := rtt()
- if err != nil {
- vlog.Errorf("error getting RTT: %v", err)
- s.advertiseName("")
- } else {
- s.advertiseName(nameFromRTT(rtt))
- }
- }
-}
-
-func (s *server) advertiseName(name string) {
- if s.advertisedName != "" {
- if err := s.proximity.UnregisterName(rt.R().TODOContext(), s.advertisedName); err != nil {
- vlog.Errorf("error unregistering name %q with proximity service: %v", s.advertisedName, err)
- }
- }
- s.advertisedName = ""
- if name != "" {
- if err := s.proximity.RegisterName(rt.R().TODOContext(), name); err == nil {
- s.advertisedName = name
- } else {
- vlog.Errorf("error registering name %q with proximity service: %v", name, err)
- }
- }
-}
-
-func runScript(t *template.Template, data interface{}) error {
- fname, err := fileFromTemplate(t, data, 0777)
- if err != nil {
- return fmt.Errorf("couldn't create template file: %v", err)
- }
- defer os.Remove(fname)
- if err := exec.Command("bash", fname).Run(); err != nil {
- return fmt.Errorf("couldn't execute script %q: %v", fname, err)
- }
- return nil
-}
-
-func fileFromTemplate(t *template.Template, data interface{}, perm os.FileMode) (string, error) {
- // Create temp file and set the right permissions on it.
- f, err := ioutil.TempFile("", "tmp")
- if err != nil {
- return "", fmt.Errorf("couldn't create temp file: %v", err)
- }
- if err := os.Chmod(f.Name(), perm); err != nil {
- f.Close()
- os.Remove(f.Name())
- return "", fmt.Errorf("couldn't change permissions on file %q to %v: err", f.Name(), perm, err)
- }
-
- // Execute the template and write it out into the temp file.
- if err := t.Execute(f, data); err != nil {
- f.Close()
- os.Remove(f.Name())
- return "", err
- }
- return f.Name(), nil
-}
-
-func rtt() (time.Duration, error) {
- v, err := ping(pingTarget)
- if err != nil {
- return 0, fmt.Errorf("error executing RTT command: %v", err)
- }
- if len(v) == 0 {
- return 0, errors.New("got empty RTT value")
- }
- f, err := strconv.ParseFloat(v, 64)
- if err != nil {
- return 0, fmt.Errorf("error parsing RTT value %q: %v", v, err)
- }
- if f < 0 {
- return 0, fmt.Errorf("negative RTT value: %f", f)
- }
- return time.Duration(int(f+0.5)) * time.Millisecond, nil
-}
diff --git a/services/gateway/lib/util_linux.go b/services/gateway/lib/util_linux.go
deleted file mode 100644
index 591fef4..0000000
--- a/services/gateway/lib/util_linux.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package gateway
-
-import (
- "bytes"
- "fmt"
- "os/exec"
- "strings"
- "time"
- "veyron2/vlog"
-
- dbus "github.com/guelfey/go.dbus"
-)
-
-const (
- agentPath = dbus.ObjectPath("/org/veyron/internal/lib/gateway/yesagent")
- agentIface = "org.bluez.Agent"
-)
-
-var pingCmd = [][]string{
- {"ping", "-i", "0.1", "-c", "3", "-w", "1.0", "-q"},
- // TODO(spetrovic): Use Go's string libraries to reduce
- // dependence on these tools.
- {"grep", "rtt"},
- {"cut", "-d", " ", "-f", "4"},
- {"cut", "-d", "/", "-f", "2"},
- {"awk", `{printf "%s", $NF}`},
-}
-
-func runPipedCmd(cmd [][]string) (string, error) {
- if len(cmd) == 0 {
- return "", fmt.Errorf("invalid command %q", cmd)
- }
- for _, c := range cmd {
- if len(c) == 0 || len(c[0]) == 0 {
- return "", fmt.Errorf("invalid command %q", cmd)
- }
- }
- cs := make([]*exec.Cmd, len(cmd))
- for i, c := range cmd {
- cs[i] = exec.Command(c[0], c[1:]...)
- }
- for i := 1; i < len(cs); i++ {
- var err error
- if cs[i].Stdin, err = cs[i-1].StdoutPipe(); err != nil {
- return "", fmt.Errorf("piping error: %v", err)
- }
- }
- var out bytes.Buffer
- cs[len(cs)-1].Stdout = &out
- for _, c := range cs {
- if err := c.Start(); err != nil {
- return "", fmt.Errorf("error starting command %q: %q", c, err)
- }
- }
- for _, c := range cs {
- if err := c.Wait(); err != nil {
- return "", fmt.Errorf("error executing command %q: %q", c, err)
- }
- }
- return out.String(), nil
-}
-
-func ping(target string) (string, error) {
- cmd := make([][]string, len(pingCmd))
- copy(cmd, pingCmd)
- cmd[0] = make([]string, len(pingCmd[0]))
- copy(cmd[0], pingCmd[0])
- cmd[0] = append(cmd[0], target)
- return runPipedCmd(cmd)
-}
-
-func nameFromRTT(rtt time.Duration) string {
- return fmt.Sprintf("NAP(%v)", rtt)
-}
-
-func rttFromName(name string) (time.Duration, error) {
- if !strings.HasPrefix(name, "NAP(") || !strings.HasSuffix(name, ")") {
- return 0, fmt.Errorf("invalid string %q", name)
- }
- d, err := time.ParseDuration(name[4 : len(name)-1])
- if err != nil {
- return 0, fmt.Errorf("couldn't parse RTT in name %q: %v", name, err)
- }
- return d, nil
-}
-
-// yesAgent implements the org.bluez.Agent DBus interface, allowing two devices
-// to connect without any security. This DBus interface is compatible
-// with bluez-4.101 package.
-type yesAgent struct{}
-
-func (*yesAgent) Release() *dbus.Error {
- vlog.VI(1).Info("Release() called")
- return nil
-}
-
-func (*yesAgent) RequestPinCode(device dbus.ObjectPath) (string, *dbus.Error) {
- vlog.VI(1).Info("RequestPinCode(%q) called", device)
- return "1234", nil
-}
-
-func (*yesAgent) RequestPasskey(device dbus.ObjectPath) (uint32, *dbus.Error) {
- vlog.VI(1).Info("RequestPasskey(%q) called", device)
- return 123456, nil
-}
-
-func (*yesAgent) DisplayPasskey(device dbus.ObjectPath, passkey uint32, entered uint8) *dbus.Error {
- vlog.VI(1).Info("DisplayPasskey(%q, %d, %d) called", device, passkey, entered)
- return nil
-}
-
-func (*yesAgent) DisplayPinCode(device dbus.ObjectPath, pincode string) *dbus.Error {
- vlog.VI(1).Info("DisplayPinCode(%q, %q) called", device, pincode)
- return nil
-}
-
-func (*yesAgent) RequestConfirmation(device dbus.ObjectPath, passkey uint32) *dbus.Error {
- vlog.VI(1).Info("RequestConfirmation(%q, %d) called", device, passkey)
- return nil
-}
-
-func (*yesAgent) Authorize(device dbus.ObjectPath, uuid string) *dbus.Error {
- vlog.VI(1).Info("AuthorizeService(%q, %q) called", device, uuid)
- return nil
-}
-
-func (*yesAgent) ConfirmModeChange(mode string) *dbus.Error {
- vlog.VI(1).Info("ConfirmModeChange(%q) called", mode)
- return nil
-}
-
-func (*yesAgent) Cancel() *dbus.Error {
- vlog.VI(1).Info("Cancel() called")
- return nil
-}
diff --git a/services/proximity/lib/advertiser.go b/services/proximity/lib/advertiser.go
deleted file mode 100644
index d46a7d8..0000000
--- a/services/proximity/lib/advertiser.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package proximity
-
-import "time"
-
-// Advertiser denotes a (local) entity that is capable of advertising small
-// payloads to neighboring devices (e.g., Bluetooth).
-type Advertiser interface {
- // StartAdvertising initiates the process of sending advertising packets
- // after every tick of the provided time interval. The payload sent
- // with each advertising packet can be specified via
- // SetAdvertisingPayload.
- // This method may be called again even if advertising is currently
- // enabled, in order to adjust the advertising interval.
- StartAdvertising(advInterval time.Duration) error
-
- // SetAdvertisingPayload sets the advertising payload that is sent with
- // each advertising packet. This function may be called at any time to
- // adjust the payload that is currently being advertised.
- SetAdvertisingPayload(payload string) error
-
- // StopAdvertising stops advertising. If the device is not advertising,
- // this function will be a noop.
- StopAdvertising() error
-}
diff --git a/services/proximity/lib/bluetooth_scanner.go b/services/proximity/lib/bluetooth_scanner.go
deleted file mode 100644
index 7929ece..0000000
--- a/services/proximity/lib/bluetooth_scanner.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package proximity
-
-import (
- "fmt"
- "time"
-
- "veyron/lib/bluetooth"
-)
-
-// NewBluetoothScanner returns a Scanner instance that uses Low-Energy Bluetooth to scan
-// for nearby devices.
-func NewBluetoothScanner() (Scanner, error) {
- return &bluetoothScanner{}, nil
-}
-
-type bluetoothScanner struct {
- device *bluetooth.Device
- c chan ScanReading
-}
-
-func (s *bluetoothScanner) StartScan(scanInterval, scanWindow time.Duration) (<-chan ScanReading, error) {
- if s.device != nil || s.c != nil {
- return nil, fmt.Errorf("scan already in progress")
- }
- var err error
- s.device, err = bluetooth.OpenFirstAvailableDevice()
- if err != nil {
- return nil, fmt.Errorf("couldn't find an available bluetooth device: %v", err)
- }
- bc, err := s.device.StartScan(scanInterval, scanWindow)
- if err != nil {
- return nil, fmt.Errorf("couldn't start bluetooth scan: %v", err)
- }
- s.c = make(chan ScanReading, 10)
- go func() {
- for r := range bc {
- s.c <- ScanReading{
- Name: r.Name,
- MAC: r.MAC,
- Distance: r.Distance,
- Time: r.Time,
- }
- }
- }()
- return s.c, nil
-}
-
-func (s *bluetoothScanner) StopScan() error {
- if s.device == nil || s.c == nil {
- if s.device != nil {
- s.device.StopScan()
- s.device.Close()
- s.device = nil
- } else {
- close(s.c)
- s.c = nil
- }
- return fmt.Errorf("scan not in progress")
- }
- s.device.StopScan()
- s.device.Close()
- s.device = nil
- close(s.c)
- s.c = nil
- return nil
-}
diff --git a/services/proximity/lib/doc.go b/services/proximity/lib/doc.go
deleted file mode 100644
index 6839ece..0000000
--- a/services/proximity/lib/doc.go
+++ /dev/null
@@ -1,3 +0,0 @@
-// Package proximity provides methods for maintaining proximity information
-// between devices.
-package proximity
diff --git a/services/proximity/lib/scanner.go b/services/proximity/lib/scanner.go
deleted file mode 100644
index 197b9b8..0000000
--- a/services/proximity/lib/scanner.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package proximity
-
-import (
- "net"
- "time"
-
- "veyron/lib/unit"
-)
-
-// Scanner denotes a (local) entity that is capable of scanning for nearby
-// devices (e.g., Bluetooth).
-type Scanner interface {
- // StartScan initiates a scan on the local device. The scan will
- // proceed over many duration intervals; within each interval, scan will
- // be ON only for a given duration window. All scan readings
- // encountered during scan-ON periods will be pushed onto the returned
- // channel. If the scan cannot be started, an error is returned.
- StartScan(scanInterval, scanWindow time.Duration) (<-chan ScanReading, error)
- // StopScan stops any scan in progress on the local device, closing
- // the channel returned by the previous call to StartScan().
- // If the device is not scanning, this function will be a noop.
- StopScan() error
-}
-
-// ScanReading holds a single reading of a neighborhood device's advertisement
-// during a scan. Typically, there will be many such readings for a particular
-// neighborhood device.
-type ScanReading struct {
- // Name represents a local name of the remote device. It can also store
- // arbitrary application-specific data.
- Name string
- // MAC is the hardware address of the remote device.
- MAC net.HardwareAddr
- // Distance represents the (estimated) distance to the neighborhood
- // device.
- Distance unit.Distance
- // Time is the time the advertisement packed was received/scanned.
- Time time.Time
-}
diff --git a/services/proximity/lib/service.go b/services/proximity/lib/service.go
deleted file mode 100644
index 2bbdd51..0000000
--- a/services/proximity/lib/service.go
+++ /dev/null
@@ -1,377 +0,0 @@
-package proximity
-
-import (
- "fmt"
- "net"
- "sort"
- "sync"
- "time"
-
- "veyron/lib/unit"
- "veyron2/ipc"
- "veyron2/services/proximity"
- "veyron2/vlog"
-)
-
-const (
- // maxRegisteredNames denotes the maximum number of names that can be
- // actively registered for this device.
- maxRegisteredNames = 10
- // advInterval specifies the frequency at which the advertising packets
- // are sent.
- advInterval = 32 * time.Millisecond
- // advCycleInterval specifies the frequency at which we change the
- // currently advertised name (out of at most maxRegisteredNames names).
- advCycleInterval = 4 * advInterval
- // scanInterval splits the entire scan duration into intervals of
- // provided length.
- scanInterval = 32 * time.Millisecond
- // scanWindow specifies, for each scanInterval, the duration during
- // which the scan will be ON. (For the remainder of scanInterval,
- // scan will be OFF.)
- scanWindow = 16 * time.Millisecond
- // minHistoryWindow denotes the minimum time window into the past
- // beyond which proximity readings should be ignored. In order to
- // catch all unique remote names (i.e., at most maxRegisteredNames of
- // them), we set this to double the interval at which the advertiser
- // can cycle through all the names.
- minHistoryWindow = 2 * maxRegisteredNames * advCycleInterval
-)
-
-// New returns a new instance of proximity service, given the provided
-// advertiser and scanner (e.g., Bluetooth). historyWindow denotes the time
-// window into the past beyond which proximity readings should be ignored.
-// refreshFrequency specifies how often the list of nearby devices should be
-// refreshed - shorter time duration means that the list will be more
-// up-to-date but more resources will be consumed. If the service cannot be
-// created, an error is returned.
-func New(a Advertiser, s Scanner, historyWindow, refreshFrequency time.Duration) (*service, error) {
- // Start advertising.
- if err := a.StartAdvertising(advInterval); err != nil {
- return nil, err
- }
-
- // Start scanning.
- r, err := s.StartScan(scanInterval, scanWindow)
- if err != nil {
- return nil, err
- }
-
- // If the history window is too small, update it.
- if historyWindow < minHistoryWindow {
- historyWindow = minHistoryWindow
- }
- srv := &service{
- devices: make(map[string]*device),
- names: make(map[string]int),
- advertiser: a,
- scanner: s,
- window: historyWindow,
- freq: refreshFrequency,
- readChan: r,
- updateChan: make(chan bool),
- updateTickerChan: time.Tick(refreshFrequency),
- advDoneChan: make(chan bool),
- }
- go srv.readLoop()
- go srv.updateLoop()
- go srv.advLoop()
- return srv, nil
-}
-
-// device represents one neighborhood device. It contains that device's MAC
-// address, average distance, list of recent scan readings, and a list of all
-// unique names stored in those scan readings.
-type device struct {
- lock sync.Mutex
- mac net.HardwareAddr
- distance unit.Distance
- names []string
- readings []ScanReading
-}
-
-// service maintains a list of devices in our close proximity, using scan
-// readings returned by the Scanner. It implements the ProximityService
-// interface, generated from proximity.idl file.
-type service struct {
- deviceLock sync.RWMutex
- nameLock sync.RWMutex
- devices map[string]*device
- nearby []proximity.Device
- names map[string]int
-
- advertiser Advertiser
- scanner Scanner
- window time.Duration
- freq time.Duration
- readChan <-chan ScanReading
- updateChan chan bool
- updateTickerChan <-chan time.Time
- advDoneChan chan bool
-}
-
-func (s *service) RegisterName(_ ipc.ServerContext, name string) error {
- s.nameLock.Lock()
- defer s.nameLock.Unlock()
- if v, ok := s.names[name]; ok {
- s.names[name] = v + 1
- return nil
- }
- if len(s.names) >= maxRegisteredNames {
- return fmt.Errorf("too many unique registered names, max allowed is %d", maxRegisteredNames)
- }
- s.names[name] = 1
- return nil
-}
-
-func (s *service) UnregisterName(_ ipc.ServerContext, name string) error {
- s.nameLock.Lock()
- defer s.nameLock.Unlock()
- v, ok := s.names[name]
- if !ok {
- return fmt.Errorf("name %q not registered", name)
- }
- if v <= 1 {
- delete(s.names, name)
- } else {
- s.names[name] = v - 1
- }
- return nil
-}
-
-// NearbyDevices returns the list of nearby devices, sorted in increasing
-// distance order.
-func (s *service) NearbyDevices(_ ipc.ServerContext) ([]proximity.Device, error) {
- s.deviceLock.RLock()
- defer s.deviceLock.RUnlock()
- return s.nearby, nil
-}
-
-// Stop terminates the process of gathering proximity information, returning
-// any error encountered.
-func (s *service) Stop() {
- vlog.VI(1).Info("stopping proximity service")
- // Stop the scanner. This action will cause the following cascading
- // effect: readChan closed -> readLoop terminated -> updateChan closed
- // -> updateLoop terminated.
- s.scanner.StopScan()
- // Stop the advertiser.
- s.advertiser.StopAdvertising()
- // Close advDoneChan, which will terminate advLoop.
- close(s.advDoneChan)
-}
-
-// readLoop extracts readings from readChan and writes notifications to
-// updateChan.
-func (s *service) readLoop() {
- for r := range s.readChan {
- s.deviceLock.Lock()
- d := s.devices[r.MAC.String()]
- if d == nil {
- d = &device{
- mac: r.MAC,
- }
- s.devices[d.mac.String()] = d
- }
- d.lock.Lock()
- d.readings = append(d.readings, r)
- d.lock.Unlock()
- s.deviceLock.Unlock()
-
- // Notify. We want at most one outstanding notification but
- // don't want to block.
- select {
- case s.updateChan <- true:
- default:
- }
- }
- close(s.updateChan)
- vlog.VI(1).Info("proximity service's read goroutine exiting")
-}
-
-// updateLoop periodically updates the state of nearby devices.
-func (s *service) updateLoop() {
- defer vlog.VI(1).Info("proximity service's update goroutine exiting")
- for {
- // Wait for a ticker. The ticker helps us avoid wasting
- // resources by doing too-frequent updates. If either the ticker
- // or the update channel gets closed in the meantime, we exit
- // this goroutine.
- var exit bool
- for !exit {
- select {
- case _, ok := <-s.updateTickerChan:
- if !ok {
- return
- }
- exit = true
- case _, ok := <-s.updateChan:
- if !ok {
- return
- }
- }
- }
-
- s.updateNearbyState()
- }
-}
-
-// advLoop cycles through all registered names and advertises each one
-// for a specified time interval (advCycleInterval). It listens on advDoneChan
-// and terminates when something is sent on it or when it gets closed.
-func (s *service) advLoop() {
- tickerChan := time.Tick(advCycleInterval)
- var ns []string
- var idx int
- defer vlog.VI(1).Info("proximity service's advertising goroutine exiting")
- for {
- select {
- case <-s.advDoneChan:
- return
- case <-tickerChan:
- }
-
- if idx >= len(ns) {
- // Cycled once through all copied names - create a
- // new copy of registered names.
- s.nameLock.RLock()
- ns = nil
- for key := range s.names {
- ns = append(ns, key)
- }
- s.nameLock.RUnlock()
- idx = 0
- if len(ns) == 0 {
- // No names to advertise: advertise an empty
- // string so that neighbors will at least
- // know this device exists.
- ns = append(ns, "")
- }
- }
- name := ns[idx]
- if err := s.advertiser.SetAdvertisingPayload(name); err != nil {
- vlog.Errorf("couldn't set advertising payload %s: %v", name, err)
- }
- idx++
- }
-}
-
-// updateNearbyStates re-computes the neighborhood list using the most recent
-// scan readings and updates it in-place.
-func (s *service) updateNearbyState() {
- // Reject all readings with timestamps before this barrier.
- barrier := time.Now().Add(-1 * s.window)
-
- // Get devices with recent readings and purge the rest.
- var recent []*device
- s.deviceLock.Lock()
- for _, d := range s.devices {
- d.lock.Lock()
- if len(d.readings) == 0 || d.readings[len(d.readings)-1].Time.Before(barrier) { // no recent readings.
- delete(s.devices, d.mac.String())
- } else {
- recent = append(recent, d)
- }
- d.lock.Unlock()
- }
- s.deviceLock.Unlock()
-
- // Purge stale readings from remaining devices and compute average
- // proximity.
- devices := make([]device, 0, len(recent))
- for _, d := range recent {
- d.lock.Lock()
- // Find the index at which readings become stale.
- var idx int
- for idx = len(d.readings) - 1; idx >= 0; idx-- {
- if r := d.readings[idx]; r.Time.Before(barrier) {
- break
- }
- }
- // Remove stale readings.
- d.readings = d.readings[idx+1:]
-
- // If we have a non-infinite distance, add the device to our
- // list.
- if dist := avgDistance(d.readings); dist != unit.MaxDistance {
- devices = append(devices, device{
- names: uniqueNames(d.readings),
- mac: d.mac,
- distance: dist,
- })
- }
- d.lock.Unlock()
- }
-
- // Sort all devices by proximity.
- incDistance := func(d1, d2 device) bool {
- return d1.distance < d2.distance
- }
- sort.Sort(&deviceSorter{
- devices: devices,
- by: incDistance,
- })
-
- // Update device list.
- newDevs := make([]proximity.Device, len(devices))
- for i, d := range devices {
- // Sort names just for stability.
- sort.Strings(d.names)
- newDevs[i] = proximity.Device{
- Names: d.names,
- MAC: d.mac.String(),
- Distance: d.distance.String(),
- }
- }
-
- vlog.VI(1).Info("Nearby devices:", newDevs)
-
- s.deviceLock.Lock()
- s.nearby = newDevs
- s.deviceLock.Unlock()
-}
-
-func avgDistance(readings []ScanReading) unit.Distance {
- if len(readings) == 0 {
- return unit.MaxDistance
- }
- // Ignore the smallest and largest 33% of the readings.
- decRSSI := func(r1, r2 ScanReading) bool {
- return r1.Distance < r2.Distance
- }
- sort.Sort(&readingSorter{
- readings: readings,
- by: decRSSI,
- })
- trim := len(readings) / 3
- readings = readings[trim : len(readings)-trim]
-
- // Prune all readings with unit.MaxDistance distance.
- idx := len(readings) - 1
- for ; idx >= 0 && readings[idx].Distance == unit.MaxDistance; idx-- {
- }
- readings = readings[:idx+1]
-
- if len(readings) == 0 {
- return unit.MaxDistance
- }
-
- // Compute average distance.
- var totalDistance unit.Distance
- for _, r := range readings {
- totalDistance += r.Distance
- }
- return totalDistance / unit.Distance(len(readings))
-}
-
-func uniqueNames(readings []ScanReading) []string {
- ns := make(map[string]bool)
- for _, r := range readings {
- ns[r.Name] = true
- }
- var names []string
- for name := range ns {
- names = append(names, name)
- }
- return names
-}
diff --git a/services/proximity/lib/service_test.go b/services/proximity/lib/service_test.go
deleted file mode 100644
index 9d086c5..0000000
--- a/services/proximity/lib/service_test.go
+++ /dev/null
@@ -1,233 +0,0 @@
-package proximity
-
-import (
- "reflect"
- "testing"
- "time"
-
- "veyron/lib/unit"
- "veyron2/services/proximity"
-)
-
-type testStream struct {
- c chan<- []proximity.Device
-}
-
-func (s *testStream) Send(item []proximity.Device) error {
- s.c <- item
- return nil
-}
-
-func testNearbyDevices(t *testing.T, history time.Duration, input []ScanReading, expected []proximity.Device) {
- const freq = 1 * time.Nanosecond // update as fast as possible
-
- // Append a dummy scan reading with a unique device name. We will later
- // wait until we see this device in the output, indicating that all of
- // the scan readings have been processed and accounted for.
- const dummyName = "$!dummy#@"
- input = append(input, ScanReading{dummyName, mac(0), 0, time.Now()})
- adv, _ := newMockAdvertiser()
- s, err := New(adv, &mockScanner{readings: input}, history, freq)
- if err != nil {
- t.Fatalf("couldn't create proximity service: %v", err)
- }
-
- // Loop until we reach the expected number of devices/readings.
- for {
- devices, err := s.NearbyDevices(nil)
- if err != nil {
- t.Fatalf("error getting nearby devices: %v", err)
- }
- var done bool
- for i, d := range devices {
- if len(d.Names) == 1 && d.Names[0] == dummyName {
- // Remove dummy device from the list.
- devices = append(devices[:i], devices[i+1:]...)
- done = true
- break
- }
- }
- if done {
- if !reflect.DeepEqual(devices, expected) {
- t.Errorf("devices mismatch: got %#v, want %#v", devices, expected)
- }
- break
- }
- }
-
- // Stop proximity.
- s.Stop()
-}
-
-func TestNearbyDevicesUniq(t *testing.T) {
- now := time.Now()
- testNearbyDevices(t, 100*time.Hour,
- []ScanReading{
- {"N1", mac(1), 5 * unit.Meter, now},
- {"N2", mac(2), 2 * unit.Meter, now},
- {"N3", mac(3), 1 * unit.Meter, now},
- {"N4", mac(4), 7 * unit.Meter, now},
- {"N5", mac(5), 3 * unit.Meter, now},
- {"N6", mac(6), 4 * unit.Meter, now},
- {"N7", mac(7), 6 * unit.Meter, now},
- },
- []proximity.Device{
- {mac(3).String(), []string{"N3"}, (1 * unit.Meter).String()},
- {mac(2).String(), []string{"N2"}, (2 * unit.Meter).String()},
- {mac(5).String(), []string{"N5"}, (3 * unit.Meter).String()},
- {mac(6).String(), []string{"N6"}, (4 * unit.Meter).String()},
- {mac(1).String(), []string{"N1"}, (5 * unit.Meter).String()},
- {mac(7).String(), []string{"N7"}, (6 * unit.Meter).String()},
- {mac(4).String(), []string{"N4"}, (7 * unit.Meter).String()},
- })
-}
-
-func TestNearbyDevicesAggr(t *testing.T) {
- now := time.Now()
- testNearbyDevices(t, 100*time.Hour,
- []ScanReading{
- {"N1", mac(1), 5 * unit.Meter, now},
- {"N2", mac(2), 2 * unit.Meter, now},
- {"N1", mac(1), 7 * unit.Meter, now},
- {"N2", mac(2), 2 * unit.Meter, now},
- {"N1", mac(1), 3 * unit.Meter, now},
- {"N2", mac(2), 5 * unit.Meter, now},
- {"N1", mac(1), 1 * unit.Meter, now},
- {"N2", mac(2), 5 * unit.Meter, now},
- {"N3", mac(3), 1 * unit.Meter, now},
- },
- []proximity.Device{
- {mac(3).String(), []string{"N3"}, (1 * unit.Meter).String()},
- {mac(2).String(), []string{"N2"}, (3.5 * unit.Meter).String()},
- {mac(1).String(), []string{"N1"}, (4 * unit.Meter).String()},
- })
-}
-
-func TestNearbyDevicesReadingsStale(t *testing.T) {
- now := time.Now()
- oneHrAgo := now.Add(-1 * time.Hour)
-
- testNearbyDevices(t, 10*time.Minute,
- []ScanReading{
- {"N1", mac(1), 5 * unit.Meter, oneHrAgo},
- {"N2", mac(2), 2 * unit.Meter, oneHrAgo},
- {"N1", mac(1), 9 * unit.Meter, oneHrAgo},
- {"N2", mac(2), 3 * unit.Meter, now},
- {"N1", mac(1), 4 * unit.Meter, now},
- {"N2", mac(2), 4 * unit.Meter, now},
- {"N3", mac(3), 1 * unit.Meter, now},
- },
- []proximity.Device{
- {mac(3).String(), []string{"N3"}, (1 * unit.Meter).String()},
- {mac(2).String(), []string{"N2"}, (3.5 * unit.Meter).String()},
- {mac(1).String(), []string{"N1"}, (4 * unit.Meter).String()},
- })
-}
-
-func TestNearbyDevicesDevicesStale(t *testing.T) {
- now := time.Now()
- oneHrAgo := now.Add(-1 * time.Hour)
-
- testNearbyDevices(t, 10*time.Minute,
- []ScanReading{
- {"N1", mac(1), 5 * unit.Meter, oneHrAgo},
- {"N2", mac(2), 2 * unit.Meter, oneHrAgo},
- {"N3", mac(3), 1 * unit.Meter, oneHrAgo},
- {"N1", mac(1), 7 * unit.Meter, now},
- {"N2", mac(2), 2 * unit.Meter, now},
- {"N1", mac(1), 4 * unit.Meter, now},
- {"N2", mac(2), 4 * unit.Meter, now},
- },
- []proximity.Device{
- {mac(2).String(), []string{"N2"}, (3 * unit.Meter).String()},
- {mac(1).String(), []string{"N1"}, (5.5 * unit.Meter).String()},
- })
-}
-
-func TestNearbyDevicesManyNames(t *testing.T) {
- now := time.Now()
- testNearbyDevices(t, 100*time.Hour,
- []ScanReading{
- {"N1B", mac(1), 6 * unit.Meter, now},
- {"N2A", mac(2), 2 * unit.Meter, now},
- {"N1A", mac(1), 7 * unit.Meter, now},
- {"N2A", mac(2), 2 * unit.Meter, now},
- {"N1A", mac(1), 5 * unit.Meter, now},
- {"N2B", mac(2), 5 * unit.Meter, now},
- {"N1A", mac(1), 1 * unit.Meter, now},
- {"N2B", mac(2), 5 * unit.Meter, now},
- {"N1A", mac(1), 4 * unit.Meter, now},
- {"N3", mac(3), 1 * unit.Meter, now},
- },
- []proximity.Device{
- {mac(3).String(), []string{"N3"}, (1 * unit.Meter).String()},
- {mac(2).String(), []string{"N2A", "N2B"}, (3.5 * unit.Meter).String()},
- {mac(1).String(), []string{"N1A", "N1B"}, (5 * unit.Meter).String()},
- })
-}
-
-func TestRegisterName(t *testing.T) {
- adv, advChan := newMockAdvertiser()
- s, err := New(adv, &mockScanner{}, time.Second, time.Second)
- if err != nil {
- t.Errorf("error creating proximity service: %v", err)
- }
-
- // Empty string should be advertising initially.
- for name := range advChan {
- if name != "" {
- t.Errorf("got advertised name %q, expected %q", name, "")
- }
- break
- }
-
- // Register name and wait for it to begin advertising.
- s.RegisterName(nil, "N1")
- for name := range advChan {
- if name == "N1" {
- break
- } else if name != "" {
- t.Errorf("got advertised name %q, expected %q", name, "N1")
- }
- }
-
- // Register another name and wait for it to begin advertising.
- s.RegisterName(nil, "N2")
- var found1, found2 bool
-Loop:
- for name := range advChan {
- switch name {
- case "N1":
- found1 = true
- case "N2":
- found2 = true
- default:
- t.Errorf("got advertised name %q, expected %q or %q", name, "N1", "N2")
- break Loop
- }
- if found1 && found2 {
- break
- }
- }
-
- // Unregister a name and wait for it to disappear. We'll consider a
- // name disappearing if it doesn't show up in 30 consecutive
- // advertisements.
- s.UnregisterName(nil, "N1")
- var n int
- for name := range advChan {
- if name == "N1" {
- n = 0
- } else {
- n++
- if n >= 30 {
- break
- }
- }
- }
-
- // Stop service and wait for the advertising channel to be closed.
- s.Stop()
- for _ = range advChan {
- }
-}
diff --git a/services/proximity/lib/sorting.go b/services/proximity/lib/sorting.go
deleted file mode 100644
index ec72860..0000000
--- a/services/proximity/lib/sorting.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package proximity
-
-type readingSorter struct {
- readings []ScanReading
- by func(r1, r2 ScanReading) bool
-}
-
-func (s *readingSorter) Len() int {
- return len(s.readings)
-}
-
-func (s *readingSorter) Swap(i, j int) {
- s.readings[i], s.readings[j] = s.readings[j], s.readings[i]
-}
-
-func (s *readingSorter) Less(i, j int) bool {
- return s.by(s.readings[i], s.readings[j])
-}
-
-type deviceSorter struct {
- devices []device
- by func(d1, d2 device) bool
-}
-
-func (s *deviceSorter) Len() int {
- return len(s.devices)
-}
-
-func (s *deviceSorter) Swap(i, j int) {
- s.devices[i], s.devices[j] = s.devices[j], s.devices[i]
-}
-
-func (s *deviceSorter) Less(i, j int) bool {
- return s.by(s.devices[i], s.devices[j])
-}
diff --git a/services/proximity/lib/util_test.go b/services/proximity/lib/util_test.go
deleted file mode 100644
index c3aade9..0000000
--- a/services/proximity/lib/util_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package proximity
-
-import (
- "fmt"
- "net"
- "sync"
- "time"
-)
-
-type mockScanner struct {
- readings []ScanReading
- c chan ScanReading
-}
-
-func (s *mockScanner) StartScan(_, _ time.Duration) (<-chan ScanReading, error) {
- s.c = make(chan ScanReading, len(s.readings))
- for _, r := range s.readings {
- s.c <- r
- }
- return s.c, nil
-}
-
-func (s *mockScanner) StopScan() error {
- close(s.c)
- return nil
-}
-
-type mockAdvertiser struct {
- lock sync.RWMutex
- payload string
- c chan string
- done chan bool
-}
-
-func newMockAdvertiser() (Advertiser, <-chan string) {
- c := make(chan string)
- return &mockAdvertiser{
- c: c,
- done: make(chan bool),
- }, c
-}
-
-func (a *mockAdvertiser) StartAdvertising(interval time.Duration) error {
- go func() {
- defer close(a.c)
- for _ = range time.Tick(interval) {
- select {
- case <-a.done:
- return
- default:
- }
- a.lock.RLock()
- p := a.payload
- a.lock.RUnlock()
- a.c <- p
- }
- }()
- return nil
-}
-
-func (a *mockAdvertiser) SetAdvertisingPayload(payload string) error {
- a.lock.Lock()
- a.payload = payload
- a.lock.Unlock()
- return nil
-}
-
-func (a *mockAdvertiser) StopAdvertising() error {
- close(a.done)
- for _ = range <-a.c {
- }
- return nil
-}
-
-func mac(id int) net.HardwareAddr {
- if id >= 256 {
- panic(fmt.Sprintf("id %d too large", id))
- }
- addr, err := net.ParseMAC(fmt.Sprintf("00:00:00:00:00:%02x", id))
- if err != nil {
- panic(fmt.Sprintf("can't create MAC address for id %d: %v", id, err))
- }
- return addr
-}
diff --git a/services/proximity/proximityd/main.go b/services/proximity/proximityd/main.go
deleted file mode 100644
index a67c5e1..0000000
--- a/services/proximity/proximityd/main.go
+++ /dev/null
@@ -1,76 +0,0 @@
-// Binary proximityd provides means for devices to announce their precence
-// to nearby devices and to obtain the list of nearby devices.
-package main
-
-import (
- "flag"
- "time"
-
- "veyron/lib/signals"
-
- "veyron/lib/bluetooth"
- vflag "veyron/security/flag"
- "veyron/services/proximity/lib"
- "veyron2/ipc"
- "veyron2/rt"
- prox "veyron2/services/proximity"
- "veyron2/vlog"
-)
-
-var (
- // TODO(rthellend): Remove the protocol and address flags when the config
- // manager is working.
- protocol = flag.String("protocol", "tcp", "protocol to listen on")
- address = flag.String("address", ":0", "address to listen on")
-
- name = flag.String("name", "", "name to mount the proximity service as")
-)
-
-func main() {
- // Get the runtime.
- r := rt.Init()
- defer r.Cleanup()
-
- // Create a new server.
- s, err := r.NewServer()
- if err != nil {
- vlog.Fatal("error creating server: ", err)
- }
- defer s.Stop()
-
- // Create a new instance of the proximity service that uses bluetooth.
- // NOTE(spetrovic): the underlying Linux bluetooth library doesn't
- // allow us to scan and advertise on the same bluetooth device
- // descriptor. We therefore open separate device descriptors for
- // advertising and scanning.
- advertiser, err := bluetooth.OpenFirstAvailableDevice()
- if err != nil {
- vlog.Fatal("couldn't find an available bluetooth device")
- }
- defer advertiser.Close()
- scanner, err := proximity.NewBluetoothScanner()
- if err != nil {
- vlog.Fatalf("couldn't create bluetooth scanner: %v", err)
- }
- p, err := proximity.New(advertiser, scanner, 1*time.Second, 500*time.Millisecond)
- if err != nil {
- vlog.Fatal("couldn't create proximity service:", err)
- }
- defer p.Stop()
-
- // Create an endpoint to listen on.
- endpoint, err := s.Listen(*protocol, *address)
- if err != nil {
- vlog.Fatal("error listening:", err)
- }
- vlog.Info("Endpoint: ", endpoint)
-
- // Start the server and register it with the mounttable under the
- // given name.
- if err := s.Serve(*name, ipc.LeafDispatcher(prox.NewServerProximity(p), vflag.NewAuthorizerOrDie())); err != nil {
- vlog.Fatalf("error publishing service (%s): %v", *name, err)
- }
-
- // Wait until shutdown.
- <-signals.ShutdownOnSignals()
-}
diff --git a/tools/proximity/impl/impl.go b/tools/proximity/impl/impl.go
deleted file mode 100644
index 907436a..0000000
--- a/tools/proximity/impl/impl.go
+++ /dev/null
@@ -1,109 +0,0 @@
-package impl
-
-import (
- "fmt"
-
- "veyron/lib/cmdline"
-
- "veyron2/rt"
- "veyron2/services/proximity"
-)
-
-var cmdRegister = &cmdline.Command{
- Run: runRegister,
- Name: "register",
- Short: "register adds a name that the remote device will be associated with.",
- Long: "register adds a name that the remote device will be associated with.",
- ArgsName: "<address> <name>",
- ArgsLong: `
-<address> is the object name of the proximity server.
-<name> is the name to register.
-`,
-}
-
-func runRegister(cmd *cmdline.Command, args []string) error {
- if expected, got := 2, len(args); expected != got {
- return cmd.Errorf("register: incorrect number of arguments, expected %d, got %d", expected, got)
- }
- p, err := proximity.BindProximity(args[0])
- if err != nil {
- return fmt.Errorf("bind error: %v", err)
- }
- if err = p.RegisterName(rt.R().TODOContext(), args[1]); err != nil {
- return err
- }
- fmt.Fprintf(cmd.Stdout(), "Name registered successfully\n")
- return nil
-}
-
-var cmdUnregister = &cmdline.Command{
- Run: runUnregister,
- Name: "unregister",
- Short: "unregister removes a name that the remote device it is associated with.",
- Long: "unregister removes a name that the remote device it is associated with.",
- ArgsName: "<address> <name>",
- ArgsLong: `
-<address> is the object name of the proximity server.
-<name> is the name to unregister.
-`,
-}
-
-func runUnregister(cmd *cmdline.Command, args []string) error {
- if expected, got := 2, len(args); expected != got {
- return cmd.Errorf("unregister: incorrect number of arguments, expected %d, got %d", expected, got)
- }
- p, err := proximity.BindProximity(args[0])
- if err != nil {
- return fmt.Errorf("bind error: %v", err)
- }
- if err = p.UnregisterName(rt.R().TODOContext(), args[1]); err != nil {
- return err
- }
- fmt.Fprintf(cmd.Stdout(), "Name unregistered successfully\n")
- return nil
-}
-
-var cmdNearbyDevices = &cmdline.Command{
- Run: runNearbyDevices,
- Name: "nearbydevices",
- Short: "nearbydevices displayes the most up-to-date list of nearby devices.",
- Long: "nearbydevices displayes the most up-to-date list of nearby devices.",
- ArgsName: "<address>",
- ArgsLong: "<address> is the object name of the proximity server.",
-}
-
-func runNearbyDevices(cmd *cmdline.Command, args []string) error {
- if expected, got := 1, len(args); expected != got {
- return cmd.Errorf("download: incorrect number of arguments, expected %d, got %d", expected, got)
- }
-
- p, err := proximity.BindProximity(args[0])
- if err != nil {
- return fmt.Errorf("bind error: %v", err)
- }
-
- devices, err := p.NearbyDevices(rt.R().TODOContext())
- if err != nil {
- return err
- }
-
- fmt.Fprintf(cmd.Stdout(), "Nearby Devices:\n")
- if len(devices) == 0 {
- fmt.Fprintf(cmd.Stdout(), "None\n")
- return nil
- }
-
- for i, d := range devices {
- fmt.Fprintf(cmd.Stdout(), "%d: MAC=%s Names=%v Distance=%s\n", i, d.MAC, d.Names, d.Distance)
- }
- return nil
-}
-
-func Root() *cmdline.Command {
- return &cmdline.Command{
- Name: "proximity",
- Short: "Command-line tool for interacting with the Veyron proximity server",
- Long: "Command-line tool for interacting with the Veyron proximity server",
- Children: []*cmdline.Command{cmdRegister, cmdUnregister, cmdNearbyDevices},
- }
-}
diff --git a/tools/proximity/impl/impl_test.go b/tools/proximity/impl/impl_test.go
deleted file mode 100644
index 7ae5914..0000000
--- a/tools/proximity/impl/impl_test.go
+++ /dev/null
@@ -1,113 +0,0 @@
-package impl_test
-
-import (
- "bytes"
- "strings"
- "testing"
-
- "veyron/tools/proximity/impl"
-
- "veyron2"
- "veyron2/ipc"
- "veyron2/naming"
- "veyron2/rt"
- "veyron2/security"
- "veyron2/services/proximity"
- "veyron2/vlog"
-)
-
-type server struct {
-}
-
-func (s *server) RegisterName(_ ipc.ServerContext, name string) error {
- vlog.VI(2).Infof("RegisterName(%q) was called", name)
- return nil
-}
-
-func (s *server) UnregisterName(_ ipc.ServerContext, name string) error {
- vlog.VI(2).Infof("UnregisterName(%q) was called", name)
- return nil
-}
-
-func (s *server) NearbyDevices(_ ipc.ServerContext) ([]proximity.Device, error) {
- vlog.VI(2).Info("NearbyDevices() was called")
- devices := []proximity.Device{
- {MAC: "xx:xx:xx:xx:xx:xx", Names: []string{"name1", "name2"}, Distance: "1m"},
- {MAC: "yy:yy:yy:yy:yy:yy", Names: []string{"name3"}, Distance: "2m"},
- }
- return devices, nil
-}
-
-type dispatcher struct {
-}
-
-func (d *dispatcher) Lookup(suffix, method string) (ipc.Invoker, security.Authorizer, error) {
- invoker := ipc.ReflectInvoker(proximity.NewServerProximity(&server{}))
- return invoker, nil, nil
-}
-
-func startServer(t *testing.T, r veyron2.Runtime) (ipc.Server, naming.Endpoint, error) {
- dispatcher := &dispatcher{}
- server, err := r.NewServer()
- if err != nil {
- t.Errorf("NewServer failed: %v", err)
- return nil, nil, err
- }
- endpoint, err := server.Listen("tcp", "127.0.0.1:0")
- if err != nil {
- t.Errorf("Listen failed: %v", err)
- return nil, nil, err
- }
- if err := server.Serve("", dispatcher); err != nil {
- t.Errorf("Serve failed: %v", err)
- return nil, nil, err
- }
- return server, endpoint, nil
-}
-
-func stopServer(t *testing.T, server ipc.Server) {
- if err := server.Stop(); err != nil {
- t.Errorf("server.Stop failed: %v", err)
- }
-}
-
-func TestProximityClient(t *testing.T) {
- runtime := rt.Init()
- server, endpoint, err := startServer(t, runtime)
- if err != nil {
- return
- }
- defer stopServer(t, server)
- // Setup the command-line.
- cmd := impl.Root()
- var stdout, stderr bytes.Buffer
- cmd.Init(nil, &stdout, &stderr)
- address := naming.JoinAddressName(endpoint.String(), "")
-
- // Test the 'register' command.
- if err := cmd.Execute([]string{"register", address, "myname"}); err != nil {
- t.Fatalf("%v", err)
- }
- if expected, got := "Name registered successfully", strings.TrimSpace(stdout.String()); got != expected {
- t.Errorf("Got %q, expected %q", got, expected)
- }
- stdout.Reset()
-
- // Test the 'unregister' command.
- if err := cmd.Execute([]string{"unregister", address, "myname"}); err != nil {
- t.Fatalf("%v", err)
- }
- if expected, got := "Name unregistered successfully", strings.TrimSpace(stdout.String()); got != expected {
- t.Errorf("Got %q, expected %q", got, expected)
- }
- stdout.Reset()
-
- // Test the 'nearbydevices' command.
- if err := cmd.Execute([]string{"nearbydevices", address}); err != nil {
- t.Fatalf("%v", err)
- }
- if expected, got := "Nearby Devices:\n0: MAC=xx:xx:xx:xx:xx:xx Names=[name1 name2] Distance=1m\n1: MAC=yy:yy:yy:yy:yy:yy Names=[name3] Distance=2m", strings.TrimSpace(stdout.String()); got != expected {
- t.Errorf("Got %q, expected %q", got, expected)
- }
- stdout.Reset()
-}
diff --git a/tools/proximity/main.go b/tools/proximity/main.go
deleted file mode 100644
index a5d8826..0000000
--- a/tools/proximity/main.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package main
-
-import (
- "veyron/tools/proximity/impl"
-
- "veyron2/rt"
-)
-
-func main() {
- defer rt.Init().Cleanup()
- impl.Root().Main()
-}