blob: 1b4c38d03ece4e4417699a91ef8de5b3ca5b6938 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ble
import (
"errors"
"fmt"
"sync"
"v.io/v23/context"
)
// mockDriver is a driver for testing BLE plugin without a real driver.
// Drivers within a "neighborhood" can advertise and discover each other.
type mockDriver struct {
mu sync.Mutex
services map[string]map[string][]byte // GUARDED_BY(mu)
scanUuids map[string]struct{} // GUARDED_BY(mu)
scanHandler ScanHandler // GUARDED_BY(mu)
broadcasting chan<- *mockPacket
scanning <-chan *mockPacket
done <-chan struct{}
}
type mockPacket struct {
driver *mockDriver
uuid string
characteristics map[string][]byte
}
func (d *mockDriver) AddService(uuid string, characteristics map[string][]byte) error {
d.mu.Lock()
if _, ok := d.services[uuid]; ok {
return fmt.Errorf("already being advertised: %s", uuid)
}
d.services[uuid] = characteristics
d.mu.Unlock()
d.broadcasting <- &mockPacket{d, uuid, characteristics}
return nil
}
func (d *mockDriver) RemoveService(uuid string) {
d.mu.Lock()
delete(d.services, uuid)
d.mu.Unlock()
}
func (d *mockDriver) StartScan(uuids []string, baseUuid, maskUUid string, handler ScanHandler) error {
d.mu.Lock()
if d.scanUuids != nil {
return errors.New("scan already started")
}
d.scanUuids = make(map[string]struct{})
for _, uuid := range uuids {
d.scanUuids[uuid] = struct{}{}
}
d.scanHandler = handler
d.mu.Unlock()
d.broadcasting <- &mockPacket{driver: d}
return nil
}
func (d *mockDriver) StopScan() {
d.mu.Lock()
d.scanUuids = nil
d.scanHandler = nil
d.mu.Unlock()
}
func (d *mockDriver) DebugString() string { return "mock" }
func (d *mockDriver) scanLoop() {
for {
select {
case packet := <-d.scanning:
if len(packet.uuid) == 0 {
d.rebroacast()
} else {
d.discover(packet.uuid, packet.characteristics)
}
case <-d.done:
return
}
}
}
func (d *mockDriver) rebroacast() {
d.mu.Lock()
for uuid, cs := range d.services {
d.broadcasting <- &mockPacket{d, uuid, cs}
}
d.mu.Unlock()
}
func (d *mockDriver) discover(uuid string, characteristics map[string][]byte) {
d.mu.Lock()
defer d.mu.Unlock()
if d.scanHandler == nil {
return
}
if _, ok := d.scanUuids[uuid]; ok || len(d.scanUuids) == 0 {
d.scanHandler.OnDiscovered(uuid, characteristics, 0)
}
}
type mockNeighborhood struct {
mu sync.Mutex
drivers map[*mockDriver]chan *mockPacket // GUARDED_BY(mu)
broadcasting chan *mockPacket
done chan struct{}
}
func (n *mockNeighborhood) newDriver(ctx *context.T, host string) (Driver, error) {
scanning := make(chan *mockPacket, 100)
driver := &mockDriver{
services: make(map[string]map[string][]byte),
broadcasting: n.broadcasting,
scanning: scanning,
done: n.done,
}
go driver.scanLoop()
n.mu.Lock()
n.drivers[driver] = scanning
n.mu.Unlock()
return driver, nil
}
func (n *mockNeighborhood) broadcastLoop() {
for {
select {
case packet := <-n.broadcasting:
n.mu.Lock()
for d, ch := range n.drivers {
if d == packet.driver {
continue
}
ch <- packet
}
n.mu.Unlock()
case <-n.done:
return
}
}
}
func (n *mockNeighborhood) shutdown() {
close(n.done)
}
func newNeighborhood() *mockNeighborhood {
n := &mockNeighborhood{
drivers: make(map[*mockDriver]chan *mockPacket),
broadcasting: make(chan *mockPacket, 1000),
done: make(chan struct{}),
}
go n.broadcastLoop()
SetDriverFactory(n.newDriver)
return n
}