1 | /* |
2 | * BluezQt - Asynchronous Bluez wrapper library |
3 | * |
4 | * SPDX-FileCopyrightText: 2014 David Rosca <nowrep@gmail.com> |
5 | * |
6 | * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
7 | */ |
8 | |
9 | #include "device_p.h" |
10 | #include "device.h" |
11 | #include "gattserviceremote_p.h" |
12 | #include "gattserviceremote.h" |
13 | #include "adapter.h" |
14 | #include "battery.h" |
15 | #include "battery_p.h" |
16 | #include "device.h" |
17 | #include "input.h" |
18 | #include "input_p.h" |
19 | #include "macros.h" |
20 | #include "mediaplayer.h" |
21 | #include "mediaplayer_p.h" |
22 | #include "mediatransport.h" |
23 | #include "mediatransport_p.h" |
24 | #include "utils.h" |
25 | |
26 | namespace BluezQt |
27 | { |
28 | static const qint16 = -32768; // qint16 minimum |
29 | |
30 | DevicePrivate::DevicePrivate(const QString &path, const QVariantMap &properties, const AdapterPtr &adapter) |
31 | : QObject() |
32 | , m_dbusProperties(nullptr) |
33 | , m_deviceClass(0) |
34 | , m_appearance(0) |
35 | , m_paired(false) |
36 | , m_trusted(false) |
37 | , m_blocked(false) |
38 | , m_legacyPairing(false) |
39 | , m_rssi(INVALID_RSSI) |
40 | , m_manufacturerData(ManData()) |
41 | , m_servicesResolved(false) |
42 | , m_connected(false) |
43 | , m_adapter(adapter) |
44 | { |
45 | m_bluezDevice = new BluezDevice(Strings::orgBluez(), path, DBusConnection::orgBluez(), this); |
46 | |
47 | init(properties); |
48 | } |
49 | |
50 | static QHash<QString, QByteArray> toByteArrayHash(const QDBusArgument &arg) |
51 | { |
52 | if (arg.currentType() != QDBusArgument::MapType) { |
53 | return {}; |
54 | } |
55 | |
56 | QHash<QString, QByteArray> result; |
57 | arg.beginMap(); |
58 | while (!arg.atEnd()) { |
59 | arg.beginMapEntry(); |
60 | QString key; |
61 | QDBusVariant value; |
62 | arg >> key >> value; |
63 | result.insert(key: key.toUpper(), value: value.variant().toByteArray()); |
64 | arg.endMapEntry(); |
65 | } |
66 | arg.endMap(); |
67 | return result; |
68 | } |
69 | |
70 | void DevicePrivate::init(const QVariantMap &properties) |
71 | { |
72 | m_dbusProperties = new DBusProperties(Strings::orgBluez(), m_bluezDevice->path(), DBusConnection::orgBluez(), this); |
73 | |
74 | // Init properties |
75 | m_address = properties.value(QStringLiteral("Address" )).toString(); |
76 | m_name = properties.value(QStringLiteral("Name" )).toString(); |
77 | m_alias = properties.value(QStringLiteral("Alias" )).toString(); |
78 | m_deviceClass = properties.value(QStringLiteral("Class" )).toUInt(); |
79 | m_appearance = properties.value(QStringLiteral("Appearance" )).toUInt(); |
80 | m_icon = properties.value(QStringLiteral("Icon" )).toString(); |
81 | m_paired = properties.value(QStringLiteral("Paired" )).toBool(); |
82 | m_trusted = properties.value(QStringLiteral("Trusted" )).toBool(); |
83 | m_blocked = properties.value(QStringLiteral("Blocked" )).toBool(); |
84 | m_legacyPairing = properties.value(QStringLiteral("LegacyPairing" )).toBool(); |
85 | m_rssi = properties.value(QStringLiteral("RSSI" )).toInt(); |
86 | m_manufacturerData = variantToManData(value: properties.value(QStringLiteral("ManufacturerData" ))); |
87 | m_servicesResolved = properties.value(QStringLiteral("ServicesResolved" )).toBool(); |
88 | m_connected = properties.value(QStringLiteral("Connected" )).toBool(); |
89 | m_uuids = stringListToUpper(list: properties.value(QStringLiteral("UUIDs" )).toStringList()); |
90 | m_modalias = properties.value(QStringLiteral("Modalias" )).toString(); |
91 | m_serviceData = toByteArrayHash(arg: properties.value(QStringLiteral("ServiceData" )).value<QDBusArgument>()); |
92 | |
93 | if (!m_rssi) { |
94 | m_rssi = INVALID_RSSI; |
95 | } |
96 | } |
97 | |
98 | void DevicePrivate::interfacesAdded(const QString &path, const QVariantMapMap &interfaces) |
99 | { |
100 | bool changed = false; |
101 | QVariantMapMap::const_iterator it; |
102 | |
103 | for (it = interfaces.constBegin(); it != interfaces.constEnd(); ++it) { |
104 | if (it.key() == Strings::orgBluezBattery1()) { |
105 | m_battery = BatteryPtr(new Battery(path, it.value())); |
106 | m_battery->d->q = m_battery.toWeakRef(); |
107 | Q_EMIT q.lock()->batteryChanged(battery: m_battery); |
108 | changed = true; |
109 | } else if (it.key() == Strings::orgBluezInput1()) { |
110 | m_input = InputPtr(new Input(path, it.value())); |
111 | m_input->d->q = m_input.toWeakRef(); |
112 | Q_EMIT q.lock()->inputChanged(input: m_input); |
113 | changed = true; |
114 | } else if (it.key() == Strings::orgBluezMediaPlayer1()) { |
115 | m_mediaPlayer = MediaPlayerPtr(new MediaPlayer(path, it.value())); |
116 | m_mediaPlayer->d->q = m_mediaPlayer.toWeakRef(); |
117 | Q_EMIT q.lock()->mediaPlayerChanged(mediaPlayer: m_mediaPlayer); |
118 | changed = true; |
119 | } else if (it.key() == Strings::orgBluezMediaTransport1()) { |
120 | m_mediaTransport = MediaTransportPtr(new MediaTransport(path, it.value())); |
121 | m_mediaTransport->d->q = m_mediaTransport.toWeakRef(); |
122 | Q_EMIT q.lock()->mediaTransportChanged(mediaTransport: m_mediaTransport); |
123 | changed = true; |
124 | } else if (it.key() == Strings::orgBluezGattService1()) { |
125 | addGattService(gattServicePath: path,properties: it.value()); |
126 | changed = true; |
127 | } |
128 | } |
129 | |
130 | for (auto& service : m_services) { |
131 | if (path.startsWith(s: service->ubi())) { |
132 | service->d->interfacesAdded(path, interfaces); |
133 | changed = true; |
134 | } |
135 | } |
136 | |
137 | if (changed) { |
138 | Q_EMIT q.lock()->deviceChanged(device: q.toStrongRef()); |
139 | } |
140 | } |
141 | |
142 | void DevicePrivate::interfacesRemoved(const QString &path, const QStringList &interfaces) |
143 | { |
144 | bool changed = false; |
145 | |
146 | for (const QString &interface : interfaces) { |
147 | if (interface == Strings::orgBluezBattery1() && m_battery && m_battery->d->m_path == path) { |
148 | m_battery.clear(); |
149 | Q_EMIT q.lock()->batteryChanged(battery: m_battery); |
150 | changed = true; |
151 | } else if (interface == Strings::orgBluezInput1() && m_input && m_input->d->m_path == path) { |
152 | m_input.clear(); |
153 | Q_EMIT q.lock()->inputChanged(input: m_input); |
154 | changed = true; |
155 | } else if (interface == Strings::orgBluezMediaPlayer1() && m_mediaPlayer && m_mediaPlayer->d->m_path == path) { |
156 | m_mediaPlayer.clear(); |
157 | Q_EMIT q.lock()->mediaPlayerChanged(mediaPlayer: m_mediaPlayer); |
158 | changed = true; |
159 | } else if (interface == Strings::orgBluezMediaTransport1() && m_mediaTransport && m_mediaTransport->d->m_path == path) { |
160 | m_mediaTransport.clear(); |
161 | Q_EMIT q.lock()->mediaTransportChanged(mediaTransport: m_mediaTransport); |
162 | changed = true; |
163 | } else if (interface == Strings::orgBluezGattService1()) { |
164 | removeGattService(gattServicePath: path); |
165 | changed = true; |
166 | } |
167 | } |
168 | |
169 | for (auto& service : m_services) { |
170 | if (path.startsWith(s: service->ubi())) { |
171 | service->d->interfacesRemoved(path,interfaces); |
172 | changed = true; |
173 | } |
174 | } |
175 | |
176 | if (changed) { |
177 | Q_EMIT q.lock()->deviceChanged(device: q.toStrongRef()); |
178 | } |
179 | } |
180 | |
181 | void DevicePrivate::addGattService(const QString &gattServicePath, const QVariantMap &properties) |
182 | { |
183 | // Check if we have the right path |
184 | if (m_bluezDevice->path() != properties.value(QStringLiteral("Device" )).value<QDBusObjectPath>().path()) { |
185 | return; |
186 | } |
187 | |
188 | DevicePtr device = DevicePtr(this->q); |
189 | |
190 | if (!device) { |
191 | return; |
192 | } |
193 | |
194 | GattServiceRemotePtr gattService = GattServiceRemotePtr(new GattServiceRemote(gattServicePath, properties, device)); |
195 | gattService->d->q = gattService.toWeakRef(); |
196 | m_services.append(t: gattService); |
197 | |
198 | Q_EMIT device->gattServiceAdded(service: gattService); |
199 | Q_EMIT device->gattServicesChanged(services: m_services); |
200 | |
201 | // Connections |
202 | connect(sender: gattService.data(),signal: &GattServiceRemote::serviceChanged,context: q.lock().data(),slot: &Device::gattServiceChanged); |
203 | } |
204 | |
205 | void DevicePrivate::removeGattService(const QString &gattServicePath) |
206 | { |
207 | DevicePtr device = DevicePtr(this->q); |
208 | |
209 | if (!device) { |
210 | return; |
211 | } |
212 | |
213 | GattServiceRemotePtr gattService = nullptr; |
214 | for (int i=0; i < device->gattServices().size(); ++i) { |
215 | if (device->gattServices().at(i)->ubi() == gattServicePath) { |
216 | gattService = device->gattServices().at(i); |
217 | } |
218 | } |
219 | |
220 | if (gattService == nullptr) { |
221 | return; |
222 | } |
223 | |
224 | m_services.removeOne(t: gattService); |
225 | |
226 | Q_EMIT device->gattServiceRemoved(service: gattService); |
227 | Q_EMIT device->gattServicesChanged(services: m_services); |
228 | |
229 | // Connections |
230 | disconnect(sender: gattService.data(),signal: &GattServiceRemote::serviceChanged,receiver: q.lock().data(),slot: &Device::gattServiceChanged); |
231 | } |
232 | |
233 | QDBusPendingReply<> DevicePrivate::setDBusProperty(const QString &name, const QVariant &value) |
234 | { |
235 | return m_dbusProperties->Set(interface_name: Strings::orgBluezDevice1(), property_name: name, value: QDBusVariant(value)); |
236 | } |
237 | |
238 | void DevicePrivate::propertiesChanged(const QString &path, const QString &interface, const QVariantMap &changed, const QStringList &invalidated) |
239 | { |
240 | if (interface == Strings::orgBluezBattery1() && m_battery) { |
241 | m_battery->d->propertiesChanged(interface, changed, invalidated); |
242 | } else if (interface == Strings::orgBluezInput1() && m_input) { |
243 | m_input->d->propertiesChanged(interface, changed, invalidated); |
244 | } else if (interface == Strings::orgBluezMediaPlayer1() && m_mediaPlayer) { |
245 | m_mediaPlayer->d->propertiesChanged(interface, changed, invalidated); |
246 | } else if ((interface == Strings::orgBluezGattService1()) || (interface == Strings::orgBluezGattCharacteristic1()) || (interface == Strings::orgBluezGattDescriptor1())) { |
247 | for (GattServiceRemotePtr service : m_services) { |
248 | if (path.startsWith(s: service->ubi())) { |
249 | service->d->propertiesChanged(path, interface, changed, invalidated); |
250 | return; |
251 | } |
252 | } |
253 | } else if (interface != Strings::orgBluezDevice1()) { |
254 | return; |
255 | } |
256 | |
257 | QVariantMap::const_iterator i; |
258 | for (i = changed.constBegin(); i != changed.constEnd(); ++i) { |
259 | const QVariant &value = i.value(); |
260 | const QString &property = i.key(); |
261 | |
262 | if (property == QLatin1String("Name" )) { |
263 | namePropertyChanged(value: value.toString()); |
264 | } else if (property == QLatin1String("Address" )) { |
265 | addressPropertyChanged(value: value.toString()); |
266 | } else if (property == QLatin1String("Alias" )) { |
267 | aliasPropertyChanged(value: value.toString()); |
268 | } else if (property == QLatin1String("Class" )) { |
269 | classPropertyChanged(value: value.toUInt()); |
270 | } else if (property == QLatin1String("Appearance" )) { |
271 | PROPERTY_CHANGED(m_appearance, toUInt, appearanceChanged); |
272 | } else if (property == QLatin1String("Icon" )) { |
273 | PROPERTY_CHANGED(m_icon, toString, iconChanged); |
274 | } else if (property == QLatin1String("Paired" )) { |
275 | PROPERTY_CHANGED(m_paired, toBool, pairedChanged); |
276 | } else if (property == QLatin1String("Trusted" )) { |
277 | PROPERTY_CHANGED(m_trusted, toBool, trustedChanged); |
278 | } else if (property == QLatin1String("Blocked" )) { |
279 | PROPERTY_CHANGED(m_blocked, toBool, blockedChanged); |
280 | } else if (property == QLatin1String("LegacyPairing" )) { |
281 | PROPERTY_CHANGED(m_legacyPairing, toBool, legacyPairingChanged); |
282 | } else if (property == QLatin1String("RSSI" )) { |
283 | PROPERTY_CHANGED(m_rssi, toInt, rssiChanged); |
284 | } else if (property == QLatin1String("ManufacturerData" )) { |
285 | PROPERTY_CHANGED2(m_manufacturerData, variantToManData(value), manufacturerDataChanged); |
286 | } else if (property == QLatin1String("ServicesResolved" )) { |
287 | PROPERTY_CHANGED(m_servicesResolved, toBool, servicesResolvedChanged); |
288 | } else if (property == QLatin1String("Connected" )) { |
289 | PROPERTY_CHANGED(m_connected, toBool, connectedChanged); |
290 | } else if (property == QLatin1String("Modalias" )) { |
291 | PROPERTY_CHANGED(m_modalias, toString, modaliasChanged); |
292 | } else if (property == QLatin1String("UUIDs" )) { |
293 | PROPERTY_CHANGED2(m_uuids, stringListToUpper(value.toStringList()), uuidsChanged); |
294 | } else if (property == QLatin1String("ServiceData" )) { |
295 | PROPERTY_CHANGED2(m_serviceData, toByteArrayHash(value.value<QDBusArgument>()), serviceDataChanged); |
296 | } |
297 | } |
298 | |
299 | for (const QString &property : invalidated) { |
300 | if (property == QLatin1String("Name" )) { |
301 | namePropertyChanged(value: QString()); |
302 | } else if (property == QLatin1String("Class" )) { |
303 | classPropertyChanged(value: 0); |
304 | } else if (property == QLatin1String("Appearance" )) { |
305 | PROPERTY_INVALIDATED(m_appearance, 0, appearanceChanged); |
306 | } else if (property == QLatin1String("Icon" )) { |
307 | PROPERTY_INVALIDATED(m_icon, QString(), iconChanged); |
308 | } else if (property == QLatin1String("RSSI" )) { |
309 | PROPERTY_INVALIDATED(m_rssi, INVALID_RSSI, rssiChanged); |
310 | } else if (property == QLatin1String("ManufacturerData" )) { |
311 | QMap<uint16_t,QByteArray> map; |
312 | PROPERTY_INVALIDATED(m_manufacturerData, map, manufacturerDataChanged); |
313 | } else if (property == QLatin1String("ServicesResolved" )) { |
314 | PROPERTY_INVALIDATED(m_servicesResolved, false, servicesResolvedChanged); |
315 | } else if (property == QLatin1String("Modalias" )) { |
316 | PROPERTY_INVALIDATED(m_modalias, QString(), modaliasChanged); |
317 | } else if (property == QLatin1String("UUIDs" )) { |
318 | PROPERTY_INVALIDATED(m_uuids, QStringList(), uuidsChanged); |
319 | } else if (property == QLatin1String("ServiceData" )) { |
320 | PROPERTY_INVALIDATED(m_serviceData, (QHash<QString, QByteArray>()), serviceDataChanged); |
321 | } |
322 | } |
323 | |
324 | Q_EMIT q.lock()->deviceChanged(device: q.toStrongRef()); |
325 | } |
326 | |
327 | void DevicePrivate::namePropertyChanged(const QString &value) |
328 | { |
329 | if (m_name != value) { |
330 | m_name = value; |
331 | Q_EMIT q.lock()->remoteNameChanged(remoteName: m_name); |
332 | Q_EMIT q.lock()->friendlyNameChanged(friendlyName: q.lock()->friendlyName()); |
333 | } |
334 | } |
335 | |
336 | void DevicePrivate::addressPropertyChanged(const QString &value) |
337 | { |
338 | if (m_address != value) { |
339 | m_address = value; |
340 | Q_EMIT q.lock()->addressChanged(address: m_address); |
341 | } |
342 | } |
343 | |
344 | void DevicePrivate::aliasPropertyChanged(const QString &value) |
345 | { |
346 | if (m_alias != value) { |
347 | m_alias = value; |
348 | Q_EMIT q.lock()->nameChanged(name: m_alias); |
349 | Q_EMIT q.lock()->friendlyNameChanged(friendlyName: q.lock()->friendlyName()); |
350 | } |
351 | } |
352 | |
353 | void DevicePrivate::classPropertyChanged(quint32 value) |
354 | { |
355 | if (m_deviceClass != value) { |
356 | m_deviceClass = value; |
357 | Q_EMIT q.lock()->deviceClassChanged(deviceClass: m_deviceClass); |
358 | Q_EMIT q.lock()->typeChanged(type: q.lock()->type()); |
359 | } |
360 | } |
361 | |
362 | void DevicePrivate::serviceDataChanged(const QHash<QString, QByteArray> &value) |
363 | { |
364 | if (m_serviceData != value) { |
365 | m_serviceData = value; |
366 | Q_EMIT q.lock()->serviceDataChanged(serviceData: m_serviceData); |
367 | } |
368 | } |
369 | |
370 | } // namespace BluezQt |
371 | |
372 | #include "moc_device_p.cpp" |
373 | |