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 "btlocaldevice.h" |
30 | #include <QDebug> |
31 | #include <QTimer> |
32 | #ifdef Q_OS_ANDROID |
33 | #include <QtAndroidExtras/QtAndroid> |
34 | #endif |
35 | #include <QtBluetooth/QBluetoothServiceInfo> |
36 | |
37 | #define BTCHAT_DEVICE_ADDR "00:15:83:38:17:C3" |
38 | |
39 | //same uuid as examples/bluetooth/btchat |
40 | //the reverse UUID is only used on Android to counter |
41 | //https://issuetracker.google.com/issues/37076498 (tracked via QTBUG-61392) |
42 | #define TEST_SERVICE_UUID "e8e10f95-1a70-4b27-9ccf-02010264e9c8" |
43 | #define TEST_REVERSE_SERVICE_UUID "c8e96402-0102-cf9c-274b-701a950fe1e8" |
44 | |
45 | #define SOCKET_PROTOCOL QBluetoothServiceInfo::RfcommProtocol |
46 | //#define SOCKET_PROTOCOL QBluetoothServiceInfo::L2capProtocol |
47 | |
48 | BtLocalDevice::BtLocalDevice(QObject *parent) : |
49 | QObject(parent), securityFlags(QBluetooth::NoSecurity) |
50 | { |
51 | localDevice = new QBluetoothLocalDevice(this); |
52 | connect(sender: localDevice, signal: &QBluetoothLocalDevice::error, |
53 | receiver: this, slot: &BtLocalDevice::error); |
54 | connect(sender: localDevice, signal: &QBluetoothLocalDevice::hostModeStateChanged, |
55 | receiver: this, slot: &BtLocalDevice::hostModeStateChanged); |
56 | connect(sender: localDevice, signal: &QBluetoothLocalDevice::pairingFinished, |
57 | receiver: this, slot: &BtLocalDevice::pairingFinished); |
58 | connect(sender: localDevice, signal: &QBluetoothLocalDevice::deviceConnected, |
59 | receiver: this, slot: &BtLocalDevice::connected); |
60 | connect(sender: localDevice, signal: &QBluetoothLocalDevice::deviceDisconnected, |
61 | receiver: this, slot: &BtLocalDevice::disconnected); |
62 | connect(sender: localDevice, signal: &QBluetoothLocalDevice::pairingDisplayConfirmation, |
63 | receiver: this, slot: &BtLocalDevice::pairingDisplayConfirmation); |
64 | connect(sender: localDevice, signal: &QBluetoothLocalDevice::pairingDisplayPinCode, |
65 | receiver: this, slot: &BtLocalDevice::pairingDisplayPinCode); |
66 | |
67 | if (localDevice->isValid()) { |
68 | deviceAgent = new QBluetoothDeviceDiscoveryAgent(this); |
69 | connect(sender: deviceAgent, signal: &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, |
70 | receiver: this, slot: &BtLocalDevice::deviceDiscovered); |
71 | connect(sender: deviceAgent, signal: &QBluetoothDeviceDiscoveryAgent::finished, |
72 | receiver: this, slot: &BtLocalDevice::discoveryFinished); |
73 | connect(sender: deviceAgent, signal: QOverload<QBluetoothDeviceDiscoveryAgent::Error>::of(ptr: &QBluetoothDeviceDiscoveryAgent::error), |
74 | receiver: this, slot: &BtLocalDevice::discoveryError); |
75 | connect(sender: deviceAgent, signal: &QBluetoothDeviceDiscoveryAgent::canceled, |
76 | receiver: this, slot: &BtLocalDevice::discoveryCanceled); |
77 | |
78 | serviceAgent = new QBluetoothServiceDiscoveryAgent(this); |
79 | connect(sender: serviceAgent, signal: &QBluetoothServiceDiscoveryAgent::serviceDiscovered, |
80 | receiver: this, slot: &BtLocalDevice::serviceDiscovered); |
81 | connect(sender: serviceAgent, signal: &QBluetoothServiceDiscoveryAgent::finished, |
82 | receiver: this, slot: &BtLocalDevice::serviceDiscoveryFinished); |
83 | connect(sender: serviceAgent, signal: &QBluetoothServiceDiscoveryAgent::canceled, |
84 | receiver: this, slot: &BtLocalDevice::serviceDiscoveryCanceled); |
85 | connect(sender: serviceAgent, signal: QOverload<QBluetoothServiceDiscoveryAgent::Error>::of(ptr: &QBluetoothServiceDiscoveryAgent::error), |
86 | receiver: this, slot: &BtLocalDevice::serviceDiscoveryError); |
87 | |
88 | socket = new QBluetoothSocket(SOCKET_PROTOCOL, this); |
89 | connect(sender: socket, signal: &QBluetoothSocket::stateChanged, |
90 | receiver: this, slot: &BtLocalDevice::socketStateChanged); |
91 | connect(sender: socket, signal: QOverload<QBluetoothSocket::SocketError>::of(ptr: &QBluetoothSocket::error), |
92 | receiver: this, slot: &BtLocalDevice::socketError); |
93 | connect(sender: socket, signal: &QBluetoothSocket::connected, receiver: this, slot: &BtLocalDevice::socketConnected); |
94 | connect(sender: socket, signal: &QBluetoothSocket::disconnected, receiver: this, slot: &BtLocalDevice::socketDisconnected); |
95 | connect(sender: socket, signal: &QIODevice::readyRead, receiver: this, slot: &BtLocalDevice::readData); |
96 | connect(sender: socket, signal: &QBluetoothSocket::bytesWritten, context: this, slot: [](qint64 bytesWritten){ |
97 | qDebug() << "Bytes Written to Client socket:" << bytesWritten; |
98 | }); |
99 | setSecFlags(static_cast<int>(socket->preferredSecurityFlags())); |
100 | |
101 | server = new QBluetoothServer(SOCKET_PROTOCOL, this); |
102 | connect(sender: server, signal: &QBluetoothServer::newConnection, receiver: this, slot: &BtLocalDevice::serverNewConnection); |
103 | connect(sender: server, signal: QOverload<QBluetoothServer::Error>::of(ptr: &QBluetoothServer::error), |
104 | receiver: this, slot: &BtLocalDevice::serverError); |
105 | } else { |
106 | deviceAgent = nullptr; |
107 | serviceAgent = nullptr; |
108 | socket = nullptr; |
109 | server = nullptr; |
110 | } |
111 | } |
112 | |
113 | BtLocalDevice::~BtLocalDevice() |
114 | { |
115 | while (!serverSockets.isEmpty()) |
116 | { |
117 | QBluetoothSocket* s = serverSockets.takeFirst(); |
118 | s->abort(); |
119 | s->deleteLater(); |
120 | } |
121 | } |
122 | |
123 | int BtLocalDevice::secFlags() const |
124 | { |
125 | return static_cast<int>(securityFlags); |
126 | } |
127 | |
128 | void BtLocalDevice::setSecFlags(int newFlags) |
129 | { |
130 | QBluetooth::SecurityFlags fl(newFlags); |
131 | |
132 | if (securityFlags != fl) { |
133 | securityFlags = fl; |
134 | emit secFlagsChanged(); |
135 | } |
136 | } |
137 | |
138 | QString BtLocalDevice::hostMode() const |
139 | { |
140 | switch (localDevice->hostMode()) { |
141 | case QBluetoothLocalDevice::HostDiscoverable: |
142 | return QStringLiteral("HostMode: Discoverable" ); |
143 | case QBluetoothLocalDevice::HostConnectable: |
144 | return QStringLiteral("HostMode: Connectable" ); |
145 | case QBluetoothLocalDevice::HostDiscoverableLimitedInquiry: |
146 | return QStringLiteral("HostMode: DiscoverableLimit" ); |
147 | case QBluetoothLocalDevice::HostPoweredOff: |
148 | return QStringLiteral("HostMode: Powered Off" ); |
149 | } |
150 | |
151 | return QStringLiteral("HostMode: <None>" ); |
152 | } |
153 | |
154 | void BtLocalDevice::setHostMode(int newMode) |
155 | { |
156 | localDevice->setHostMode(static_cast<QBluetoothLocalDevice::HostMode>(newMode)); |
157 | } |
158 | |
159 | void BtLocalDevice::requestPairingUpdate(bool isPairing) |
160 | { |
161 | QBluetoothAddress baddr(BTCHAT_DEVICE_ADDR); |
162 | if (baddr.isNull()) |
163 | return; |
164 | |
165 | |
166 | |
167 | if (isPairing) { |
168 | //toggle between authorized and non-authorized pairing to achieve better |
169 | //level of testing |
170 | static short pairing = 0; |
171 | if ((pairing%2) == 1) |
172 | localDevice->requestPairing(address: baddr, pairing: QBluetoothLocalDevice::Paired); |
173 | else |
174 | localDevice->requestPairing(address: baddr, pairing: QBluetoothLocalDevice::AuthorizedPaired); |
175 | pairing++; |
176 | } else { |
177 | localDevice->requestPairing(address: baddr, pairing: QBluetoothLocalDevice::Unpaired); |
178 | } |
179 | |
180 | for (int i = 0; i < foundTestServers.count(); i++) { |
181 | if (isPairing) |
182 | localDevice->requestPairing(address: foundTestServers.at(i).device().address(), |
183 | pairing: QBluetoothLocalDevice::Paired); |
184 | else |
185 | localDevice->requestPairing(address: foundTestServers.at(i).device().address(), |
186 | pairing: QBluetoothLocalDevice::Unpaired); |
187 | } |
188 | } |
189 | |
190 | void BtLocalDevice::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing) |
191 | { |
192 | qDebug() << "(Un)Pairing finished" << address.toString() << pairing; |
193 | } |
194 | |
195 | void BtLocalDevice::connected(const QBluetoothAddress &addr) |
196 | { |
197 | qDebug() << "Newly connected device" << addr.toString(); |
198 | } |
199 | |
200 | void BtLocalDevice::disconnected(const QBluetoothAddress &addr) |
201 | { |
202 | qDebug() << "Newly disconnected device" << addr.toString(); |
203 | } |
204 | |
205 | void BtLocalDevice::pairingDisplayConfirmation(const QBluetoothAddress &address, const QString &pin) |
206 | { |
207 | qDebug() << "PairingDisplayConfirmation" << address << pin; |
208 | QTimer::singleShot(msec: 3000, receiver: this, SLOT(confirmPairing())); |
209 | } |
210 | |
211 | void BtLocalDevice::pairingDisplayPinCode(const QBluetoothAddress &address, const QString &pin) |
212 | { |
213 | qDebug() << "PairingDisplayPinCode" << address << pin; |
214 | } |
215 | |
216 | void BtLocalDevice::confirmPairing() |
217 | { |
218 | static bool confirm = false; |
219 | confirm = !confirm; //toggle |
220 | qDebug() << "######" << "Sending pairing confirmation: " << confirm; |
221 | localDevice->pairingConfirmation(confirmation: confirm); |
222 | } |
223 | |
224 | void BtLocalDevice::cycleSecurityFlags() |
225 | { |
226 | if (securityFlags.testFlag(flag: QBluetooth::Secure)) |
227 | setSecFlags(QBluetooth::NoSecurity); |
228 | else if (securityFlags.testFlag(flag: QBluetooth::Encryption)) |
229 | setSecFlags(secFlags() | QBluetooth::Secure); |
230 | else if (securityFlags.testFlag(flag: QBluetooth::Authentication)) |
231 | setSecFlags(secFlags() | QBluetooth::Encryption); |
232 | else if (securityFlags.testFlag(flag: QBluetooth::Authorization)) |
233 | setSecFlags(secFlags() | QBluetooth::Authentication); |
234 | else |
235 | setSecFlags(secFlags() | QBluetooth::Authorization); |
236 | } |
237 | |
238 | void BtLocalDevice::deviceDiscovered(const QBluetoothDeviceInfo &info) |
239 | { |
240 | QString services; |
241 | if (info.serviceClasses() & QBluetoothDeviceInfo::PositioningService) |
242 | services += "Position|" ; |
243 | if (info.serviceClasses() & QBluetoothDeviceInfo::NetworkingService) |
244 | services += "Network|" ; |
245 | if (info.serviceClasses() & QBluetoothDeviceInfo::RenderingService) |
246 | services += "Rendering|" ; |
247 | if (info.serviceClasses() & QBluetoothDeviceInfo::CapturingService) |
248 | services += "Capturing|" ; |
249 | if (info.serviceClasses() & QBluetoothDeviceInfo::ObjectTransferService) |
250 | services += "ObjectTra|" ; |
251 | if (info.serviceClasses() & QBluetoothDeviceInfo::AudioService) |
252 | services += "Audio|" ; |
253 | if (info.serviceClasses() & QBluetoothDeviceInfo::TelephonyService) |
254 | services += "Telephony|" ; |
255 | if (info.serviceClasses() & QBluetoothDeviceInfo::InformationService) |
256 | services += "Information|" ; |
257 | |
258 | services.truncate(pos: services.length()-1); //cut last '/' |
259 | |
260 | qDebug() << "Found new device: " << info.name() << info.isValid() << info.address().toString() |
261 | << info.rssi() << info.majorDeviceClass() |
262 | << info.minorDeviceClass() << services; |
263 | |
264 | } |
265 | |
266 | void BtLocalDevice::discoveryFinished() |
267 | { |
268 | qDebug() << "###### Device Discovery Finished" ; |
269 | } |
270 | |
271 | void BtLocalDevice::discoveryCanceled() |
272 | { |
273 | qDebug() << "###### Device Discovery Canceled" ; |
274 | } |
275 | |
276 | void BtLocalDevice::discoveryError(QBluetoothDeviceDiscoveryAgent::Error error) |
277 | { |
278 | auto *client = qobject_cast<QBluetoothDeviceDiscoveryAgent *>(object: sender()); |
279 | if (!client) |
280 | return; |
281 | qDebug() << "###### Device Discovery Error:" << error << (client ? client->errorString() : QString()); |
282 | } |
283 | |
284 | void BtLocalDevice::startDiscovery() |
285 | { |
286 | if (deviceAgent) { |
287 | qDebug() << "###### Starting device discovery process" ; |
288 | deviceAgent->start(method: QBluetoothDeviceDiscoveryAgent::ClassicMethod); |
289 | } |
290 | } |
291 | |
292 | void BtLocalDevice::stopDiscovery() |
293 | { |
294 | if (deviceAgent) { |
295 | qDebug() << "Stopping device discovery process" ; |
296 | deviceAgent->stop(); |
297 | } |
298 | } |
299 | |
300 | void BtLocalDevice::startServiceDiscovery(bool isMinimalDiscovery) |
301 | { |
302 | if (serviceAgent) { |
303 | serviceAgent->setRemoteAddress(QBluetoothAddress()); |
304 | |
305 | qDebug() << "###### Starting service discovery process" ; |
306 | serviceAgent->start(mode: isMinimalDiscovery |
307 | ? QBluetoothServiceDiscoveryAgent::MinimalDiscovery |
308 | : QBluetoothServiceDiscoveryAgent::FullDiscovery); |
309 | } |
310 | } |
311 | |
312 | void BtLocalDevice::startTargettedServiceDiscovery() |
313 | { |
314 | if (serviceAgent) { |
315 | const QBluetoothAddress baddr(BTCHAT_DEVICE_ADDR); |
316 | qDebug() << "###### Starting service discovery on" |
317 | << baddr.toString(); |
318 | if (baddr.isNull()) |
319 | return; |
320 | |
321 | if (!serviceAgent->setRemoteAddress(baddr)) { |
322 | qWarning() << "###### Cannot set remote address. Aborting" ; |
323 | return; |
324 | } |
325 | |
326 | serviceAgent->start(); |
327 | } |
328 | } |
329 | |
330 | void BtLocalDevice::stopServiceDiscovery() |
331 | { |
332 | if (serviceAgent) { |
333 | qDebug() << "Stopping service discovery process" ; |
334 | serviceAgent->stop(); |
335 | } |
336 | } |
337 | |
338 | void BtLocalDevice::serviceDiscovered(const QBluetoothServiceInfo &info) |
339 | { |
340 | QStringList classIds; |
341 | const QList<QBluetoothUuid> uuids = info.serviceClassUuids(); |
342 | for (const QBluetoothUuid &uuid : uuids) |
343 | classIds.append(t: uuid.toString()); |
344 | qDebug() << "$$ Found new service" << info.device().address().toString() |
345 | << info.serviceUuid() << info.serviceName() << info.serviceDescription() << classIds; |
346 | |
347 | bool matchingService = |
348 | (info.serviceUuid() == QBluetoothUuid(QString(TEST_SERVICE_UUID))); |
349 | #ifdef Q_OS_ANDROID |
350 | if (QtAndroid::androidSdkVersion() >= 23) //bug introduced by Android 6.0.1 |
351 | matchingService = matchingService |
352 | || (info.serviceUuid() == QBluetoothUuid(QString(TEST_REVERSE_SERVICE_UUID))); |
353 | #endif |
354 | |
355 | if (matchingService |
356 | || info.serviceClassUuids().contains(t: QBluetoothUuid(QString(TEST_SERVICE_UUID)))) |
357 | { |
358 | //This is here to detect the test server for SPP testing later on |
359 | bool alreadyKnown = false; |
360 | for (const QBluetoothServiceInfo& found : qAsConst(t&: foundTestServers)) { |
361 | if (found.device().address() == info.device().address()) { |
362 | alreadyKnown = true; |
363 | break; |
364 | } |
365 | } |
366 | |
367 | if (!alreadyKnown) { |
368 | foundTestServers.append(t: info); |
369 | qDebug() << "@@@@@@@@ Adding:" << info.device().address().toString(); |
370 | } |
371 | } |
372 | } |
373 | |
374 | void BtLocalDevice::serviceDiscoveryFinished() |
375 | { |
376 | qDebug() << "###### Service Discovery Finished" ; |
377 | } |
378 | |
379 | void BtLocalDevice::serviceDiscoveryCanceled() |
380 | { |
381 | qDebug() << "###### Service Discovery Canceled" ; |
382 | } |
383 | |
384 | void BtLocalDevice::serviceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error error) |
385 | { |
386 | auto *client = qobject_cast<QBluetoothServiceDiscoveryAgent *>(object: sender()); |
387 | if (!client) |
388 | return; |
389 | qDebug() << "###### Service Discovery Error:" << error << (client ? client->errorString() : QString()); |
390 | } |
391 | |
392 | void BtLocalDevice::dumpServiceDiscovery() |
393 | { |
394 | if (deviceAgent) { |
395 | qDebug() << "Device Discovery active:" << deviceAgent->isActive(); |
396 | qDebug() << "Error:" << deviceAgent->error() << deviceAgent->errorString(); |
397 | const QList<QBluetoothDeviceInfo> list = deviceAgent->discoveredDevices(); |
398 | qDebug() << "Discovered Devices:" << list.count(); |
399 | |
400 | for (const QBluetoothDeviceInfo &info : list) |
401 | qDebug() << info.name() << info.address().toString() << info.rssi(); |
402 | } |
403 | if (serviceAgent) { |
404 | qDebug() << "Service Discovery active:" << serviceAgent->isActive(); |
405 | qDebug() << "Error:" << serviceAgent->error() << serviceAgent->errorString(); |
406 | const QList<QBluetoothServiceInfo> list = serviceAgent->discoveredServices(); |
407 | qDebug() << "Discovered Services:" << list.count(); |
408 | |
409 | for (const QBluetoothServiceInfo &i : list) { |
410 | qDebug() << i.device().address().toString() << i.device().name() << i.serviceName(); |
411 | } |
412 | |
413 | qDebug() << "###### TestServer offered by:" ; |
414 | for (const QBluetoothServiceInfo& found : qAsConst(t&: foundTestServers)) { |
415 | qDebug() << found.device().name() << found.device().address().toString(); |
416 | } |
417 | } |
418 | } |
419 | |
420 | void BtLocalDevice::connectToService() |
421 | { |
422 | if (socket) { |
423 | if (socket->preferredSecurityFlags() != securityFlags) |
424 | socket->setPreferredSecurityFlags(securityFlags); |
425 | socket->connectToService(address: QBluetoothAddress(BTCHAT_DEVICE_ADDR),uuid: QBluetoothUuid(QString(TEST_SERVICE_UUID))); |
426 | } |
427 | } |
428 | |
429 | void BtLocalDevice::connectToServiceViaSearch() |
430 | { |
431 | if (socket) { |
432 | qDebug() << "###### Connecting to service socket" ; |
433 | if (!foundTestServers.isEmpty()) { |
434 | if (socket->preferredSecurityFlags() != securityFlags) |
435 | socket->setPreferredSecurityFlags(securityFlags); |
436 | |
437 | QBluetoothServiceInfo info = foundTestServers.at(i: 0); |
438 | socket->connectToService(service: info); |
439 | } else { |
440 | qWarning() << "Perform search for test service before triggering this function" ; |
441 | } |
442 | } |
443 | } |
444 | |
445 | void BtLocalDevice::disconnectToService() |
446 | { |
447 | if (socket) { |
448 | qDebug() << "###### Disconnecting socket" ; |
449 | socket->disconnectFromService(); |
450 | } |
451 | } |
452 | |
453 | void BtLocalDevice::closeSocket() |
454 | { |
455 | if (socket) { |
456 | qDebug() << "###### Closing socket" ; |
457 | socket->close(); |
458 | } |
459 | |
460 | if (!serverSockets.isEmpty()) { |
461 | qDebug() << "###### Closing server sockets" ; |
462 | for (QBluetoothSocket *s : serverSockets) |
463 | s->close(); |
464 | } |
465 | } |
466 | |
467 | void BtLocalDevice::abortSocket() |
468 | { |
469 | if (socket) { |
470 | qDebug() << "###### Disconnecting socket" ; |
471 | socket->abort(); |
472 | } |
473 | |
474 | if (!serverSockets.isEmpty()) { |
475 | qDebug() << "###### Closing server sockets" ; |
476 | for (QBluetoothSocket *s : serverSockets) |
477 | s->abort(); |
478 | } |
479 | } |
480 | |
481 | void BtLocalDevice::socketConnected() |
482 | { |
483 | qDebug() << "###### Socket connected" ; |
484 | } |
485 | |
486 | void BtLocalDevice::socketDisconnected() |
487 | { |
488 | qDebug() << "###### Socket disconnected" ; |
489 | } |
490 | |
491 | void BtLocalDevice::socketError(QBluetoothSocket::SocketError error) |
492 | { |
493 | auto *client = qobject_cast<QBluetoothSocket *>(object: sender()); |
494 | |
495 | qDebug() << "###### Socket error" << error << (client ? client->errorString() : QString()); |
496 | } |
497 | |
498 | void BtLocalDevice::socketStateChanged(QBluetoothSocket::SocketState state) |
499 | { |
500 | qDebug() << "###### Socket state" << state; |
501 | emit socketStateUpdate(foobar: static_cast<int>(state)); |
502 | } |
503 | |
504 | void BtLocalDevice::dumpSocketInformation() |
505 | { |
506 | if (socket) { |
507 | qDebug() << "*******************************" ; |
508 | qDebug() << "Local info (addr, name, port):" << socket->localAddress().toString() |
509 | << socket->localName() << socket->localPort(); |
510 | qDebug() << "Peer Info (adr, name, port):" << socket->peerAddress().toString() |
511 | << socket->peerName() << socket->peerPort(); |
512 | qDebug() << "socket type:" << socket->socketType(); |
513 | qDebug() << "socket state:" << socket->state(); |
514 | qDebug() << "socket bytesAvailable()" << socket->bytesAvailable(); |
515 | QString tmp; |
516 | switch (socket->error()) { |
517 | case QBluetoothSocket::NoSocketError: tmp += "NoSocketError" ; break; |
518 | case QBluetoothSocket::UnknownSocketError: tmp += "UnknownSocketError" ; break; |
519 | case QBluetoothSocket::HostNotFoundError: tmp += "HostNotFoundError" ; break; |
520 | case QBluetoothSocket::ServiceNotFoundError: tmp += "ServiceNotFound" ; break; |
521 | case QBluetoothSocket::NetworkError: tmp += "NetworkError" ; break; |
522 | //case QBluetoothSocket::OperationError: tmp+= "OperationError"; break; |
523 | case QBluetoothSocket::UnsupportedProtocolError: tmp += "UnsupportedProtocolError" ; break; |
524 | default: tmp+= "Undefined" ; break; |
525 | } |
526 | |
527 | qDebug() << "socket error:" << tmp << socket->errorString(); |
528 | } else { |
529 | qDebug() << "No valid socket existing" ; |
530 | } |
531 | } |
532 | |
533 | void BtLocalDevice::writeData() |
534 | { |
535 | const char * testData = "ABCABC\n" ; |
536 | if (socket && socket->state() == QBluetoothSocket::ConnectedState) { |
537 | socket->write(data: testData); |
538 | } |
539 | for (QBluetoothSocket* client : serverSockets) { |
540 | client->write(data: testData); |
541 | } |
542 | } |
543 | |
544 | void BtLocalDevice::readData() |
545 | { |
546 | if (socket) { |
547 | while (socket->canReadLine()) { |
548 | QByteArray line = socket->readLine().trimmed(); |
549 | qDebug() << ">> peer(" << socket->peerName() << socket->peerAddress() |
550 | << socket->peerPort() << ") local(" |
551 | << socket->localName() << socket->localAddress() << socket->localPort() |
552 | << ")>>" << QString::fromUtf8(str: line.constData(), size: line.length()); |
553 | } |
554 | } |
555 | } |
556 | |
557 | void BtLocalDevice::serverError(QBluetoothServer::Error error) |
558 | { |
559 | qDebug() << "###### Server socket error" << error; |
560 | } |
561 | |
562 | void BtLocalDevice::serverListenPort() |
563 | { |
564 | if (server && localDevice) { |
565 | if (server->isListening() || serviceInfo.isRegistered()) { |
566 | qDebug() << "###### Already listening" << serviceInfo.isRegistered(); |
567 | return; |
568 | } |
569 | |
570 | if (server->securityFlags() != securityFlags) { |
571 | qDebug() << "###### Setting security policy on server socket" << securityFlags; |
572 | server->setSecurityFlags(securityFlags); |
573 | } |
574 | |
575 | qDebug() << "###### Start listening via port" ; |
576 | bool ret = server->listen(address: localDevice->address()); |
577 | qDebug() << "###### Listening(Expecting TRUE):" << ret; |
578 | |
579 | if (!ret) |
580 | return; |
581 | |
582 | QBluetoothServiceInfo::Sequence profileSequence; |
583 | QBluetoothServiceInfo::Sequence classId; |
584 | classId << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::SerialPort)); |
585 | classId << QVariant::fromValue(value: quint16(0x100)); |
586 | profileSequence.append(t: QVariant::fromValue(value: classId)); |
587 | serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::BluetoothProfileDescriptorList, |
588 | value: profileSequence); |
589 | |
590 | classId.clear(); |
591 | classId << QVariant::fromValue(value: QBluetoothUuid(QString(TEST_SERVICE_UUID))); |
592 | classId << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::SerialPort)); |
593 | serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::ServiceClassIds, value: classId); |
594 | |
595 | // Service name, description and provider |
596 | serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::ServiceName, value: tr(s: "Bt Chat Server" )); |
597 | serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::ServiceDescription, |
598 | value: tr(s: "Example bluetooth chat server" )); |
599 | serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::ServiceProvider, value: tr(s: "qt-project.org" )); |
600 | |
601 | // Service UUID set |
602 | serviceInfo.setServiceUuid(QBluetoothUuid(QString(TEST_SERVICE_UUID))); |
603 | |
604 | |
605 | // Service Discoverability |
606 | QBluetoothServiceInfo::Sequence browseSequence; |
607 | browseSequence << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup)); |
608 | serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::BrowseGroupList, value: browseSequence); |
609 | |
610 | // Protocol descriptor list |
611 | QBluetoothServiceInfo::Sequence protocolDescriptorList; |
612 | QBluetoothServiceInfo::Sequence protocol; |
613 | protocol << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::L2cap)); |
614 | if (server->serverType() == QBluetoothServiceInfo::L2capProtocol) |
615 | protocol << QVariant::fromValue(value: server->serverPort()); |
616 | protocolDescriptorList.append(t: QVariant::fromValue(value: protocol)); |
617 | |
618 | if (server->serverType() == QBluetoothServiceInfo::RfcommProtocol) { |
619 | protocol.clear(); |
620 | protocol << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::Rfcomm)) |
621 | << QVariant::fromValue(value: quint8(server->serverPort())); |
622 | protocolDescriptorList.append(t: QVariant::fromValue(value: protocol)); |
623 | } |
624 | serviceInfo.setAttribute(attributeId: QBluetoothServiceInfo::ProtocolDescriptorList, |
625 | value: protocolDescriptorList); |
626 | |
627 | //Register service |
628 | qDebug() << "###### Registering service on" << localDevice->address().toString() << server->serverPort(); |
629 | bool result = serviceInfo.registerService(localAdapter: localDevice->address()); |
630 | if (!result) { |
631 | server->close(); |
632 | qDebug() << "###### Reverting registration due to SDP failure." ; |
633 | } |
634 | } |
635 | |
636 | } |
637 | |
638 | void BtLocalDevice::serverListenUuid() |
639 | { |
640 | if (server) { |
641 | if (server->isListening() || serviceInfo.isRegistered()) { |
642 | qDebug() << "###### Already listening" << serviceInfo.isRegistered(); |
643 | return; |
644 | } |
645 | |
646 | if (server->securityFlags() != securityFlags) { |
647 | qDebug() << "###### Setting security policy on server socket" << securityFlags; |
648 | server->setSecurityFlags(securityFlags); |
649 | } |
650 | |
651 | qDebug() << "###### Start listening via UUID" ; |
652 | serviceInfo = server->listen(uuid: QBluetoothUuid(QString(TEST_SERVICE_UUID)), serviceName: tr(s: "Bt Chat Server" )); |
653 | qDebug() << "###### Listening(Expecting TRUE, TRUE):" << serviceInfo.isRegistered() << serviceInfo.isValid(); |
654 | } |
655 | } |
656 | |
657 | void BtLocalDevice::serverClose() |
658 | { |
659 | if (server) { |
660 | qDebug() << "###### Closing Server socket" ; |
661 | if (serviceInfo.isRegistered()) |
662 | serviceInfo.unregisterService(); |
663 | server->close(); |
664 | } |
665 | } |
666 | |
667 | void BtLocalDevice::serverNewConnection() |
668 | { |
669 | qDebug() << "###### New incoming server connection, pending:" << server->hasPendingConnections(); |
670 | if (!server->hasPendingConnections()) { |
671 | qDebug() << "FAIL: expected pending server connection" ; |
672 | return; |
673 | } |
674 | QBluetoothSocket *client = server->nextPendingConnection(); |
675 | if (!client) { |
676 | qDebug() << "FAIL: Cannot obtain pending server connection" ; |
677 | return; |
678 | } |
679 | |
680 | client->setParent(this); |
681 | connect(sender: client, signal: &QBluetoothSocket::disconnected, receiver: this, slot: &BtLocalDevice::clientSocketDisconnected); |
682 | connect(sender: client, signal: &QIODevice::readyRead, receiver: this, slot: &BtLocalDevice::clientSocketReadyRead); |
683 | connect(sender: client, signal: &QBluetoothSocket::stateChanged, |
684 | receiver: this, slot: &BtLocalDevice::socketStateChanged); |
685 | connect(sender: client, signal: QOverload<QBluetoothSocket::SocketError>::of(ptr: &QBluetoothSocket::error), |
686 | receiver: this, slot: &BtLocalDevice::socketError); |
687 | connect(sender: client, signal: &QBluetoothSocket::connected, receiver: this, slot: &BtLocalDevice::socketConnected); |
688 | connect(sender: client, signal: &QBluetoothSocket::bytesWritten, context: this, slot: [](qint64 bytesWritten){ |
689 | qDebug() << "Bytes Written to Server socket:" << bytesWritten; |
690 | }); |
691 | serverSockets.append(t: client); |
692 | } |
693 | |
694 | void BtLocalDevice::clientSocketDisconnected() |
695 | { |
696 | auto *client = qobject_cast<QBluetoothSocket *>(object: sender()); |
697 | if (!client) |
698 | return; |
699 | |
700 | qDebug() << "######" << "Removing server socket connection" ; |
701 | |
702 | serverSockets.removeOne(t: client); |
703 | client->deleteLater(); |
704 | } |
705 | |
706 | |
707 | void BtLocalDevice::clientSocketReadyRead() |
708 | { |
709 | auto *socket = qobject_cast<QBluetoothSocket *>(object: sender()); |
710 | if (!socket) |
711 | return; |
712 | |
713 | while (socket->canReadLine()) { |
714 | const QByteArray line = socket->readLine().trimmed(); |
715 | QString lineString = QString::fromUtf8(str: line.constData(), size: line.length()); |
716 | qDebug() << ">>(" << server->serverAddress() << server->serverPort() <<")>>" |
717 | << lineString; |
718 | |
719 | //when using the tst_QBluetoothSocket we echo received text back |
720 | //Any line starting with "Echo:" will be echoed |
721 | if (lineString.startsWith(QStringLiteral("Echo:" ))) { |
722 | qDebug() << "Assuming tst_qbluetoothsocket as client. Echoing back." ; |
723 | lineString += QLatin1Char('\n'); |
724 | socket->write(data: lineString.toUtf8()); |
725 | } |
726 | } |
727 | } |
728 | |
729 | |
730 | void BtLocalDevice::dumpServerInformation() |
731 | { |
732 | static QBluetooth::SecurityFlags secFlag = QBluetooth::Authentication; |
733 | if (server) { |
734 | qDebug() << "*******************************" ; |
735 | qDebug() << "server port:" <<server->serverPort() |
736 | << "type:" << server->serverType() |
737 | << "address:" << server->serverAddress().toString(); |
738 | qDebug() << "error:" << server->error(); |
739 | qDebug() << "listening:" << server->isListening() |
740 | << "hasPending:" << server->hasPendingConnections() |
741 | << "maxPending:" << server->maxPendingConnections(); |
742 | qDebug() << "security:" << server->securityFlags() << "Togling security flag" ; |
743 | if (secFlag == QBluetooth::Authentication) |
744 | secFlag = QBluetooth::Encryption; |
745 | else |
746 | secFlag = QBluetooth::Authentication; |
747 | |
748 | //server->setSecurityFlags(secFlag); |
749 | |
750 | for (const QBluetoothSocket *client : qAsConst(t&: serverSockets)) { |
751 | qDebug() << "##" << client->localAddress().toString() |
752 | << client->localName() << client->localPort(); |
753 | qDebug() << "##" << client->peerAddress().toString() |
754 | << client->peerName() << client->peerPort(); |
755 | qDebug() << client->socketType() << client->state(); |
756 | qDebug() << "Pending bytes: " << client->bytesAvailable(); |
757 | QString tmp; |
758 | switch (client->error()) { |
759 | case QBluetoothSocket::NoSocketError: tmp += "NoSocketError" ; break; |
760 | case QBluetoothSocket::UnknownSocketError: tmp += "UnknownSocketError" ; break; |
761 | case QBluetoothSocket::HostNotFoundError: tmp += "HostNotFoundError" ; break; |
762 | case QBluetoothSocket::ServiceNotFoundError: tmp += "ServiceNotFound" ; break; |
763 | case QBluetoothSocket::NetworkError: tmp += "NetworkError" ; break; |
764 | case QBluetoothSocket::UnsupportedProtocolError: tmp += "UnsupportedProtocolError" ; break; |
765 | //case QBluetoothSocket::OperationError: tmp+= "OperationError"; break; |
766 | default: tmp += QString::number(static_cast<int>(client->error())); break; |
767 | } |
768 | |
769 | qDebug() << "socket error:" << tmp << client->errorString(); |
770 | } |
771 | } |
772 | } |
773 | |
774 | void BtLocalDevice::dumpInformation() |
775 | { |
776 | qDebug() << "###### default local device" ; |
777 | dumpLocalDevice(dev: localDevice); |
778 | const QList<QBluetoothHostInfo> list = QBluetoothLocalDevice::allDevices(); |
779 | qDebug() << "Found local devices: " << list.count(); |
780 | for (const QBluetoothHostInfo &info : list) { |
781 | qDebug() << " " << info.address().toString() << " " <<info.name(); |
782 | } |
783 | |
784 | QBluetoothAddress address(QStringLiteral("11:22:33:44:55:66" )); |
785 | QBluetoothLocalDevice temp(address); |
786 | qDebug() << "###### 11:22:33:44:55:66 address valid:" << !address.isNull(); |
787 | dumpLocalDevice(dev: &temp); |
788 | |
789 | QBluetoothAddress address2; |
790 | QBluetoothLocalDevice temp2(address2); |
791 | qDebug() << "###### 00:00:00:00:00:00 address valid:" << !address2.isNull(); |
792 | dumpLocalDevice(dev: &temp2); |
793 | |
794 | const QBluetoothAddress BB(BTCHAT_DEVICE_ADDR); |
795 | qDebug() << "###### Bonding state with" << QString(BTCHAT_DEVICE_ADDR) << ":" << localDevice->pairingStatus(address: BB); |
796 | qDebug() << "###### Bonding state with" << address2.toString() << ": " << localDevice->pairingStatus(address: address2); |
797 | qDebug() << "###### Bonding state with" << address.toString() << ": " << localDevice->pairingStatus(address); |
798 | |
799 | qDebug() << "###### Connected Devices" ; |
800 | const QList<QBluetoothAddress> connectedDevices = localDevice->connectedDevices(); |
801 | for (const QBluetoothAddress &addr : connectedDevices) |
802 | qDebug() << " " << addr.toString(); |
803 | |
804 | qDebug() << "###### Discovered Devices" ; |
805 | if (deviceAgent) { |
806 | const QList<QBluetoothDeviceInfo> devices = deviceAgent->discoveredDevices(); |
807 | for (const QBluetoothDeviceInfo &info : devices) { |
808 | deviceDiscovered(info); |
809 | } |
810 | } |
811 | |
812 | QBluetoothDeviceDiscoveryAgent invalidAgent(QBluetoothAddress("11:22:33:44:55:66" )); |
813 | invalidAgent.start(); |
814 | qDebug() << "######" << "Testing device discovery agent constructor with invalid address" ; |
815 | qDebug() << "######" << (invalidAgent.error() == QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError) |
816 | << "(Expected: true)" ; |
817 | QBluetoothDeviceDiscoveryAgent validAgent(localDevice->address()); |
818 | validAgent.start(); |
819 | qDebug() << "######" << (validAgent.error() == QBluetoothDeviceDiscoveryAgent::NoError) << "(Expected: true)" ; |
820 | |
821 | QBluetoothServiceDiscoveryAgent invalidSAgent(QBluetoothAddress("11:22:33:44:55:66" )); |
822 | invalidSAgent.start(); |
823 | qDebug() << "######" << "Testing service discovery agent constructor with invalid address" ; |
824 | qDebug() << "######" << (invalidSAgent.error() == QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError) |
825 | << "(Expected: true)" ; |
826 | QBluetoothServiceDiscoveryAgent validSAgent(localDevice->address()); |
827 | validSAgent.start(); |
828 | qDebug() << "######" << (validSAgent.error() == QBluetoothServiceDiscoveryAgent::NoError) << "(Expected: true)" ; |
829 | } |
830 | |
831 | void BtLocalDevice::powerOn() |
832 | { |
833 | qDebug() << "Powering on" ; |
834 | localDevice->powerOn(); |
835 | } |
836 | |
837 | void BtLocalDevice::reset() |
838 | { |
839 | emit error(error: static_cast<QBluetoothLocalDevice::Error>(1000)); |
840 | if (serviceAgent) { |
841 | serviceAgent->clear(); |
842 | } |
843 | foundTestServers.clear(); |
844 | } |
845 | |
846 | void BtLocalDevice::dumpLocalDevice(QBluetoothLocalDevice *dev) |
847 | { |
848 | qDebug() << " Valid: " << dev->isValid(); |
849 | qDebug() << " Name" << dev->name(); |
850 | qDebug() << " Address" << dev->address().toString(); |
851 | qDebug() << " HostMode" << dev->hostMode(); |
852 | } |
853 | |