diff --git a/darwin/device.go b/darwin/device.go index 05996e42..a8aceb02 100644 --- a/darwin/device.go +++ b/darwin/device.go @@ -93,6 +93,16 @@ func (d *Device) Option(opts ...ble.Option) error { // // Concurrent Scan operations will result in undefined behavior. func (d *Device) Scan(ctx context.Context, allowDup bool, h ble.AdvHandler) error { + return d.scan(ctx, allowDup, h, nil) +} + +// ScanWithServices behaves like Scan but forwards the service UUID filter to +// CoreBluetooth so macOS will request scan responses for matching devices. +func (d *Device) ScanWithServices(ctx context.Context, allowDup bool, h ble.AdvHandler, services []ble.UUID) error { + return d.scan(ctx, allowDup, h, services) +} + +func (d *Device) scan(ctx context.Context, allowDup bool, h ble.AdvHandler, services []ble.UUID) error { // Because the OS delivers results to the delegate // DidDiscoverPeripheral concurrently, we need a way to handle // events when a Scan is in progress, and safely discard them @@ -117,7 +127,7 @@ func (d *Device) Scan(ctx context.Context, allowDup bool, h ble.AdvHandler) erro // Start scanning, and stop scanning when the context expires. go func() { - d.cm.Scan(nil, &cbgo.CentralManagerScanOpts{ + d.cm.Scan(uuidsToCbgoUUIDs(services), &cbgo.CentralManagerScanOpts{ AllowDuplicates: allowDup, }) <-ctx.Done() diff --git a/gatt.go b/gatt.go index ae8fcc00..ecd9c62b 100644 --- a/gatt.go +++ b/gatt.go @@ -14,6 +14,10 @@ var ErrDefaultDevice = errors.New("default device is not set") var defaultDevice Device +type serviceScanner interface { + ScanWithServices(ctx context.Context, allowDup bool, h AdvHandler, services []UUID) error +} + // SetDefaultDevice returns the default HCI device. func SetDefaultDevice(d Device) { defaultDevice = d @@ -100,6 +104,35 @@ func Scan(ctx context.Context, allowDup bool, h AdvHandler, f AdvFilter) error { return defaultDevice.Scan(ctx, allowDup, h2) } +// ScanWithServices starts scanning while requesting that the backend use the +// provided service UUIDs as native scan filters when supported. +func ScanWithServices(ctx context.Context, allowDup bool, h AdvHandler, f AdvFilter, services []UUID) error { + if defaultDevice == nil { + return ErrDefaultDevice + } + defer untrap(trap(ctx)) + + if len(services) == 0 { + return Scan(ctx, allowDup, h, f) + } + + scanner, ok := defaultDevice.(serviceScanner) + if !ok { + return Scan(ctx, allowDup, h, f) + } + + if f == nil { + return scanner.ScanWithServices(ctx, allowDup, h, services) + } + + h2 := func(a Advertisement) { + if f(a) { + h(a) + } + } + return scanner.ScanWithServices(ctx, allowDup, h2, services) +} + // Find ... func Find(ctx context.Context, allowDup bool, f AdvFilter) ([]Advertisement, error) { if defaultDevice == nil {