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
qbluetoothsocket_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
10#include "android/androidutils_p.h"
11#include "android/jni_android_p.h"
12#include <QCoreApplication>
13#include <QtCore/QLoggingCategory>
14#include <QtCore/QThread>
15#include <QtCore/QTime>
16#include <QtCore/QJniEnvironment>
17#include <QtCore/q26numeric.h>
18
20
21Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
22
23#define USE_FALLBACK true
24
25Q_BLUETOOTH_EXPORT bool useReverseUuidWorkAroundConnect = true;
26
27/* BluetoothSocket.connect() can block up to 10s. Therefore it must be
28 * in a separate thread. Unfortunately if BluetoothSocket.close() is
29 * called while connect() is still blocking the resulting behavior is not reliable.
30 * This may well be an Android platform bug. In any case, close() must
31 * be queued up until connect() has returned.
32 *
33 * The WorkerThread manages the connect() and close() calls. Interaction
34 * with the main thread happens via signals and slots. There is an accepted but
35 * undesirable side effect of this approach as the user may call connect()
36 * and close() and the socket would continue to successfully connect to
37 * the remote device just to immidiately close the physical connection again.
38 *
39 * WorkerThread and SocketConnectWorker are cleaned up via the threads
40 * finished() signal.
41 */
42
44{
46public:
58
61 void socketConnectFailed(const QJniObject &socket,
62 const QJniObject &targetUuid,
63 const QBluetoothUuid &qtUuid);
64public slots:
66 {
68
69 qCDebug(QT_BT_ANDROID) << "Connecting socket";
70 auto methodId = env.findMethod(mSocketObject.objectClass(), "connect", "()V");
71 if (methodId)
76 return;
77 }
78
79 qCDebug(QT_BT_ANDROID) << "Socket connection established";
81 }
82
84 {
85 qCDebug(QT_BT_ANDROID) << "Executing queued closeSocket()";
86
87 mSocketObject.callMethod<void>("close");
89 }
90
91private:
93 QJniObject mTargetUuid;
94 // same as mTargetUuid above - just the Qt C++ version rather than jni uuid
95 QBluetoothUuid mQtTargetUuid;
96};
97
98class WorkerThread: public QThread
99{
101public:
103 : QThread(), workerPointer(0)
104 {
105 }
106
107 // Runs in same thread as QBluetoothSocketPrivateAndroid
134
135private:
137};
138
140 :
141 inputThread(0)
142{
143 secFlags = QBluetooth::Security::Secure;
144 adapter = getDefaultBluetoothAdapter();
145 qRegisterMetaType<QBluetoothSocket::SocketError>();
146 qRegisterMetaType<QBluetoothSocket::SocketState>();
147}
148
150{
151 if (state != QBluetoothSocket::SocketState::UnconnectedState)
152 emit closeJavaSocket();
153}
154
155bool QBluetoothSocketPrivateAndroid::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
156{
157 socketType = type;
158 if (socketType == QBluetoothServiceInfo::RfcommProtocol)
159 return true;
160
161 return false;
162}
163
164/*
165 * Workaround for QTBUG-61392. If the underlying Android bug gets fixed,
166 * we need to consider restoring the non-reversed fallbackConnect from the repository.
167 */
168bool QBluetoothSocketPrivateAndroid::fallBackReversedConnect(const QBluetoothUuid &uuid)
169{
170 Q_Q(QBluetoothSocket);
171
172 qCWarning(QT_BT_ANDROID) << "Falling back to reverse uuid workaround.";
173 const QBluetoothUuid reverse = reverseUuid(uuid);
174 if (reverse.isNull())
175 return false;
176
177 QString tempUuid = reverse.toString(QUuid::WithoutBraces);
178
179 QJniEnvironment env;
180 const QJniObject inputString = QJniObject::fromString(tempUuid);
181 const QJniObject uuidObject = QJniObject::callStaticMethod<QtJniTypes::UUID>(
182 QtJniTypes::Traits<QtJniTypes::UUID>::className(), "fromString",
183 inputString.object<jstring>());
184
185 if (secFlags == QBluetooth::SecurityFlags(QBluetooth::Security::NoSecurity)) {
186 qCDebug(QT_BT_ANDROID) << "Connecting via insecure rfcomm";
187 socketObject = remoteDevice.callMethod<QtJniTypes::BluetoothSocket>(
188 "createInsecureRfcommSocketToServiceRecord",
189 uuidObject.object<QtJniTypes::UUID>());
190 } else {
191 qCDebug(QT_BT_ANDROID) << "Connecting via secure rfcomm";
192 socketObject = remoteDevice.callMethod<QtJniTypes::BluetoothSocket>(
193 "createRfcommSocketToServiceRecord",
194 uuidObject.object<QtJniTypes::UUID>());
195 }
196
197 if (!socketObject.isValid()) {
198 remoteDevice = QJniObject();
199 errorString = QBluetoothSocket::tr("Cannot connect to %1",
200 "%1 = uuid").arg(reverse.toString());
201 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
202 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
203 return false;
204 }
205
206 WorkerThread *workerThread = new WorkerThread();
207 workerThread->setupWorker(this, socketObject, uuidObject, USE_FALLBACK);
208 workerThread->start();
209 emit connectJavaSocket();
210
211 return true;
212}
213
214/*
215 * The call order during a connectToServiceHelper() is as follows:
216 *
217 * 1. call connectToServiceHelper()
218 * 2. wait for execution of SocketConnectThread::run()
219 * 3. if threaded connect succeeds call socketConnectSuccess() via signals
220 * -> done
221 * 4. if threaded connect fails call defaultSocketConnectFailed() via signals
222 * 5. call fallBackReversedConnect()
223 * -> if failure entire connectToServiceHelper() fails
224 * Note: This fallback can be disabled with private API boolean
225 * 6. if threaded connect on one of above fallbacks succeeds call socketConnectSuccess()
226 * via signals
227 * -> done
228 * 7. if threaded connect on fallback channel fails call fallbackSocketConnectFailed()
229 * -> complete failure of entire connectToServiceHelper()
230 * */
231void QBluetoothSocketPrivateAndroid::connectToServiceHelper(const QBluetoothAddress &address,
232 const QBluetoothUuid &uuid,
233 QIODevice::OpenMode openMode)
234{
235 Q_Q(QBluetoothSocket);
236 Q_UNUSED(openMode);
237
238 qCDebug(QT_BT_ANDROID) << "connectToServiceHelper()" << address.toString() << uuid.toString();
239
240 if (!ensureAndroidPermission(QBluetoothPermission::Access)) {
241 qCWarning(QT_BT_ANDROID) << "Bluetooth socket connect failed due to missing permissions";
242 errorString = QBluetoothSocket::tr(
243 "Bluetooth socket connect failed due to missing permissions.");
244 q->setSocketError(QBluetoothSocket::SocketError::MissingPermissionsError);
245 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
246 return;
247 }
248
249 q->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
250
251 if (!adapter.isValid()) {
252 qCWarning(QT_BT_ANDROID) << "Device does not support Bluetooth";
253 errorString = QBluetoothSocket::tr("Device does not support Bluetooth");
254 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
255 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
256 return;
257 }
258
259 const int state = adapter.callMethod<jint>("getState");
260 if (state != 12 ) { //BluetoothAdapter.STATE_ON
261 qCWarning(QT_BT_ANDROID) << "Bluetooth device offline";
262 errorString = QBluetoothSocket::tr("Device is powered off");
263 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
264 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
265 return;
266 }
267
268 QJniEnvironment env;
269 QJniObject inputString = QJniObject::fromString(address.toString());
270 remoteDevice = adapter.callMethod<QtJniTypes::BluetoothDevice>("getRemoteDevice",
271 inputString.object<jstring>());
272 if (!remoteDevice.isValid()) {
273 errorString = QBluetoothSocket::tr("Cannot access address %1", "%1 = Bt address e.g. 11:22:33:44:55:66").arg(address.toString());
274 q->setSocketError(QBluetoothSocket::SocketError::HostNotFoundError);
275 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
276 return;
277 }
278
279 //cut leading { and trailing } {xxx-xxx}
280 const QString tempUuid = uuid.toString(QUuid::WithoutBraces);
281
282 inputString = QJniObject::fromString(tempUuid);
283 const QJniObject uuidObject = QJniObject::callStaticMethod<QtJniTypes::UUID>(
284 QtJniTypes::Traits<QtJniTypes::UUID>::className(), "fromString",
285 inputString.object<jstring>());
286
287 if (secFlags == QBluetooth::SecurityFlags(QBluetooth::Security::NoSecurity)) {
288 qCDebug(QT_BT_ANDROID) << "Connecting via insecure rfcomm";
289 socketObject = remoteDevice.callMethod<QtJniTypes::BluetoothSocket>(
290 "createInsecureRfcommSocketToServiceRecord",
291 uuidObject.object<QtJniTypes::UUID>());
292 } else {
293 qCDebug(QT_BT_ANDROID) << "Connecting via secure rfcomm";
294 socketObject = remoteDevice.callMethod<QtJniTypes::BluetoothSocket>(
295 "createRfcommSocketToServiceRecord",
296 uuidObject.object<QtJniTypes::UUID>());
297 }
298
299 if (!socketObject.isValid()) {
300 remoteDevice = QJniObject();
301 errorString = QBluetoothSocket::tr("Cannot connect to %1 on %2",
302 "%1 = uuid, %2 = Bt address").arg(uuid.toString()).arg(address.toString());
303 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
304 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
305 return;
306 }
307
308 WorkerThread *workerThread = new WorkerThread();
309 workerThread->setupWorker(this, socketObject, uuidObject, !USE_FALLBACK, uuid);
310 workerThread->start();
311 emit connectJavaSocket();
312}
313
315 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
316{
317 Q_Q(QBluetoothSocket);
318
319 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState
320 && q->state() != QBluetoothSocket::SocketState::ServiceLookupState) {
321 qCWarning(QT_BT_ANDROID) << "QBluetoothSocketPrivateAndroid::connectToService called on busy socket";
322 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
323 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
324 return;
325 }
326
327 // Workaround for QTBUG-75035
328 /* Not all Android devices publish or discover the SPP uuid for serial services.
329 * Also, Android does not permit the detection of the protocol used by a serial
330 * Bluetooth connection.
331 *
332 * Therefore, QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices()
333 * may have to guess what protocol a potential custom uuid uses. The guessing works
334 * reasonably well as long as the SDP discovery finds the SPP uuid. Otherwise
335 * the SPP and rfcomm protocol info is missing in \a service.
336 *
337 * Android only supports RFCOMM (no L2CP). We assume (in favor of user experience)
338 * that a non-RFCOMM protocol implies a missing SPP uuid during discovery but the user
339 * still wanting to connect with the given \a service instance.
340 */
341
342 auto protocol = service.socketProtocol();
343 switch (protocol) {
344 case QBluetoothServiceInfo::L2capProtocol:
345 case QBluetoothServiceInfo::UnknownProtocol:
346 qCWarning(QT_BT_ANDROID) << "Changing socket protocol to RFCOMM";
347 protocol = QBluetoothServiceInfo::RfcommProtocol;
348 break;
349 case QBluetoothServiceInfo::RfcommProtocol:
350 break;
351 }
352
353 if (!ensureNativeSocket(protocol)) {
354 errorString = QBluetoothSocket::tr("Socket type not supported");
355 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
356 return;
357 }
358 connectToServiceHelper(service.device().address(), service.serviceUuid(), openMode);
359}
360
361void QBluetoothSocketPrivateAndroid::connectToService(
362 const QBluetoothAddress &address, const QBluetoothUuid &uuid,
363 QIODevice::OpenMode openMode)
364{
365 Q_Q(QBluetoothSocket);
366
367 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState) {
368 qCWarning(QT_BT_ANDROID) << "QBluetoothSocketPrivateAndroid::connectToService called on busy socket";
369 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
370 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
371 return;
372 }
373
374 if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
375 qCWarning(QT_BT_ANDROID) << "QBluetoothSocketPrivateAndroid::connectToService cannot "
376 "connect with 'UnknownProtocol' (type provided by given service)";
377 errorString = QBluetoothSocket::tr("Socket type not supported");
378 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
379 return;
380 }
381
382 if (!ensureNativeSocket(q->socketType())) {
383 errorString = QBluetoothSocket::tr("Socket type not supported");
384 q->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
385 return;
386 }
387 connectToServiceHelper(address, uuid, openMode);
388}
389
390void QBluetoothSocketPrivateAndroid::connectToService(
391 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
392{
393 Q_UNUSED(port);
394 Q_UNUSED(openMode);
395 Q_UNUSED(address);
396
397 Q_Q(QBluetoothSocket);
398
399 errorString = tr("Connecting to port is not supported");
400 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
401 qCWarning(QT_BT_ANDROID) << "Connecting to port is not supported";
402}
403
404void QBluetoothSocketPrivateAndroid::socketConnectSuccess(const QJniObject &socket)
405{
406 Q_Q(QBluetoothSocket);
407 QJniEnvironment env;
408
409 // test we didn't get a success from a previous connect
410 // which was cleaned up late
411 if (socket != socketObject)
412 return;
413
414 if (inputThread) {
415 inputThread->deleteLater();
416 inputThread = 0;
417 }
418
419 inputStream = socketObject.callMethod<QtJniTypes::InputStream>("getInputStream");
420 outputStream = socketObject.callMethod<QtJniTypes::OutputStream>("getOutputStream");
421
422 if (!inputStream.isValid() || !outputStream.isValid()) {
423
424 emit closeJavaSocket();
425 socketObject = inputStream = outputStream = remoteDevice = QJniObject();
426
427
428 errorString = QBluetoothSocket::tr("Obtaining streams for service failed");
429 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
430 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
431 return;
432 }
433
434 inputThread = new InputStreamThread(this);
435 QObject::connect(inputThread, SIGNAL(dataAvailable()),
436 q, SIGNAL(readyRead()), Qt::QueuedConnection);
437 QObject::connect(inputThread, SIGNAL(errorOccurred(int)), this, SLOT(inputThreadError(int)),
438 Qt::QueuedConnection);
439
440 if (!inputThread->run()) {
441 //close socket again
442 emit closeJavaSocket();
443
444 socketObject = inputStream = outputStream = remoteDevice = QJniObject();
445
446 delete inputThread;
447 inputThread = 0;
448
449 errorString = QBluetoothSocket::tr("Input stream thread cannot be started");
450 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
451 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
452 return;
453 }
454
455 // only unbuffered behavior supported at this stage
456 q->setOpenMode(QIODevice::ReadWrite|QIODevice::Unbuffered);
457
458 q->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
459}
460
461void QBluetoothSocketPrivateAndroid::defaultSocketConnectFailed(
462 const QJniObject &socket, const QJniObject &targetUuid,
463 const QBluetoothUuid &qtTargetUuid)
464{
465 Q_UNUSED(targetUuid);
466 Q_Q(QBluetoothSocket);
467
468 // test we didn't get a fail from a previous connect
469 // which was cleaned up late - should be same socket
470 if (socket != socketObject)
471 return;
472
473 if (!useReverseUuidWorkAroundConnect || !fallBackReversedConnect(qtTargetUuid)) {
474 errorString = QBluetoothSocket::tr("Connection to service failed");
475 socketObject = remoteDevice = QJniObject();
476 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
477 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
478 qCWarning(QT_BT_ANDROID) << "Socket connect workaround failed";
479 }
480}
481
483 const QJniObject &socket, const QJniObject &targetUuid)
484{
485 Q_UNUSED(targetUuid);
486 Q_Q(QBluetoothSocket);
487
488 // test we didn't get a fail from a previous connect
489 // which was cleaned up late - should be same socket
490 if (socket != socketObject)
491 return;
492
493 qCWarning(QT_BT_ANDROID) << "Socket connect via workaround failed.";
494 errorString = QBluetoothSocket::tr("Connection to service failed");
495 socketObject = remoteDevice = QJniObject();
496
497 q->setSocketError(QBluetoothSocket::SocketError::ServiceNotFoundError);
498 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
499}
500
502{
503 if (state == QBluetoothSocket::SocketState::UnconnectedState)
504 return;
505
506 if (socketObject.isValid()) {
507 QJniEnvironment env;
508
509 /*
510 * BluetoothSocket.close() triggers an abort of the input stream
511 * thread because inputStream.read() throws IOException
512 * In turn the thread stops and throws an error which sets
513 * new state, error and emits relevant signals.
514 * See QBluetoothSocketPrivateAndroid::inputThreadError() for details
515 */
516
517 if (inputThread)
519
520 emit closeJavaSocket();
521
522 inputStream = outputStream = socketObject = remoteDevice = QJniObject();
523
524 if (inputThread) {
525 // inputThread exists hence we had a successful connect
526 // which means inputThread is responsible for setting Unconnected
527
528 //don't delete here as signals caused by Java Thread are still
529 //going to be emitted
530 //delete occurs in inputThreadError()
531 inputThread = 0;
532 } else {
533 // inputThread doesn't exist hence
534 // we abort in the middle of connect(). WorkerThread will do
535 // close() without further feedback. Therefore we have to set
536 // Unconnected (now) in advance
537 Q_Q(QBluetoothSocket);
538 q->setOpenMode(QIODevice::NotOpen);
539 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
540 emit q->readChannelFinished();
541 }
542 }
543}
544
546{
547 if (!ensureAndroidPermission(QBluetoothPermission::Access)) {
548 qCWarning(QT_BT_ANDROID) << "Bluetooth socket localName() failed due to"
549 "missing permissions";
550 } else if (adapter.isValid()) {
551 return adapter.callMethod<jstring>("getName").toString();
552 }
553
554 return QString();
555}
556
558{
559 QString result;
560
561 if (!ensureAndroidPermission(QBluetoothPermission::Access)) {
562 qCWarning(QT_BT_ANDROID) << "Bluetooth socket localAddress() failed due to"
563 "missing permissions";
564 } else if (adapter.isValid()) {
565 result = adapter.callMethod<jstring>("getAddress").toString();
566 }
567
568 return QBluetoothAddress(result);
569}
570
572{
573 // Impossible to get channel number with current Android API (Levels 5 to 19)
574 return 0;
575}
576
578{
579 if (!remoteDevice.isValid())
580 return QString();
581
582 return remoteDevice.callMethod<jstring>("getName").toString();
583}
584
586{
587 if (!remoteDevice.isValid())
588 return QBluetoothAddress();
589
590 const QString address = remoteDevice.callMethod<jstring>("getAddress").toString();
591 return QBluetoothAddress(address);
592}
593
595{
596 // Impossible to get channel number with current Android API (Levels 5 to 13)
597 return 0;
598}
599
600qint64 QBluetoothSocketPrivateAndroid::writeData(const char *data, qint64 maxSize)
601{
602 //TODO implement buffered behavior (so far only unbuffered)
603 Q_Q(QBluetoothSocket);
604 if (state != QBluetoothSocket::SocketState::ConnectedState || !outputStream.isValid()) {
605 qCWarning(QT_BT_ANDROID) << "Socket::writeData: " << state << outputStream.isValid();
606 errorString = QBluetoothSocket::tr("Cannot write while not connected");
607 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
608 return -1;
609 }
610
611 const qint32 javaSize = q26::saturate_cast<qint32>(maxSize);
612 QJniEnvironment env;
613 jbyteArray nativeData = env->NewByteArray(javaSize);
614 env->SetByteArrayRegion(nativeData, 0, javaSize, reinterpret_cast<const jbyte*>(data));
615 auto methodId = env.findMethod(outputStream.objectClass(),
616 "write",
617 "([BII)V");
618 if (methodId)
619 env->CallVoidMethod(outputStream.object(), methodId, nativeData, 0, javaSize);
620 env->DeleteLocalRef(nativeData);
621
622 if (!methodId || env.checkAndClearExceptions()) {
623 qCWarning(QT_BT_ANDROID) << "Error while writing";
624 errorString = QBluetoothSocket::tr("Error during write on socket.");
625 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
626 return -1;
627 }
628
629 emit q->bytesWritten(javaSize);
630 return javaSize;
631}
632
634{
635 Q_Q(QBluetoothSocket);
636 if (state != QBluetoothSocket::SocketState::ConnectedState || !inputThread) {
637 qCWarning(QT_BT_ANDROID) << "Socket::readData: " << state << inputThread ;
638 errorString = QBluetoothSocket::tr("Cannot read while not connected");
639 q->setSocketError(QBluetoothSocket::SocketError::OperationError);
640 return -1;
641 }
642
643 return inputThread->readData(data, maxSize);
644}
645
647{
648 Q_Q(QBluetoothSocket);
649
650 if (errorCode != -1) { //magic error which is expected and can be ignored
651 errorString = QBluetoothSocket::tr("Network error during read");
652 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
653 }
654
655 //finally we can delete the InputStreamThread
656 InputStreamThread *client = qobject_cast<InputStreamThread *>(sender());
657 if (client)
658 client->deleteLater();
659
660 if (socketObject.isValid()) {
661 //triggered when remote side closed the socket
662 //cleanup internal objects
663 //if it was call to local close()/abort() the objects are cleaned up already
664
665 emit closeJavaSocket();
666
667 inputStream = outputStream = remoteDevice = socketObject = QJniObject();
668 if (inputThread) {
669 // deleted already above (client->deleteLater())
670 inputThread = 0;
671 }
672 }
673
674 q->setOpenMode(QIODevice::NotOpen);
675 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
676 emit q->readChannelFinished();
677}
678
680{
681 /* This function is called by QBluetoothSocket::close and softer version
682 QBluetoothSocket::disconnectFromService() which difference I do not quite fully understand.
683 Anyways we end up in Android "close" function call.
684 */
685 abort();
686}
687
688bool QBluetoothSocketPrivateAndroid::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
689 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
690{
691 Q_UNUSED(socketDescriptor);
692 Q_UNUSED(socketType);
693 Q_UNUSED(socketState);
694 Q_UNUSED(openMode);
695 qCWarning(QT_BT_ANDROID) << "No socket descriptor support on Android.";
696 return false;
697}
698
699bool QBluetoothSocketPrivateAndroid::setSocketDescriptor(const QJniObject &socket, QBluetoothServiceInfo::Protocol socketType_,
700 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
701{
702 Q_Q(QBluetoothSocket);
703
704 if (q->state() != QBluetoothSocket::SocketState::UnconnectedState || !socket.isValid())
705 return false;
706
707 if (!ensureNativeSocket(socketType_))
708 return false;
709
710 socketObject = socket;
711
712 inputStream = socketObject.callMethod<QtJniTypes::InputStream>("getInputStream");
713 outputStream = socketObject.callMethod<QtJniTypes::OutputStream>("getOutputStream");
714
715 if (!inputStream.isValid() || !outputStream.isValid()) {
716
717 //close socket again
718 socketObject.callMethod<void>("close");
719
720 socketObject = inputStream = outputStream = remoteDevice = QJniObject();
721
722
723 errorString = QBluetoothSocket::tr("Obtaining streams for service failed");
724 q->setSocketError(QBluetoothSocket::SocketError::NetworkError);
725 q->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
726 return false;
727 }
728
729 remoteDevice = socketObject.callMethod<QtJniTypes::BluetoothDevice>("getRemoteDevice");
730
731 if (inputThread) {
732 inputThread->deleteLater();
733 inputThread = 0;
734 }
735 inputThread = new InputStreamThread(this);
736 QObject::connect(inputThread, SIGNAL(dataAvailable()),
737 q, SIGNAL(readyRead()), Qt::QueuedConnection);
738 QObject::connect(inputThread, SIGNAL(errorOccurred(int)), this, SLOT(inputThreadError(int)),
739 Qt::QueuedConnection);
741
742 // WorkerThread manages all sockets for us
743 // When we come through here the socket was already connected by
744 // server socket listener (see QBluetoothServer)
745 // Therefore we only use WorkerThread to potentially close it later on
746 WorkerThread *workerThread = new WorkerThread();
747 workerThread->setupWorker(this, socketObject, QJniObject(), !USE_FALLBACK);
748 workerThread->start();
749
750 q->setOpenMode(openMode | QIODevice::Unbuffered);
751 q->setSocketState(socketState);
752
753 return true;
754}
755
757{
758 //We cannot access buffer directly as it is part of different thread
759 if (inputThread)
760 return inputThread->bytesAvailable();
761
762 return 0;
763}
764
766{
767 return 0; // nothing because always unbuffered
768}
769
770/*
771 * This function is part of a workaround for QTBUG-61392
772 *
773 * Returns null uuid if the given \a serviceUuid is not a uuid
774 * derived from the Bluetooth base uuid.
775 */
776QBluetoothUuid QBluetoothSocketPrivateAndroid::reverseUuid(const QBluetoothUuid &serviceUuid)
777{
778 if (serviceUuid.isNull())
779 return QBluetoothUuid();
780
781 bool isBaseUuid = false;
782 serviceUuid.toUInt32(&isBaseUuid);
783 if (isBaseUuid)
784 return serviceUuid;
785
786 const QUuid::Id128Bytes original = serviceUuid.toBytes();
787 QUuid::Id128Bytes reversed;
788 for (int i = 0; i < 16; i++)
789 reversed.data[15-i] = original.data[i];
790 return QBluetoothUuid{reversed};
791}
792
794{
795 // We cannot access buffer directly as it is part of different thread
796 if (inputThread)
798
799 return false;
800}
801
802QT_END_NAMESPACE
803
804#include <qbluetoothsocket_android.moc>
bool setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType, QBluetoothSocket::SocketState socketState=QBluetoothSocket::SocketState::ConnectedState, QBluetoothSocket::OpenMode openMode=QBluetoothSocket::ReadWrite) override
QBluetoothAddress peerAddress() const override
void fallbackSocketConnectFailed(const QJniObject &socket, const QJniObject &targetUuid)
bool setSocketDescriptor(const QJniObject &socket, QBluetoothServiceInfo::Protocol socketType, QBluetoothSocket::SocketState socketState=QBluetoothSocket::SocketState::ConnectedState, QBluetoothSocket::OpenMode openMode=QBluetoothSocket::ReadWrite) override
QBluetoothAddress localAddress() const override
void connectToService(const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode) override
qint64 writeData(const char *data, qint64 maxSize) override
qint64 readData(char *data, qint64 maxSize) override
void socketConnectFailed(const QJniObject &socket, const QJniObject &targetUuid, const QBluetoothUuid &qtUuid)
#define USE_FALLBACK