1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtBluetooth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest/QtTest>
30#include <QUuid>
31
32#include <QDebug>
33
34#include <qbluetoothdeviceinfo.h>
35#include <qbluetoothserviceinfo.h>
36#include <qbluetoothaddress.h>
37#include <qbluetoothlocaldevice.h>
38#include <qbluetoothuuid.h>
39#include <QtBluetooth/QBluetoothServer>
40
41#include <QtCore/qoperatingsystemversion.h>
42
43QT_USE_NAMESPACE
44
45Q_DECLARE_METATYPE(QBluetoothUuid::ProtocolUuid)
46Q_DECLARE_METATYPE(QUuid)
47Q_DECLARE_METATYPE(QBluetoothServiceInfo::Protocol)
48
49class tst_QBluetoothServiceInfo : public QObject
50{
51 Q_OBJECT
52
53public:
54 tst_QBluetoothServiceInfo();
55 ~tst_QBluetoothServiceInfo();
56
57private slots:
58 void initTestCase();
59
60 void tst_construction();
61
62 void tst_assignment_data();
63 void tst_assignment();
64
65 void tst_serviceClassUuids();
66
67 void tst_writeByteArray();
68};
69
70tst_QBluetoothServiceInfo::tst_QBluetoothServiceInfo()
71{
72}
73
74tst_QBluetoothServiceInfo::~tst_QBluetoothServiceInfo()
75{
76}
77
78void tst_QBluetoothServiceInfo::initTestCase()
79{
80 qRegisterMetaType<QBluetoothUuid::ProtocolUuid>();
81 qRegisterMetaType<QUuid>();
82 qRegisterMetaType<QBluetoothServiceInfo::Protocol>();
83 // start Bluetooth if not started
84 QBluetoothLocalDevice *device = new QBluetoothLocalDevice();
85 device->powerOn();
86 delete device;
87}
88
89void tst_QBluetoothServiceInfo::tst_construction()
90{
91 const QString serviceName("My Service");
92 const QString alternateServiceName("Another ServiceName");
93 const QBluetoothDeviceInfo deviceInfo(QBluetoothAddress("001122334455"), "Test Device", 0);
94 const QBluetoothDeviceInfo alternatedeviceInfo(QBluetoothAddress("554433221100"), "Test Device2", 0);
95
96 QList<QBluetoothUuid::ProtocolUuid> protUuids;
97 //list taken from qbluetoothuuid.h
98 protUuids << QBluetoothUuid::Sdp;
99 protUuids << QBluetoothUuid::Udp;
100 protUuids << QBluetoothUuid::Rfcomm;
101 protUuids << QBluetoothUuid::Tcp;
102 protUuids << QBluetoothUuid::TcsBin;
103 protUuids << QBluetoothUuid::TcsAt;
104 protUuids << QBluetoothUuid::Att;
105 protUuids << QBluetoothUuid::Obex;
106 protUuids << QBluetoothUuid::Ip;
107 protUuids << QBluetoothUuid::Ftp;
108 protUuids << QBluetoothUuid::Http;
109 protUuids << QBluetoothUuid::Wsp;
110 protUuids << QBluetoothUuid::Bnep;
111 protUuids << QBluetoothUuid::Upnp;
112 protUuids << QBluetoothUuid::Hidp;
113 protUuids << QBluetoothUuid::HardcopyControlChannel;
114 protUuids << QBluetoothUuid::HardcopyDataChannel;
115 protUuids << QBluetoothUuid::HardcopyNotification;
116 protUuids << QBluetoothUuid::Avctp;
117 protUuids << QBluetoothUuid::Avdtp;
118 protUuids << QBluetoothUuid::Cmtp;
119 protUuids << QBluetoothUuid::UdiCPlain;
120 protUuids << QBluetoothUuid::McapControlChannel;
121 protUuids << QBluetoothUuid::McapDataChannel;
122 protUuids << QBluetoothUuid::L2cap;
123
124 {
125 QBluetoothServiceInfo serviceInfo;
126
127 QVERIFY(!serviceInfo.isValid());
128 QVERIFY(!serviceInfo.isComplete());
129 QVERIFY(!serviceInfo.isRegistered());
130 QCOMPARE(serviceInfo.serviceName(), QString());
131 QCOMPARE(serviceInfo.serviceDescription(), QString());
132 QCOMPARE(serviceInfo.serviceProvider(), QString());
133 QCOMPARE(serviceInfo.serviceUuid(), QBluetoothUuid());
134 QCOMPARE(serviceInfo.serviceClassUuids().count(), 0);
135 QCOMPARE(serviceInfo.attributes().count(), 0);
136 QCOMPARE(serviceInfo.serverChannel(), -1);
137 QCOMPARE(serviceInfo.protocolServiceMultiplexer(), -1);
138
139 for (QBluetoothUuid::ProtocolUuid u : qAsConst(t&: protUuids))
140 QCOMPARE(serviceInfo.protocolDescriptor(u).count(), 0);
141 }
142
143 {
144 QBluetoothServiceInfo serviceInfo;
145 serviceInfo.setServiceName(serviceName);
146 serviceInfo.setDevice(deviceInfo);
147
148 QVERIFY(serviceInfo.isValid());
149 QVERIFY(!serviceInfo.isComplete());
150 QVERIFY(!serviceInfo.isRegistered());
151
152 QCOMPARE(serviceInfo.serviceName(), serviceName);
153 QCOMPARE(serviceInfo.device().address(), deviceInfo.address());
154
155 QBluetoothServiceInfo copyInfo(serviceInfo);
156
157 QVERIFY(copyInfo.isValid());
158 QVERIFY(!copyInfo.isComplete());
159 QVERIFY(!copyInfo.isRegistered());
160
161 QCOMPARE(copyInfo.serviceName(), serviceName);
162 QCOMPARE(copyInfo.device().address(), deviceInfo.address());
163
164
165 copyInfo.setAttribute(attributeId: QBluetoothServiceInfo::ServiceName, value: alternateServiceName);
166 copyInfo.setDevice(alternatedeviceInfo);
167 QCOMPARE(copyInfo.serviceName(), alternateServiceName);
168 QCOMPARE(copyInfo.attribute(QBluetoothServiceInfo::ServiceName).toString(), alternateServiceName);
169 QCOMPARE(serviceInfo.serviceName(), alternateServiceName);
170 QCOMPARE(copyInfo.device().address(), alternatedeviceInfo.address());
171 QCOMPARE(serviceInfo.device().address(), alternatedeviceInfo.address());
172
173 for (QBluetoothUuid::ProtocolUuid u : qAsConst(t&: protUuids))
174 QCOMPARE(serviceInfo.protocolDescriptor(u).count(), 0);
175 for (QBluetoothUuid::ProtocolUuid u : qAsConst(t&: protUuids))
176 QCOMPARE(copyInfo.protocolDescriptor(u).count(), 0);
177 }
178}
179
180void tst_QBluetoothServiceInfo::tst_assignment_data()
181{
182 QTest::addColumn<QUuid>(name: "uuid");
183 QTest::addColumn<QBluetoothUuid::ProtocolUuid>(name: "protocolUuid");
184 QTest::addColumn<QBluetoothServiceInfo::Protocol>(name: "serviceInfoProtocol");
185 QTest::addColumn<bool>(name: "protocolSupported");
186
187 bool l2cpSupported = true;
188 //some platforms don't support L2CP
189#if defined(QT_ANDROID_BLUETOOTH) || defined(Q_OS_WIN)
190 l2cpSupported = false;
191#endif
192
193#if defined(Q_OS_MACOS)
194 l2cpSupported = QOperatingSystemVersion::current() <= QOperatingSystemVersion::MacOSBigSur;
195#endif
196
197 QTest::newRow(dataTag: "assignment_data_l2cp")
198 << QUuid(0x67c8770b, 0x44f1, 0x410a, 0xab, 0x9a, 0xf9, 0xb5, 0x44, 0x6f, 0x13, 0xee)
199 << QBluetoothUuid::L2cap << QBluetoothServiceInfo::L2capProtocol << l2cpSupported;
200 QTest::newRow(dataTag: "assignment_data_rfcomm")
201 << QUuid(0x67c8770b, 0x44f1, 0x410a, 0xab, 0x9a, 0xf9, 0xb5, 0x44, 0x6f, 0x13, 0xee)
202 << QBluetoothUuid::Rfcomm << QBluetoothServiceInfo::RfcommProtocol << true;
203
204}
205
206void tst_QBluetoothServiceInfo::tst_assignment()
207{
208 QFETCH(QUuid, uuid);
209 QFETCH(QBluetoothUuid::ProtocolUuid, protocolUuid);
210 QFETCH(QBluetoothServiceInfo::Protocol, serviceInfoProtocol);
211 QFETCH(bool, protocolSupported);
212
213 const QString serviceName("My Service");
214 const QBluetoothDeviceInfo deviceInfo(QBluetoothAddress("001122334455"), "Test Device", 0);
215
216 QBluetoothServiceInfo serviceInfo;
217 serviceInfo.setServiceName(serviceName);
218 serviceInfo.setDevice(deviceInfo);
219
220 QVERIFY(serviceInfo.isValid());
221 QVERIFY(!serviceInfo.isRegistered());
222 QVERIFY(!serviceInfo.isComplete());
223
224 {
225 QBluetoothServiceInfo copyInfo = serviceInfo;
226
227 QVERIFY(copyInfo.isValid());
228 QVERIFY(!copyInfo.isRegistered());
229 QVERIFY(!copyInfo.isComplete());
230
231 QCOMPARE(copyInfo.serviceName(), serviceName);
232 QCOMPARE(copyInfo.device().address(), deviceInfo.address());
233 }
234
235 {
236 QBluetoothServiceInfo copyInfo;
237
238 QVERIFY(!copyInfo.isValid());
239 QVERIFY(!copyInfo.isRegistered());
240 QVERIFY(!copyInfo.isComplete());
241
242 copyInfo = serviceInfo;
243
244 QVERIFY(copyInfo.isValid());
245 QVERIFY(!copyInfo.isRegistered());
246 QVERIFY(!copyInfo.isComplete());
247
248 QCOMPARE(copyInfo.serviceName(), serviceName);
249 QCOMPARE(copyInfo.device().address(), deviceInfo.address());
250 }
251
252 {
253 QBluetoothServiceInfo copyInfo1;
254 QBluetoothServiceInfo copyInfo2;
255
256 QVERIFY(!copyInfo1.isValid());
257 QVERIFY(!copyInfo1.isRegistered());
258 QVERIFY(!copyInfo1.isComplete());
259 QVERIFY(!copyInfo2.isValid());
260 QVERIFY(!copyInfo2.isRegistered());
261 QVERIFY(!copyInfo2.isComplete());
262
263 copyInfo1 = copyInfo2 = serviceInfo;
264
265 QVERIFY(copyInfo1.isValid());
266 QVERIFY(!copyInfo1.isRegistered());
267 QVERIFY(!copyInfo1.isComplete());
268 QVERIFY(copyInfo2.isValid());
269 QVERIFY(!copyInfo2.isRegistered());
270 QVERIFY(!copyInfo2.isComplete());
271
272 QCOMPARE(copyInfo1.serviceName(), serviceName);
273 QCOMPARE(copyInfo2.serviceName(), serviceName);
274 QCOMPARE(copyInfo1.device().address(), deviceInfo.address());
275 QCOMPARE(copyInfo2.device().address(), deviceInfo.address());
276 }
277
278 {
279 QBluetoothServiceInfo copyInfo;
280 QVERIFY(!copyInfo.isValid());
281 QVERIFY(!copyInfo.isRegistered());
282 QVERIFY(!copyInfo.isComplete());
283 copyInfo = serviceInfo;
284 QVERIFY(copyInfo.contains(QBluetoothServiceInfo::ServiceName));
285
286 copyInfo.setAttribute(attributeId: QBluetoothServiceInfo::ProtocolDescriptorList, value: QBluetoothUuid(uuid));
287 QVERIFY(copyInfo.contains(QBluetoothServiceInfo::ProtocolDescriptorList));
288 QVERIFY(copyInfo.isComplete());
289 QVERIFY(copyInfo.attributes().count() > 0);
290
291 copyInfo.removeAttribute(attributeId: QBluetoothServiceInfo::ProtocolDescriptorList);
292 QVERIFY(!copyInfo.contains(QBluetoothServiceInfo::ProtocolDescriptorList));
293 QVERIFY(!copyInfo.isComplete());
294 }
295
296 {
297 QBluetoothServiceInfo copyInfo;
298 QVERIFY(!copyInfo.isValid());
299 copyInfo = serviceInfo;
300
301 QVERIFY(copyInfo.serverChannel() == -1);
302 QVERIFY(copyInfo.protocolServiceMultiplexer() == -1);
303
304 QBluetoothServiceInfo::Sequence protocolDescriptorList;
305 QBluetoothServiceInfo::Sequence protocol;
306 protocol << QVariant::fromValue(value: QBluetoothUuid(protocolUuid));
307 protocolDescriptorList.append(t: QVariant::fromValue(value: protocol));
308 protocol.clear();
309
310 protocolDescriptorList.append(t: QVariant::fromValue(value: protocol));
311 copyInfo.setAttribute(attributeId: QBluetoothServiceInfo::ProtocolDescriptorList,
312 value: protocolDescriptorList);
313 if (serviceInfoProtocol == QBluetoothServiceInfo::L2capProtocol) {
314 QVERIFY(copyInfo.serverChannel() == -1);
315 QVERIFY(copyInfo.protocolServiceMultiplexer() != -1);
316 } else if (serviceInfoProtocol == QBluetoothServiceInfo::RfcommProtocol) {
317 QVERIFY(copyInfo.serverChannel() != -1);
318 QVERIFY(copyInfo.protocolServiceMultiplexer() == -1);
319 }
320
321 QVERIFY(copyInfo.socketProtocol() == serviceInfoProtocol);
322 }
323
324 {
325 QBluetoothServiceInfo copyInfo;
326
327 QVERIFY(!copyInfo.isValid());
328 copyInfo = serviceInfo;
329 copyInfo.setServiceUuid(QBluetoothUuid::SerialPort);
330 QVERIFY(!copyInfo.isRegistered());
331
332 if (!QBluetoothLocalDevice::allDevices().count()) {
333 QSKIP("Skipping test due to missing Bluetooth device");
334 } else if (protocolSupported) {
335 QBluetoothServer server(serviceInfoProtocol);
336 QVERIFY(server.listen());
337 QTRY_VERIFY_WITH_TIMEOUT(server.isListening(), 5000);
338 QVERIFY(server.serverPort() > 0);
339
340 QBluetoothServiceInfo::Sequence protocolDescriptorList;
341 QBluetoothServiceInfo::Sequence protocol;
342 protocol << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::L2cap));
343
344 if (serviceInfoProtocol == QBluetoothServiceInfo::L2capProtocol) {
345 protocol << QVariant::fromValue(value: server.serverPort());
346 protocolDescriptorList.append(t: QVariant::fromValue(value: protocol));
347 } else if (serviceInfoProtocol == QBluetoothServiceInfo::RfcommProtocol) {
348 protocolDescriptorList.append(t: QVariant::fromValue(value: protocol));
349 protocol.clear();
350 protocol << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::Rfcomm))
351 << QVariant::fromValue(value: quint8(server.serverPort()));
352 protocolDescriptorList.append(t: QVariant::fromValue(value: protocol));
353 }
354
355 serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::ProtocolDescriptorList,
356 value: protocolDescriptorList);
357
358#if defined(Q_OS_MACOS)
359 // bluetoothd on Monterey does not want to register a record if there is no
360 // ServiceClassIDList provided.
361 if (QOperatingSystemVersion::current() > QOperatingSystemVersion::MacOSBigSur) {
362 // Nothing seems to help with L2CAP though:
363 if (serviceInfoProtocol == QBluetoothServiceInfo::RfcommProtocol) {
364 QBluetoothServiceInfo::Sequence classIds;
365 classIds << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort));
366 copyInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classIds);
367 }
368 }
369#endif // Q_OS_MACOS
370
371 QVERIFY(copyInfo.registerService());
372 QVERIFY(copyInfo.isRegistered());
373 QVERIFY(serviceInfo.isRegistered());
374 QBluetoothServiceInfo secondCopy;
375 secondCopy = copyInfo;
376 QVERIFY(secondCopy.isRegistered());
377
378 QVERIFY(secondCopy.unregisterService());
379 QVERIFY(!copyInfo.isRegistered());
380 QVERIFY(!secondCopy.isRegistered());
381 QVERIFY(!serviceInfo.isRegistered());
382 QVERIFY(server.isListening());
383 server.close();
384 QVERIFY(!server.isListening());
385 }
386 }
387}
388
389void tst_QBluetoothServiceInfo::tst_serviceClassUuids()
390{
391 QBluetoothServiceInfo info;
392 QCOMPARE(info.serviceClassUuids().count(), 0);
393
394 QBluetoothServiceInfo::Sequence classIds;
395 classIds << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::SerialPort));
396 QCOMPARE(classIds.count(), 1);
397
398 QBluetoothUuid uuid(QString("e8e10f95-1a70-4b27-9ccf-02010264e9c8"));
399 classIds.prepend(t: QVariant::fromValue(value: uuid));
400 QCOMPARE(classIds.count(), 2);
401 QCOMPARE(classIds.at(0).value<QBluetoothUuid>(), uuid);
402
403 info.setAttribute(attributeId: QBluetoothServiceInfo::ServiceClassIds, value: classIds);
404 QList<QBluetoothUuid> svclids = info.serviceClassUuids();
405 QCOMPARE(svclids.count(), 2);
406 QCOMPARE(svclids.at(0), uuid);
407 QCOMPARE(svclids.at(1), QBluetoothUuid(QBluetoothUuid::SerialPort));
408}
409
410static QByteArray debugOutput;
411
412void debugHandler(QtMsgType type, const QMessageLogContext &, const QString &msg)
413{
414 switch (type) {
415 case QtDebugMsg :
416 debugOutput = msg.toLocal8Bit();
417 break;
418 default:
419 break;
420 }
421}
422
423void tst_QBluetoothServiceInfo::tst_writeByteArray()
424{
425 // We cannot directly test the produced XML output for Bluez
426 // as there no public API to retrieve it and it would be Bluez specific.
427 // However we can check the debug output.
428 // It should contain a qbyteArray rather than a string. In the XML the QByteArray
429 // is converted to a text tag with hex encoding.
430
431 const QByteArray expected("\n (518)\tSequence\n (518)\t\tSequence\n (518)\t\t\tuchar 34\n (518)\t\t\tbytearray 05010906a101850105079508750119e029e7150025018102950175088103050795067508150026ff00190029ff8100050895057501190129059102950175039103c005010902a10185020901a1000509190129031500250175019503810275059501810105010930093109381581257f750895038106c0c0\n");
432
433 const QByteArray hidDescriptor =
434 QByteArray::fromHex(hexEncoded: "05010906a101850105079508750119e029e7150025018102950175088103050795067508150026FF00190029FF8100050895057501190129059102950175039103c005010902a10185020901a1000509190129031500250175019503810275059501810105010930093109381581257f750895038106c0c0");
435 const QBluetoothServiceInfo::Sequence hidDescriptorList({
436 QVariant::fromValue(value: quint8(0x22)), // Report type
437 QByteArray(hidDescriptor) // Descriptor array
438 });
439 const QBluetoothServiceInfo::Sequence hidDescriptorListSeq({
440 QVariant::fromValue(value: hidDescriptorList)
441 });
442 QBluetoothServiceInfo srvInfo;
443 srvInfo.setAttribute(attributeId: 0x0206, value: QVariant::fromValue(value: hidDescriptorListSeq));
444
445 const QVariant attribute = srvInfo.attribute(attributeId: 0x0206);
446 debugOutput.clear();
447 qInstallMessageHandler(debugHandler);
448 qDebug() << srvInfo;
449 qInstallMessageHandler(nullptr);
450 QCOMPARE(debugOutput, expected);
451}
452
453QTEST_MAIN(tst_QBluetoothServiceInfo)
454
455#include "tst_qbluetoothserviceinfo.moc"
456

source code of qtconnectivity/tests/auto/qbluetoothserviceinfo/tst_qbluetoothserviceinfo.cpp