Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qbluetoothserviceinfo_bluez.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include "bluez/bluez5_helper_p.h"
8#include "bluez/profilemanager1_p.h"
9
10#include <QtCore/QLoggingCategory>
11#include <QtCore/QXmlStreamWriter>
12#include <QtCore/QAtomicInt>
13
14QT_BEGIN_NAMESPACE
15
16using namespace Qt::StringLiterals;
17using namespace QtBluetoothPrivate; // for D-Bus wrappers
18
19Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
20
22
23static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute)
24{
25 const QString unsignedFormat(QStringLiteral("0x%1"));
26
27 switch (attribute.typeId()) {
28 case QMetaType::Void:
29 stream->writeEmptyElement(QStringLiteral("nil"));
30 break;
31 case QMetaType::UChar:
32 stream->writeEmptyElement(QStringLiteral("uint8"));
33 stream->writeAttribute(QStringLiteral("value"),
34 unsignedFormat.arg(attribute.value<quint8>(), 2, 16,
35 QLatin1Char('0')));
36 break;
37 case QMetaType::UShort:
38 stream->writeEmptyElement(QStringLiteral("uint16"));
39 stream->writeAttribute(QStringLiteral("value"),
40 unsignedFormat.arg(attribute.value<quint16>(), 4, 16,
41 QLatin1Char('0')));
42 break;
43 case QMetaType::UInt:
44 stream->writeEmptyElement(QStringLiteral("uint32"));
45 stream->writeAttribute(QStringLiteral("value"),
46 unsignedFormat.arg(attribute.value<quint32>(), 8, 16,
47 QLatin1Char('0')));
48 break;
49 case QMetaType::Char:
50 stream->writeEmptyElement(QStringLiteral("int8"));
51 stream->writeAttribute(QStringLiteral("value"),
52 QString::number(attribute.value<qint8>()));
53 break;
54 case QMetaType::Short:
55 stream->writeEmptyElement(QStringLiteral("int16"));
56 stream->writeAttribute(QStringLiteral("value"),
57 QString::number(attribute.value<qint16>()));
58 break;
59 case QMetaType::Int:
60 stream->writeEmptyElement(QStringLiteral("int32"));
61 stream->writeAttribute(QStringLiteral("value"),
62 QString::number(attribute.value<qint32>()));
63 break;
64 case QMetaType::QByteArray:
65 stream->writeEmptyElement(QStringLiteral("text"));
66 stream->writeAttribute(QStringLiteral("value"),
67 QString::fromLatin1(attribute.value<QByteArray>().toHex().constData()));
68 stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("hex"));
69 break;
70 case QMetaType::QString:
71 stream->writeEmptyElement(QStringLiteral("text"));
72 stream->writeAttribute(QStringLiteral("value"), attribute.value<QString>());
73 stream->writeAttribute(QStringLiteral("encoding"), QStringLiteral("normal"));
74 break;
75 case QMetaType::Bool:
76 stream->writeEmptyElement(QStringLiteral("boolean"));
77 if (attribute.value<bool>())
78 stream->writeAttribute(QStringLiteral("value"), QStringLiteral("true"));
79 else
80 stream->writeAttribute(QStringLiteral("value"), QStringLiteral("false"));
81 break;
82 case QMetaType::QUrl:
83 stream->writeEmptyElement(QStringLiteral("url"));
84 stream->writeAttribute(QStringLiteral("value"), attribute.value<QUrl>().toString());
85 break;
86 default:
87 if (attribute.userType() == qMetaTypeId<QBluetoothUuid>()) {
88 stream->writeEmptyElement(QStringLiteral("uuid"));
89
90 QBluetoothUuid uuid = attribute.value<QBluetoothUuid>();
91 switch (uuid.minimumSize()) {
92 case 0:
93 stream->writeAttribute(QStringLiteral("value"),
94 unsignedFormat.arg(quint16(0), 4, 16, QLatin1Char('0')));
95 break;
96 case 2:
97 stream->writeAttribute(QStringLiteral("value"),
98 unsignedFormat.arg(uuid.toUInt16(), 4, 16,
99 QLatin1Char('0')));
100 break;
101 case 4:
102 stream->writeAttribute(QStringLiteral("value"),
103 unsignedFormat.arg(uuid.toUInt32(), 8, 16,
104 QLatin1Char('0')));
105 break;
106 case 16:
107 stream->writeAttribute(QStringLiteral("value"), uuid.toString().mid(1, 36));
108 break;
109 default:
110 stream->writeAttribute(QStringLiteral("value"), uuid.toString().mid(1, 36));
111 }
112 } else if (attribute.userType() == qMetaTypeId<QBluetoothServiceInfo::Sequence>()) {
113 stream->writeStartElement(QStringLiteral("sequence"));
114 const QBluetoothServiceInfo::Sequence *sequence =
115 static_cast<const QBluetoothServiceInfo::Sequence *>(attribute.data());
116 for (const QVariant &v : *sequence)
117 writeAttribute(stream, v);
118 stream->writeEndElement();
119 } else if (attribute.userType() == qMetaTypeId<QBluetoothServiceInfo::Alternative>()) {
120 const QBluetoothServiceInfo::Alternative *alternative =
121 static_cast<const QBluetoothServiceInfo::Alternative *>(attribute.data());
122 for (const QVariant &v : *alternative)
123 writeAttribute(stream, v);
124 stream->writeEndElement();
125 } else {
126 qCWarning(QT_BT_BLUEZ) << "Unknown variant type" << attribute.userType();
127 }
128 }
129}
130
131QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate()
132: serviceRecord(0), registered(false)
133{
134 initializeBluez5();
135 service = new OrgBluezProfileManager1Interface(QStringLiteral("org.bluez"),
136 QStringLiteral("/org/bluez"),
137 QDBusConnection::systemBus(), this);
138}
139
141{
142}
143
145{
146 return registered;
147}
148
150{
151 if (!registered)
152 return false;
153
154 if (profilePath.isEmpty())
155 return false;
156
157 QDBusPendingReply<> reply = service->UnregisterProfile(QDBusObjectPath(profilePath));
158 reply.waitForFinished();
159 if (reply.isError()) {
160 qCWarning(QT_BT_BLUEZ) << "Cannot unregister profile:" << profilePath
161 << reply.error().message();
162 return false;
163 }
164 profilePath.clear();
165
166 registered = false;
167 return true;
168}
169
170// TODO Implement local adapter behavior
171bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress & /*localAdapter*/)
172{
173 if (registered)
174 return false;
175
176 QString xmlServiceRecord;
177
178 QXmlStreamWriter stream(&xmlServiceRecord);
179 stream.setAutoFormatting(true);
180
181 stream.writeStartDocument(QStringLiteral("1.0"));
182
183 stream.writeStartElement(QStringLiteral("record"));
184
185 const QString unsignedFormat(QStringLiteral("0x%1"));
186
187 QMap<quint16, QVariant>::ConstIterator i = attributes.constBegin();
188 while (i != attributes.constEnd()) {
189 stream.writeStartElement(QStringLiteral("attribute"));
190 stream.writeAttribute(QStringLiteral("id"), unsignedFormat.arg(i.key(), 4, 16, QLatin1Char('0')));
191 writeAttribute(&stream, i.value());
192 stream.writeEndElement();
193
194 ++i;
195 }
196
197 stream.writeEndElement();
198
199 stream.writeEndDocument();
200
201 // create path
202 profilePath = u"/qt/profile"_s;
203 profilePath.append(QString::fromLatin1("/%1%2/%3")
204 .arg(sanitizeNameForDBus(QCoreApplication::applicationName()))
205 .arg(QCoreApplication::applicationPid())
206 .arg(pathCounter.fetchAndAddOrdered(1)));
207
208 QVariantMap mapping;
209 mapping.insert(QStringLiteral("ServiceRecord"), xmlServiceRecord);
210 mapping.insert(QStringLiteral("Role"), QStringLiteral("server"));
211
212 // Strategy to pick service uuid
213 // 1.) use serviceUuid()
214 // 2.) use first custom uuid if available
215 // 3.) use first service class uuid
216 QBluetoothUuid profileUuid =
217 attributes.value(QBluetoothServiceInfo::ServiceId).value<QBluetoothUuid>();
218 QBluetoothUuid firstCustomUuid;
219 if (profileUuid.isNull()) {
220 const QVariant var = attributes.value(QBluetoothServiceInfo::ServiceClassIds);
221 if (var.isValid()) {
222 const QBluetoothServiceInfo::Sequence seq =
223 var.value<QBluetoothServiceInfo::Sequence>();
224 for (const auto &e : seq) {
225 auto tempUuid = e.value<QBluetoothUuid>();
226 if (tempUuid.isNull())
227 continue;
228
229 int size = tempUuid.minimumSize();
230 if (size == 2 || size == 4) { // Base UUID derived
231 if (profileUuid.isNull())
232 profileUuid = tempUuid;
233 } else if (firstCustomUuid.isNull()) {
234 firstCustomUuid = tempUuid;
235 }
236 }
237 }
238 }
239
240 if (!firstCustomUuid.isNull())
241 profileUuid = firstCustomUuid;
242
243 QString uuidString = profileUuid.toString(QUuid::WithoutBraces);
244
245 qCDebug(QT_BT_BLUEZ) << "Registering profile under" << profilePath << uuidString;
246
247 QDBusPendingReply<> reply =
248 service->RegisterProfile(QDBusObjectPath(profilePath), uuidString, mapping);
249 reply.waitForFinished();
250 if (reply.isError()) {
251 qCWarning(QT_BT_BLUEZ) << "Cannot register profile" << reply.error().message();
252 return false;
253 }
254
255 registered = true;
256 return true;
257}
258
259QT_END_NAMESPACE
bool registerService(const QBluetoothAddress &localAdapter=QBluetoothAddress())
static QAtomicInt pathCounter
static void writeAttribute(QXmlStreamWriter *stream, const QVariant &attribute)