Skip to content

Commit 2fe9724

Browse files
authored
[Linux] Improve connection stability (#98)
1 parent 114c2c7 commit 2fe9724

4 files changed

Lines changed: 204 additions & 150 deletions

File tree

linux/BluetoothMonitor.cpp

Lines changed: 99 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,24 @@
22
#include "logger.h"
33

44
#include <QDebug>
5+
#include <QDBusObjectPath>
6+
#include <QDBusMetaType>
57

6-
BluetoothMonitor::BluetoothMonitor(QObject *parent)
8+
BluetoothMonitor::BluetoothMonitor(QObject *parent)
79
: QObject(parent), m_dbus(QDBusConnection::systemBus())
810
{
11+
// Register meta-types for D-Bus interaction
12+
qDBusRegisterMetaType<QDBusObjectPath>();
13+
qDBusRegisterMetaType<ManagedObjectList>();
14+
915
if (!m_dbus.isConnected())
1016
{
1117
LOG_WARN("Failed to connect to system D-Bus");
1218
return;
1319
}
1420

1521
registerDBusService();
22+
checkAlreadyConnectedDevices(); // Check for already connected devices on startup
1623
}
1724

1825
BluetoothMonitor::~BluetoothMonitor()
@@ -23,25 +30,93 @@ BluetoothMonitor::~BluetoothMonitor()
2330
void BluetoothMonitor::registerDBusService()
2431
{
2532
// Match signals for PropertiesChanged on any BlueZ Device interface
26-
QString matchRule = QStringLiteral("type='signal',"
27-
"interface='org.freedesktop.DBus.Properties',"
28-
"member='PropertiesChanged',"
29-
"path_namespace='/org/bluez'");
30-
31-
m_dbus.connect("org.freedesktop.DBus",
32-
"/org/freedesktop/DBus",
33-
"org.freedesktop.DBus",
34-
"AddMatch",
35-
this,
36-
SLOT(onPropertiesChanged(QString, QVariantMap, QStringList)));
37-
3833
if (!m_dbus.connect("", "", "org.freedesktop.DBus.Properties", "PropertiesChanged",
3934
this, SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))))
4035
{
4136
LOG_WARN("Failed to connect to D-Bus PropertiesChanged signal");
4237
}
4338
}
4439

