Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 86 additions & 1 deletion adapter_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ type Adapter struct {
address string
defaultAdvertisement *Advertisement

connectHandler func(device Device, connected bool)
connectHandler func(device Device, connected bool)
connectionMonitorChan chan *dbus.Signal
monitoringConnections bool
stopMonitorChan chan struct{}
}

// NewAdapter creates a new Adapter with the given ID.
Expand Down Expand Up @@ -61,6 +64,11 @@ func (a *Adapter) Enable() (err error) {
}
addr.Store(&a.address)

// Start connection monitoring if connectHandler is set
if a.connectHandler != nil {
a.startConnectionMonitoring()
}

return nil
}

Expand All @@ -74,3 +82,80 @@ func (a *Adapter) Address() (MACAddress, error) {
}
return MACAddress{MAC: mac}, nil
}

func (a *Adapter) startConnectionMonitoring() error {
if a.monitoringConnections {
return nil // already monitoring
}

a.connectionMonitorChan = make(chan *dbus.Signal, 10)
a.stopMonitorChan = make(chan struct{})
a.bus.Signal(a.connectionMonitorChan)

if err := a.bus.AddMatchSignal(matchOptionsPropertiesChanged...); err != nil {
return fmt.Errorf("bluetooth: add dbus match signal: %w", err)
}

a.monitoringConnections = true
go a.handleConnectionSignals()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sorayaormazabalmayo I think there needs to be a way to stop this goroutine cleanly on exit.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right @deadprogram! I have just added StopConnectionMonitoring that unregisters the D-Bus signal handlers, closes channels, and sets monitoringConnections to false. Also, the select loop in handleConnectionSignals listens on stopMonitorChan so the goroutine exits immediately on shutdown.


return nil
}

func (a *Adapter) handleConnectionSignals() {
for {
select {
case <-a.stopMonitorChan:
return
case sig, ok := <-a.connectionMonitorChan:
if !ok {
return // channel was closed
}
if sig.Name != dbusSignalPropertiesChanged {
continue
}
interfaceName, ok := sig.Body[0].(string)
if !ok || interfaceName != bluezDevice1Interface {
continue
}
changes, ok := sig.Body[1].(map[string]dbus.Variant)
if !ok {
continue
}
if connectedVariant, ok := changes["Connected"]; ok {
connected, ok := connectedVariant.Value().(bool)
if !ok {
continue
}
device := Device{
device: a.bus.Object("org.bluez", sig.Path),
adapter: a,
}
var props map[string]dbus.Variant
if err := device.device.Call("org.freedesktop.DBus.Properties.GetAll",
0, bluezDevice1Interface).Store(&props); err != nil {
continue
}
if err := device.parseProperties(&props); err != nil {
continue
}
a.connectHandler(device, connected)
}
}
}
}

func (a *Adapter) StopConnectionMonitoring() error {
if !a.monitoringConnections {
return nil
}
// Unregister the signal channel from D-Bus before closing
a.bus.RemoveSignal(a.connectionMonitorChan)
if err := a.bus.RemoveMatchSignal(matchOptionsPropertiesChanged...); err != nil {
return fmt.Errorf("bluetooth: remove dbus match signal: %w", err)
}
close(a.stopMonitorChan)
close(a.connectionMonitorChan)
a.monitoringConnections = false
return nil
}
Loading