From 0b506899d55a8ced8b7ffacce6e709680c9e809f Mon Sep 17 00:00:00 2001 From: Roan Kirwin Date: Fri, 22 Mar 2024 14:46:07 -0400 Subject: [PATCH 1/5] restored functionality to connectHandler on Linux --- adapter_linux.go | 4 ++++ gap_linux.go | 61 +++++++++++++++++++++++++++++++----------------- 2 files changed, 43 insertions(+), 22 deletions(-) diff --git a/adapter_linux.go b/adapter_linux.go index 8eac7083..0e28127d 100644 --- a/adapter_linux.go +++ b/adapter_linux.go @@ -54,6 +54,10 @@ func (a *Adapter) Enable() (err error) { return fmt.Errorf("could not activate BlueZ adapter: %w", err) } addr.Store(&a.address) + + // Add a match for properties changed signals + propertiesChangedMatchOptions := []dbus.MatchOption{dbus.WithMatchInterface("org.freedesktop.DBus.Properties")} + a.bus.AddMatchSignal(propertiesChangedMatchOptions...) return nil } diff --git a/gap_linux.go b/gap_linux.go index 2e4ecc3a..ae2342c1 100644 --- a/gap_linux.go +++ b/gap_linux.go @@ -345,10 +345,6 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err // were connected between the two calls the signal wouldn't be picked up. signal := make(chan *dbus.Signal) a.bus.Signal(signal) - defer a.bus.RemoveSignal(signal) - propertiesChangedMatchOptions := []dbus.MatchOption{dbus.WithMatchInterface("org.freedesktop.DBus.Properties")} - a.bus.AddMatchSignal(propertiesChangedMatchOptions...) - defer a.bus.RemoveMatchSignal(propertiesChangedMatchOptions...) // Read whether this device is already connected. connected, err := device.device.GetProperty("org.bluez.Device1.Connected") @@ -366,30 +362,51 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err // Wait until the device has connected. connectChan := make(chan struct{}) - go func() { - for sig := range signal { - switch sig.Name { - case "org.freedesktop.DBus.Properties.PropertiesChanged": - interfaceName := sig.Body[0].(string) - if interfaceName != "org.bluez.Device1" { - continue - } - if sig.Path != device.device.Path() { - continue - } - changes := sig.Body[1].(map[string]dbus.Variant) - if connected, ok := changes["Connected"].Value().(bool); ok && connected { - close(connectChan) - } - } - } - }() + go device.watchForPropertyChanges(signal, connectChan) <-connectChan } return device, nil } +// watchForPropertyChanges listens for property change signals on the given channel and handles them accordingly. +// It checks for changes in the "Connected" property of the "org.bluez.Device1" interface and calls the appropriate +// connectHandler function from the adapter. If the "Connected" property is true, it closes the connectChan channel. +// The function continues to listen for signals until the signal channel is closed. +// +// Parameters: +// - signal: A channel of dbus signals to listen for property change signals. +// - connectChan: A channel used to notify when the device is connected. +// +func (d Device) watchForPropertyChanges(signal chan *dbus.Signal, connectChan chan struct{}) { + // signal := make(chan *dbus.Signal) + // d.adapter.bus.Signal(signal) + defer d.adapter.bus.RemoveSignal(signal) + for sig := range signal { + switch sig.Name { + case "org.freedesktop.DBus.Properties.PropertiesChanged": + interfaceName := sig.Body[0].(string) + if interfaceName != "org.bluez.Device1" { + continue + } + if sig.Path != d.device.Path() { + continue + } + changes := sig.Body[1].(map[string]dbus.Variant) + fmt.Println(changes) + if connected, ok := changes["Connected"].Value().(bool); ok { + go d.adapter.connectHandler(d, connected) + if connected { + close(connectChan) + }else { + return + } + } + } + } + fmt.Println("Signal Killed") +} + // Disconnect from the BLE device. This method is non-blocking and does not // wait until the connection is fully gone. func (d Device) Disconnect() error { From 4c32d48a292c5a07f4cf6e4c85119f140c95ec87 Mon Sep 17 00:00:00 2001 From: Roan Kirwin Date: Mon, 25 Mar 2024 09:02:51 -0400 Subject: [PATCH 2/5] Removed Extranious fmt.Println() --- gap_linux.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/gap_linux.go b/gap_linux.go index ae2342c1..d4ce38d2 100644 --- a/gap_linux.go +++ b/gap_linux.go @@ -373,11 +373,10 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err // It checks for changes in the "Connected" property of the "org.bluez.Device1" interface and calls the appropriate // connectHandler function from the adapter. If the "Connected" property is true, it closes the connectChan channel. // The function continues to listen for signals until the signal channel is closed. -// +// // Parameters: // - signal: A channel of dbus signals to listen for property change signals. // - connectChan: A channel used to notify when the device is connected. -// func (d Device) watchForPropertyChanges(signal chan *dbus.Signal, connectChan chan struct{}) { // signal := make(chan *dbus.Signal) // d.adapter.bus.Signal(signal) @@ -393,12 +392,11 @@ func (d Device) watchForPropertyChanges(signal chan *dbus.Signal, connectChan ch continue } changes := sig.Body[1].(map[string]dbus.Variant) - fmt.Println(changes) if connected, ok := changes["Connected"].Value().(bool); ok { go d.adapter.connectHandler(d, connected) if connected { close(connectChan) - }else { + } else { return } } From 0093359c2724a0451f5001a60c9b60321b828726 Mon Sep 17 00:00:00 2001 From: Roan Kirwin Date: Mon, 25 Mar 2024 12:32:05 -0400 Subject: [PATCH 3/5] Fix race condition in property change signal handling --- gap_linux.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/gap_linux.go b/gap_linux.go index d4ce38d2..469ce949 100644 --- a/gap_linux.go +++ b/gap_linux.go @@ -157,9 +157,9 @@ func (a *Adapter) Scan(callback func(*Adapter, ScanResult)) error { a.bus.Signal(signal) defer a.bus.RemoveSignal(signal) - propertiesChangedMatchOptions := []dbus.MatchOption{dbus.WithMatchInterface("org.freedesktop.DBus.Properties")} - a.bus.AddMatchSignal(propertiesChangedMatchOptions...) - defer a.bus.RemoveMatchSignal(propertiesChangedMatchOptions...) + // propertiesChangedMatchOptions := []dbus.MatchOption{dbus.WithMatchInterface("org.freedesktop.DBus.Properties")} + // a.bus.AddMatchSignal(propertiesChangedMatchOptions...) + // defer a.bus.RemoveMatchSignal(propertiesChangedMatchOptions...) newObjectMatchOptions := []dbus.MatchOption{dbus.WithMatchInterface("org.freedesktop.DBus.ObjectManager")} a.bus.AddMatchSignal(newObjectMatchOptions...) @@ -340,12 +340,6 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err adapter: a, } - // Already start watching for property changes. We do this before reading - // the Connected property below to avoid a race condition: if the device - // were connected between the two calls the signal wouldn't be picked up. - signal := make(chan *dbus.Signal) - a.bus.Signal(signal) - // Read whether this device is already connected. connected, err := device.device.GetProperty("org.bluez.Device1.Connected") if err != nil { @@ -354,6 +348,12 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err // Connect to the device, if not already connected. if !connected.Value().(bool) { + // Already start watching for property changes. We do this before reading + // the Connected property below to avoid a race condition: if the device + // were connected between the two calls the signal wouldn't be picked up. + signal := make(chan *dbus.Signal) + a.bus.Signal(signal) + // Start connecting (async). err := device.device.Call("org.bluez.Device1.Connect", 0).Err if err != nil { From c6004535c213820faeed4a486222b60782fd196e Mon Sep 17 00:00:00 2001 From: Roan Kirwin Date: Mon, 25 Mar 2024 15:42:35 -0400 Subject: [PATCH 4/5] fixed a bug that spawned infinite goroutines if a connection is requested and not made --- gap_linux.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/gap_linux.go b/gap_linux.go index 469ce949..cd9d0457 100644 --- a/gap_linux.go +++ b/gap_linux.go @@ -353,6 +353,10 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err // were connected between the two calls the signal wouldn't be picked up. signal := make(chan *dbus.Signal) a.bus.Signal(signal) + + // Wait until the device has connected. + connectChan := make(chan struct{}) + go device.watchForPropertyChanges(signal, connectChan) // Start connecting (async). err := device.device.Call("org.bluez.Device1.Connect", 0).Err @@ -360,9 +364,6 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err return Device{}, fmt.Errorf("bluetooth: failed to connect: %w", err) } - // Wait until the device has connected. - connectChan := make(chan struct{}) - go device.watchForPropertyChanges(signal, connectChan) <-connectChan } From bae5a56f9e7a22fd774047d4e763217deed34409 Mon Sep 17 00:00:00 2001 From: Roan Kirwin Date: Wed, 27 Mar 2024 14:12:24 -0400 Subject: [PATCH 5/5] Add Connected field to Device struct and update Connect method --- gap_linux.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/gap_linux.go b/gap_linux.go index cd9d0457..5a67cc60 100644 --- a/gap_linux.go +++ b/gap_linux.go @@ -323,7 +323,8 @@ func makeScanResult(props map[string]dbus.Variant) ScanResult { // Device is a connection to a remote peripheral. type Device struct { - Address Address // the MAC address of the device + Address Address // the MAC address of the device + Connected bool // whether the device is currently connected device dbus.BusObject // bluez device interface adapter *Adapter // the adapter that was used to form this device connection @@ -335,9 +336,10 @@ type Device struct { func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, error) { devicePath := dbus.ObjectPath(string(a.adapter.Path()) + "/dev_" + strings.Replace(address.MAC.String(), ":", "_", -1)) device := Device{ - Address: address, - device: a.bus.Object("org.bluez", devicePath), - adapter: a, + Address: address, + device: a.bus.Object("org.bluez", devicePath), + adapter: a, + Connected: false, } // Read whether this device is already connected. @@ -357,7 +359,7 @@ func (a *Adapter) Connect(address Address, params ConnectionParams) (Device, err // Wait until the device has connected. connectChan := make(chan struct{}) go device.watchForPropertyChanges(signal, connectChan) - + // Start connecting (async). err := device.device.Call("org.bluez.Device1.Connect", 0).Err if err != nil { @@ -396,8 +398,10 @@ func (d Device) watchForPropertyChanges(signal chan *dbus.Signal, connectChan ch if connected, ok := changes["Connected"].Value().(bool); ok { go d.adapter.connectHandler(d, connected) if connected { + d.Connected = true close(connectChan) } else { + d.Connected = false return } }