40+
bool BluetoothMonitor::isAirPodsDevice(const QString &devicePath)
41+
{
42+
QDBusInterface deviceInterface("org.bluez", devicePath, "org.freedesktop.DBus.Properties", m_dbus);
43+
44+
// Get UUIDs to check if it's an AirPods device
45+
QDBusReply<QVariant> uuidsReply = deviceInterface.call("Get", "org.bluez.Device1", "UUIDs");
46+
if (!uuidsReply.isValid())
47+
{
48+
return false;
49+
}
50+
51+
QStringList uuids = uuidsReply.value().toStringList();
52+
return uuids.contains("74ec2172-0bad-4d01-8f77-997b2be0722a");
53+
}
54+
55+
QString BluetoothMonitor::getDeviceName(const QString &devicePath)
56+
{
57+
QDBusInterface deviceInterface("org.bluez", devicePath, "org.freedesktop.DBus.Properties", m_dbus);
58+
QDBusReply<QVariant> nameReply = deviceInterface.call("Get", "org.bluez.Device1", "Name");
59+
if (nameReply.isValid())
60+
{
61+
return nameReply.value().toString();
62+
}
63+
return "Unknown";
64+
}
65+
66+
bool BluetoothMonitor::checkAlreadyConnectedDevices()
67+
{
68+
QDBusInterface objectManager("org.bluez", "/", "org.freedesktop.DBus.ObjectManager", m_dbus);
69+
QDBusMessage reply = objectManager.call("GetManagedObjects");
70+
71+
if (reply.type() == QDBusMessage::ErrorMessage)
72+
{
73+
LOG_WARN("Failed to get managed objects: " << reply.errorMessage());
74+
return false;
75+
}
76+
77+
QVariant firstArg = reply.arguments().constFirst();
78+
QDBusArgument arg = firstArg.value<QDBusArgument>();
79+
ManagedObjectList managedObjects;
80+
arg >> managedObjects;
81+
82+
bool deviceFound = false;
83+
84+
for (auto it = managedObjects.constBegin(); it != managedObjects.constEnd(); ++it)
85+
{
86+
const QDBusObjectPath &objPath = it.key();
87+
const QMap<QString, QVariantMap> &interfaces = it.value();
88+
89+
if (interfaces.contains("org.bluez.Device1"))
90+
{
91+
const QVariantMap &deviceProps = interfaces.value("org.bluez.Device1");
92+
93+
// Check if the device has the necessary properties
94+
if (!deviceProps.contains("UUIDs") || !deviceProps.contains("Connected") ||
95+
!deviceProps.contains("Address") || !deviceProps.contains("Name"))
96+
{
97+
continue;
98+
}
99+
100+
QStringList uuids = deviceProps["UUIDs"].toStringList();
101+
bool isAirPods = uuids.contains("74ec2172-0bad-4d01-8f77-997b2be0722a");
102+
103+
if (isAirPods)
104+
{
105+
bool connected = deviceProps["Connected"].toBool();
106+
if (connected)
107+
{
108+
QString macAddress = deviceProps["Address"].toString();
109+
QString deviceName = deviceProps["Name"].toString();
110+
emit deviceConnected(macAddress, deviceName);
111+
LOG_DEBUG("Found already connected AirPods: " << macAddress << " Name: " << deviceName);
112+
deviceFound = true;
113+
}
114+
}
115+
}
116+
}
117+
return deviceFound;
118+
}
119+
45120
void BluetoothMonitor::onPropertiesChanged(const QString &interface, const QVariantMap &changedProps, const QStringList &invalidatedProps)
46121
{
47122
Q_UNUSED(invalidatedProps);
@@ -56,38 +131,31 @@ void BluetoothMonitor::onPropertiesChanged(const QString &interface, const QVari
56131
bool connected = changedProps["Connected"].toBool();
57132
QString path = QDBusContext::message().path();
58133

134+
if (!isAirPodsDevice(path))
135+
{
136+
return;
137+
}
138+
59139
QDBusInterface deviceInterface("org.bluez", path, "org.freedesktop.DBus.Properties", m_dbus);
60-
140+
61141
// Get the device address
62142
QDBusReply<QVariant> addrReply = deviceInterface.call("Get", "org.bluez.Device1", "Address");
63143
if (!addrReply.isValid())
64144
{
65145
return;
66146
}
67147
QString macAddress = addrReply.value().toString();
68-
69-
// Get UUIDs to check if it's an AirPods device
70-
QDBusReply<QVariant> uuidsReply = deviceInterface.call("Get", "org.bluez.Device1", "UUIDs");
71-
if (!uuidsReply.isValid())
72-
{
73-
return;
74-
}
75-
76-
QStringList uuids = uuidsReply.value().toStringList();
77-
if (!uuids.contains("74ec2172-0bad-4d01-8f77-997b2be0722a"))
78-
{
79-
return; // Not an AirPods device
80-
}
148+
QString deviceName = getDeviceName(path);
81149

82150
if (connected)
83151
{
84-
emit deviceConnected(macAddress);
85-
LOG_DEBUG("AirPods device connected:" << macAddress);
152+
emit deviceConnected(macAddress, deviceName);
153+
LOG_DEBUG("AirPods device connected:" << macAddress << " Name:" << deviceName);
86154
}
87155
else
88156
{
89-
emit deviceDisconnected(macAddress);
90-
LOG_DEBUG("AirPods device disconnected:" << macAddress);
157+
emit deviceDisconnected(macAddress, deviceName);
158+
LOG_DEBUG("AirPods device disconnected:" << macAddress << " Name:" << deviceName);
91159
}
92160
}
93161
}

linux/BluetoothMonitor.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,31 @@
44
#include <QObject>
55
#include <QtDBus/QtDBus>
66

7+
// Forward declarations for D-Bus types
8+
typedef QMap<QDBusObjectPath, QMap<QString, QVariantMap>> ManagedObjectList;
9+
Q_DECLARE_METATYPE(ManagedObjectList)
10+
711
class BluetoothMonitor : public QObject, protected QDBusContext
812
{
913
Q_OBJECT
1014
public:
1115
explicit BluetoothMonitor(QObject *parent = nullptr);
1216
~BluetoothMonitor();
1317

18+
bool checkAlreadyConnectedDevices();
19+
1420
signals:
15-
void deviceConnected(const QString &macAddress);
16-
void deviceDisconnected(const QString &macAddress);
21+
void deviceConnected(const QString &macAddress, const QString &deviceName);
22+
void deviceDisconnected(const QString &macAddress, const QString &deviceName);
1723

1824
private slots:
1925
void onPropertiesChanged(const QString &interface, const QVariantMap &changedProps, const QStringList &invalidatedProps);
2026

2127
private:
2228
QDBusConnection m_dbus;
2329
void registerDBusService();
30+
bool isAirPodsDevice(const QString &devicePath);
31+
QString getDeviceName(const QString &devicePath);
2432
};
2533

2634
#endif // BLUETOOTHMONITOR_H

linux/Main.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ ApplicationWindow {
104104
model: ["Off", "Noise Cancellation", "Transparency", "Adaptive"]
105105
currentIndex: airPodsTrayApp.noiseControlMode
106106
onCurrentIndexChanged: airPodsTrayApp.noiseControlMode = currentIndex
107+
visible: airPodsTrayApp.airpodsConnected
107108
}
108109

109110
Text {

0 commit comments

Comments
 (0)