Files
2026-08-13 23:00:59 +02:00

84 lines
2.6 KiB
Go

package bluetooth
import (
"fmt"
"sync"
"github.com/godbus/dbus/v5"
)
const (
agentPath = dbus.ObjectPath("/org/anotherhadi/settuings/agent")
agentCapability = "DisplayYesNo"
)
// agent implements the BlueZ org.bluez.Agent1 interface, auto-accepting
// every pairing prompt. `bluetoothctl pair` run non-interactively never
// registers an agent, so BlueZ has nobody to ask for PIN/passkey
// confirmation and any device that isn't "Just Works" fails with
// AuthenticationFailed. Registering this agent as the system default
// fixes that even though pairing itself still happens in a separate
// bluetoothctl process: BlueZ routes auth requests to whichever agent is
// registered as default, not to whoever initiated the pairing.
type agent struct{}
func (agent) Release() *dbus.Error { return nil }
func (agent) RequestPinCode(_ dbus.ObjectPath) (string, *dbus.Error) {
return "", dbus.NewError("org.bluez.Error.Rejected", nil)
}
func (agent) DisplayPinCode(_ dbus.ObjectPath, _ string) *dbus.Error { return nil }
func (agent) RequestPasskey(_ dbus.ObjectPath) (uint32, *dbus.Error) {
return 0, dbus.NewError("org.bluez.Error.Rejected", nil)
}
func (agent) DisplayPasskey(_ dbus.ObjectPath, _ uint32, _ uint16) *dbus.Error { return nil }
func (agent) RequestConfirmation(_ dbus.ObjectPath, _ uint32) *dbus.Error { return nil }
func (agent) RequestAuthorization(_ dbus.ObjectPath) *dbus.Error { return nil }
func (agent) AuthorizeService(_ dbus.ObjectPath, _ string) *dbus.Error { return nil }
func (agent) Cancel() *dbus.Error { return nil }
var (
agentOnce sync.Once
agentErr error
// agentConn is kept alive for the process lifetime: BlueZ calls back
// into our agent over this connection while pairing is in progress.
agentConn *dbus.Conn
)
// ensureAgent registers this process as BlueZ's default pairing agent, once
// per run. Call before any Pair.
func ensureAgent() error {
agentOnce.Do(func() {
conn, err := dbus.ConnectSystemBus()
if err != nil {
agentErr = fmt.Errorf("connect to system bus: %w", err)
return
}
if err := conn.Export(agent{}, agentPath, "org.bluez.Agent1"); err != nil {
agentErr = fmt.Errorf("export agent: %w", err)
return
}
manager := conn.Object("org.bluez", dbus.ObjectPath("/org/bluez"))
if call := manager.Call("org.bluez.AgentManager1.RegisterAgent", 0, agentPath, agentCapability); call.Err != nil {
agentErr = fmt.Errorf("register agent: %w", call.Err)
return
}
if call := manager.Call("org.bluez.AgentManager1.RequestDefaultAgent", 0, agentPath); call.Err != nil {
agentErr = fmt.Errorf("request default agent: %w", call.Err)
return
}
agentConn = conn
})
return agentErr
}