Warning: That file was not part of the compilation database. It may have many parsing errors.

1/****************************************************************************
2**
3** Copyright (C) 2016 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee>
4** Copyright (C) 2016 The Qt Company Ltd.
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtBluetooth module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qbluetoothsocket.h"
42#include "qbluetoothsocket_android_p.h"
43#include "qbluetoothaddress.h"
44#include "qbluetoothdeviceinfo.h"
45#include "qbluetoothserviceinfo.h"
46#include <QtCore/QLoggingCategory>
47#include <QtCore/QThread>
48#include <QtCore/QTime>
49#include <QtCore/private/qjni_p.h>
50#include <QtAndroidExtras/QAndroidJniEnvironment>
51#include <QtAndroid>
52
53QT_BEGIN_NAMESPACE
54
55Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
56
57#define FALLBACK_CHANNEL 1
58#define USE_FALLBACK true
59
60Q_DECLARE_METATYPE(QAndroidJniObject)
61
62Q_BLUETOOTH_EXPORT bool useReverseUuidWorkAroundConnect = true;
63
64/* BluetoothSocket.connect() can block up to 10s. Therefore it must be
65 * in a separate thread. Unfortunately if BluetoothSocket.close() is
66 * called while connect() is still blocking the resulting behavior is not reliable.
67 * This may well be an Android platform bug. In any case, close() must
68 * be queued up until connect() has returned.
69 *
70 * The WorkerThread manages the connect() and close() calls. Interaction
71 * with the main thread happens via signals and slots. There is an accepted but
72 * undesirable side effect of this approach as the user may call connect()
73 * and close() and the socket would continue to successfully connect to
74 * the remote device just to immidiately close the physical connection again.
75 *
76 * WorkerThread and SocketConnectWorker are cleaned up via the threads
77 * finished() signal.
78 */
79
80class SocketConnectWorker : public QObject
81{
82 Q_OBJECT
83public:
84 SocketConnectWorker(const QAndroidJniObject& socket,
85 const QAndroidJniObject& targetUuid,
86 const QBluetoothUuid& qtTargetUuid)
87 : QObject(),
88 mSocketObject(socket),
89 mTargetUuid(targetUuid),
90 mQtTargetUuid(qtTargetUuid)
91 {
92 static int t = qRegisterMetaType<QAndroidJniObject>();
93 Q_UNUSED(t);
94 }
95
96signals:
97 void socketConnectDone(const QAndroidJniObject &socket);
98 void socketConnectFailed(const QAndroidJniObject &socket,
99 const QAndroidJniObject &targetUuid,
100 const QBluetoothUuid &qtUuid);
101public slots:
102 void connectSocket()
103 {
104 QAndroidJniEnvironment env;
105
106 qCDebug(QT_BT_ANDROID) << "Connecting socket";
107 mSocketObject.callMethod<void>("connect");
108 if (env->ExceptionCheck()) {
109 env->ExceptionDescribe();
110 env->ExceptionClear();
111
112 emit socketConnectFailed(mSocketObject, mTargetUuid, mQtTargetUuid);
113 QThread::currentThread()->quit();
114 return;
115 }
116
117 qCDebug(QT_BT_ANDROID) << "Socket connection established";
118 emit socketConnectDone(mSocketObject);
119 }
120
121 void closeSocket()
122 {
123 qCDebug(QT_BT_ANDROID) << "Executing queued closeSocket()";
124
125 QAndroidJniEnvironment env;
126 mSocketObject.callMethod<void>("close");
127 if (env->ExceptionCheck()) {
128
129 qCWarning(QT_BT_ANDROID) << "Error during closure of abandoned socket";
130 env->ExceptionDescribe();
131 env->ExceptionClear();
132 }
133
134 QThread::currentThread()->quit();
135 }
136
137private:
138 QAndroidJniObject mSocketObject;
139 QAndroidJniObject mTargetUuid;
140 // same as mTargetUuid above - just the Qt C++ version rather than jni uuid
141 QBluetoothUuid mQtTargetUuid;
142};
143
144class WorkerThread: public QThread
145{
146 Q_OBJECT
147public:
148 WorkerThread()
149 : QThread(), workerPointer(0)
150 {
151 }
152
153 // Runs in same thread as QBluetoothSocketPrivateAndroid
154 void setupWorker(QBluetoothSocketPrivateAndroid* d_ptr, const QAndroidJniObject& socketObject,
155 const QAndroidJniObject& uuidObject, bool useFallback,
156 const QBluetoothUuid& qtUuid = QBluetoothUuid())
157 {
158 SocketConnectWorker* worker = new SocketConnectWorker(
159 socketObject, uuidObject, qtUuid);
160 worker->moveToThread(this);
161
162 connect(this, &QThread::finished, worker, &QObject::deleteLater);
163 connect(this, &QThread::finished, this, &QObject::deleteLater);
164 connect(d_ptr, &QBluetoothSocketPrivateAndroid::connectJavaSocket,
165 worker, &SocketConnectWorker::connectSocket);
166 connect(d_ptr, &QBluetoothSocketPrivateAndroid::closeJavaSocket,
167 worker, &SocketConnectWorker::closeSocket);
168 connect(worker, &SocketConnectWorker::socketConnectDone,
169 d_ptr, &QBluetoothSocketPrivateAndroid::socketConnectSuccess);
170 if (useFallback) {
171 connect(worker, &SocketConnectWorker::socketConnectFailed,
172 d_ptr, &QBluetoothSocketPrivateAndroid::fallbackSocketConnectFailed);
173 } else {
174 connect(worker, &SocketConnectWorker::socketConnectFailed,
175 d_ptr, &QBluetoothSocketPrivateAndroid::defaultSocketConnectFailed);
176 }
177
178 workerPointer = worker;
179 }
180
181private:
182 QPointer<SocketConnectWorker> workerPointer;
183};
184
185QBluetoothSocketPrivateAndroid::QBluetoothSocketPrivateAndroid()
186 :
187 inputThread(0)
188{
189 secFlags = QBluetooth::Secure;
190 adapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter",
191 "getDefaultAdapter",
192 "()Landroid/bluetooth/BluetoothAdapter;");
193 qRegisterMetaType<QBluetoothSocket::SocketError>();
194 qRegisterMetaType<QBluetoothSocket::SocketState>();
195}
196
197QBluetoothSocketPrivateAndroid::~QBluetoothSocketPrivateAndroid()
198{
199 if (state != QBluetoothSocket::UnconnectedState)
200 emit closeJavaSocket();
201}
202
203bool QBluetoothSocketPrivateAndroid::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
204{
205 socketType = type;
206 if (socketType == QBluetoothServiceInfo::RfcommProtocol)
207 return true;
208
209 return false;
210}
211
212bool QBluetoothSocketPrivateAndroid::fallBackConnect(QAndroidJniObject uuid, int channel)
213{
214 qCWarning(QT_BT_ANDROID) << "Falling back to getServiceChannel() workaround.";
215
216 QAndroidJniEnvironment env;
217
218 QAndroidJniObject remoteDeviceClass = remoteDevice.callObjectMethod("getClass", "()Ljava/lang/Class;");
219 if (!remoteDeviceClass.isValid()) {
220 qCWarning(QT_BT_ANDROID) << "Could not invoke BluetoothDevice.getClass.";
221 return false;
222 }
223
224 QAndroidJniObject integerObject = QAndroidJniObject::getStaticObjectField(
225 "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
226 if (!integerObject.isValid()) {
227 qCWarning(QT_BT_ANDROID) << "Could not get Integer.TYPE";
228 if (env->ExceptionCheck()) {
229 env->ExceptionDescribe();
230 env->ExceptionClear();
231 }
232
233 return false;
234 }
235
236 jclass classClass = QJNIEnvironmentPrivate::findClass("java/lang/Class");
237 jobjectArray rawArray = env->NewObjectArray(1, classClass,
238 integerObject.object<jobject>());
239 QAndroidJniObject paramTypes(rawArray);
240 env->DeleteLocalRef(rawArray);
241 if (!paramTypes.isValid()) {
242 qCWarning(QT_BT_ANDROID) << "Could not create new Class[]{Integer.TYPE}";
243
244 if (env->ExceptionCheck()) {
245 env->ExceptionDescribe();
246 env->ExceptionClear();
247 }
248 return false;
249 }
250
251 QAndroidJniObject parcelUuid("android/os/ParcelUuid", "(Ljava/util/UUID;)V",
252 uuid.object());
253 if (parcelUuid.isValid()) {
254 jint socketChannel = remoteDevice.callMethod<jint>("getServiceChannel",
255 "(Landroid/os/ParcelUuid;)I",
256 parcelUuid.object());
257 if (env->ExceptionCheck()) {
258 env->ExceptionDescribe();
259 env->ExceptionClear();
260 } else {
261 if (socketChannel
262 == remoteDevice.getStaticField<jint>("android/bluetooth/BluetoothDevice", "ERROR")
263 || socketChannel == -1) {
264 qCWarning(QT_BT_ANDROID) << "Cannot determine RFCOMM service channel.";
265 } else {
266 qCWarning(QT_BT_ANDROID) << "Using found rfcomm channel" << socketChannel;
267 channel = socketChannel;
268 }
269 }
270 }
271
272 QAndroidJniObject method;
273 if (secFlags == QBluetooth::NoSecurity) {
274 qCDebug(QT_BT_ANDROID) << "Connnecting via insecure rfcomm";
275 method = remoteDeviceClass.callObjectMethod(
276 "getMethod",
277 "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
278 QAndroidJniObject::fromString(QLatin1String("createInsecureRfcommSocket")).object<jstring>(),
279 paramTypes.object<jobjectArray>());
280 } else {
281 qCDebug(QT_BT_ANDROID) << "Connnecting via secure rfcomm";
282 method = remoteDeviceClass.callObjectMethod(
283 "getMethod",
284 "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;",
285 QAndroidJniObject::fromString(QLatin1String("createRfcommSocket")).object<jstring>(),
286 paramTypes.object<jobjectArray>());
287 }
288 if (!method.isValid() || env->ExceptionCheck()) {
289 qCWarning(QT_BT_ANDROID) << "Could not invoke getMethod";
290 if (env->ExceptionCheck()) {
291 env->ExceptionDescribe();
292 env->ExceptionClear();
293 }
294 return false;
295 }
296
297 jclass objectClass = QJNIEnvironmentPrivate::findClass("java/lang/Object");
298 QAndroidJniObject channelObject = QAndroidJniObject::callStaticObjectMethod(
299 "java/lang/Integer", "valueOf", "(I)Ljava/lang/Integer;", channel);
300 rawArray = env->NewObjectArray(1, objectClass, channelObject.object<jobject>());
301
302 QAndroidJniObject invokeResult = method.callObjectMethod("invoke",
303 "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
304 remoteDevice.object<jobject>(), rawArray);
305 env->DeleteLocalRef(rawArray);
306 if (!invokeResult.isValid())
307 {
308 qCWarning(QT_BT_ANDROID) << "Invoke Resulted with error.";
309 if (env->ExceptionCheck()) {
310 env->ExceptionDescribe();
311 env->ExceptionClear();
312 }
313
314 return false;
315 }
316
317 socketObject = QAndroidJniObject(invokeResult);
318
319 WorkerThread *workerThread = new WorkerThread();
320 workerThread->setupWorker(this, socketObject, uuid, USE_FALLBACK);
321 workerThread->start();
322 emit connectJavaSocket();
323
324 qCWarning(QT_BT_ANDROID) << "Workaround thread invoked.";
325 return true;
326}
327
328/*
329 * Workaround for QTBUG-61392
330 */
331bool QBluetoothSocketPrivateAndroid::fallBackReversedConnect(const QBluetoothUuid &uuid)
332{
333 Q_Q(QBluetoothSocket);
334
335 qCWarning(QT_BT_ANDROID) << "Falling back to reverse uuid workaround.";
336 const QBluetoothUuid reverse = reverseUuid(uuid);
337 if (reverse.isNull())
338 return false;
339
340 //cut leading { and trailing } {xxx-xxx}
341 QString tempUuid = reverse.toString();
342 tempUuid.chop(1); //remove trailing '}'
343 tempUuid.remove(0, 1); //remove first '{'
344
345 QAndroidJniEnvironment env;
346 const QAndroidJniObject inputString = QAndroidJniObject::fromString(tempUuid);
347 const QAndroidJniObject uuidObject = QAndroidJniObject::callStaticObjectMethod("java/util/UUID", "fromString",
348 "(Ljava/lang/String;)Ljava/util/UUID;",
349 inputString.object<jstring>());
350
351 if (secFlags == QBluetooth::NoSecurity) {
352 qCDebug(QT_BT_ANDROID) << "Connnecting via insecure rfcomm";
353 socketObject = remoteDevice.callObjectMethod("createInsecureRfcommSocketToServiceRecord",
354 "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;",
355 uuidObject.object<jobject>());
356 } else {
357 qCDebug(QT_BT_ANDROID) << "Connnecting via secure rfcomm";
358 socketObject = remoteDevice.callObjectMethod("createRfcommSocketToServiceRecord",
359 "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;",
360 uuidObject.object<jobject>());
361 }
362
363 if (env->ExceptionCheck()) {
364 env->ExceptionDescribe();
365 env->ExceptionClear();
366
367 socketObject = remoteDevice = QAndroidJniObject();
368 errorString = QBluetoothSocket::tr("Cannot connect to %1",
369 "%1 = uuid").arg(reverse.toString());
370 q->setSocketError(QBluetoothSocket::ServiceNotFoundError);
371 q->setSocketState(QBluetoothSocket::UnconnectedState);
372 return false;
373 }
374
375 WorkerThread *workerThread = new WorkerThread();
376 workerThread->setupWorker(this, socketObject, uuidObject, USE_FALLBACK);
377 workerThread->start();
378 emit connectJavaSocket();
379
380 return true;
381}
382
383/*
384 * The call order during a connectToServiceHelper() is as follows:
385 *
386 * 1. call connectToServiceHelper()
387 * 2. wait for execution of SocketConnectThread::run()
388 * 3. if threaded connect succeeds call socketConnectSuccess() via signals
389 * -> done
390 * 4. if threaded connect fails call defaultSocketConnectFailed() via signals
391 * 5. call fallBackConnect() if Android version 22 or below
392 * -> Android 23+ complete failure of entire connectToServiceHelper()
393 * 6. call fallBackReversedConnect() if Android version 23 or above
394 * -> if failure entire connectToServiceHelper() fails
395 * 7. if threaded connect on one of above fallbacks succeeds call socketConnectSuccess()
396 * via signals
397 * -> done
398 * 8. if threaded connect on fallback channel fails call fallbackSocketConnectFailed()
399 * -> complete failure of entire connectToServiceHelper()
400 * */
401void QBluetoothSocketPrivateAndroid::connectToServiceHelper(const QBluetoothAddress &address,
402 const QBluetoothUuid &uuid,
403 QIODevice::OpenMode openMode)
404{
405 Q_Q(QBluetoothSocket);
406 Q_UNUSED(openMode);
407
408 qCDebug(QT_BT_ANDROID) << "connectToServiceHelper()" << address.toString() << uuid.toString();
409
410 q->setSocketState(QBluetoothSocket::ConnectingState);
411
412 if (!adapter.isValid()) {
413 qCWarning(QT_BT_ANDROID) << "Device does not support Bluetooth";
414 errorString = QBluetoothSocket::tr("Device does not support Bluetooth");
415 q->setSocketError(QBluetoothSocket::NetworkError);
416 q->setSocketState(QBluetoothSocket::UnconnectedState);
417 return;
418 }
419
420 const int state = adapter.callMethod<jint>("getState");
421 if (state != 12 ) { //BluetoothAdapter.STATE_ON
422 qCWarning(QT_BT_ANDROID) << "Bt device offline";
423 errorString = QBluetoothSocket::tr("Device is powered off");
424 q->setSocketError(QBluetoothSocket::NetworkError);
425 q->setSocketState(QBluetoothSocket::UnconnectedState);
426 return;
427 }
428
429 QAndroidJniEnvironment env;
430 QAndroidJniObject inputString = QAndroidJniObject::fromString(address.toString());
431 remoteDevice = adapter.callObjectMethod("getRemoteDevice",
432 "(Ljava/lang/String;)Landroid/bluetooth/BluetoothDevice;",
433 inputString.object<jstring>());
434 if (env->ExceptionCheck()) {
435 env->ExceptionDescribe();
436 env->ExceptionClear();
437
438 errorString = QBluetoothSocket::tr("Cannot access address %1", "%1 = Bt address e.g. 11:22:33:44:55:66").arg(address.toString());
439 q->setSocketError(QBluetoothSocket::HostNotFoundError);
440 q->setSocketState(QBluetoothSocket::UnconnectedState);
441 return;
442 }
443
444 //cut leading { and trailing } {xxx-xxx}
445 QString tempUuid = uuid.toString();
446 tempUuid.chop(1); //remove trailing '}'
447 tempUuid.remove(0, 1); //remove first '{'
448
449 inputString = QAndroidJniObject::fromString(tempUuid);
450 QAndroidJniObject uuidObject = QAndroidJniObject::callStaticObjectMethod("java/util/UUID", "fromString",
451 "(Ljava/lang/String;)Ljava/util/UUID;",
452 inputString.object<jstring>());
453
454 if (secFlags == QBluetooth::NoSecurity) {
455 qCDebug(QT_BT_ANDROID) << "Connnecting via insecure rfcomm";
456 socketObject = remoteDevice.callObjectMethod("createInsecureRfcommSocketToServiceRecord",
457 "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;",
458 uuidObject.object<jobject>());
459 } else {
460 qCDebug(QT_BT_ANDROID) << "Connnecting via secure rfcomm";
461 socketObject = remoteDevice.callObjectMethod("createRfcommSocketToServiceRecord",
462 "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;",
463 uuidObject.object<jobject>());
464 }
465
466 if (env->ExceptionCheck()) {
467 env->ExceptionDescribe();
468 env->ExceptionClear();
469
470 socketObject = remoteDevice = QAndroidJniObject();
471 errorString = QBluetoothSocket::tr("Cannot connect to %1 on %2",
472 "%1 = uuid, %2 = Bt address").arg(uuid.toString()).arg(address.toString());
473 q->setSocketError(QBluetoothSocket::ServiceNotFoundError);
474 q->setSocketState(QBluetoothSocket::UnconnectedState);
475 return;
476 }
477
478 WorkerThread *workerThread = new WorkerThread();
479 workerThread->setupWorker(this, socketObject, uuidObject, !USE_FALLBACK, uuid);
480 workerThread->start();
481 emit connectJavaSocket();
482}
483
484void QBluetoothSocketPrivateAndroid::connectToService(
485 const QBluetoothServiceInfo &service, QIODevice::OpenMode openMode)
486{
487 Q_Q(QBluetoothSocket);
488
489 if (q->state() != QBluetoothSocket::UnconnectedState
490 && q->state() != QBluetoothSocket::ServiceLookupState) {
491 qCWarning(QT_BT_ANDROID) << "QBluetoothSocketPrivateAndroid::connectToService called on busy socket";
492 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
493 q->setSocketError(QBluetoothSocket::OperationError);
494 return;
495 }
496
497 // Workaround for QTBUG-75035
498 /* Not all Android devices publish or discover the SPP uuid for serial services.
499 * Also, Android does not permit the detection of the protocol used by a serial
500 * Bluetooth connection.
501 *
502 * Therefore, QBluetoothServiceDiscoveryAgentPrivate::populateDiscoveredServices()
503 * may have to guess what protocol a potential custom uuid uses. The guessing works
504 * reasonably well as long as the SDP discovery finds the SPP uuid. Otherwise
505 * the SPP and rfcomm protocol info is missing in \a service.
506 *
507 * Android only supports RFCOMM (no L2CP). We assume (in favor of user experience)
508 * that a non-RFCOMM protocol implies a missing SPP uuid during discovery but the user
509 * still wanting to connect with the given \a service instance.
510 */
511
512 auto protocol = service.socketProtocol();
513 switch (protocol) {
514 case QBluetoothServiceInfo::L2capProtocol:
515 case QBluetoothServiceInfo::UnknownProtocol:
516 qCWarning(QT_BT_ANDROID) << "Changing socket protocol to RFCOMM";
517 protocol = QBluetoothServiceInfo::RfcommProtocol;
518 break;
519 case QBluetoothServiceInfo::RfcommProtocol:
520 break;
521 }
522
523 if (!ensureNativeSocket(protocol)) {
524 errorString = QBluetoothSocket::tr("Socket type not supported");
525 q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
526 return;
527 }
528 connectToServiceHelper(service.device().address(), service.serviceUuid(), openMode);
529}
530
531void QBluetoothSocketPrivateAndroid::connectToService(
532 const QBluetoothAddress &address, const QBluetoothUuid &uuid,
533 QIODevice::OpenMode openMode)
534{
535 Q_Q(QBluetoothSocket);
536
537 if (q->state() != QBluetoothSocket::UnconnectedState) {
538 qCWarning(QT_BT_ANDROID) << "QBluetoothSocketPrivateAndroid::connectToService called on busy socket";
539 errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
540 q->setSocketError(QBluetoothSocket::OperationError);
541 return;
542 }
543
544 if (q->socketType() == QBluetoothServiceInfo::UnknownProtocol) {
545 qCWarning(QT_BT_ANDROID) << "QBluetoothSocketPrivateAndroid::connectToService cannot "
546 "connect with 'UnknownProtocol' (type provided by given service)";
547 errorString = QBluetoothSocket::tr("Socket type not supported");
548 q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
549 return;
550 }
551
552 if (!ensureNativeSocket(q->socketType())) {
553 errorString = QBluetoothSocket::tr("Socket type not supported");
554 q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
555 return;
556 }
557 connectToServiceHelper(address, uuid, openMode);
558}
559
560void QBluetoothSocketPrivateAndroid::connectToService(
561 const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
562{
563 Q_UNUSED(port);
564 Q_UNUSED(openMode);
565 Q_UNUSED(address);
566
567 Q_Q(QBluetoothSocket);
568
569 errorString = tr("Connecting to port is not supported");
570 q->setSocketError(QBluetoothSocket::ServiceNotFoundError);
571 qCWarning(QT_BT_ANDROID) << "Connecting to port is not supported";
572}
573
574void QBluetoothSocketPrivateAndroid::socketConnectSuccess(const QAndroidJniObject &socket)
575{
576 Q_Q(QBluetoothSocket);
577 QAndroidJniEnvironment env;
578
579 // test we didn't get a success from a previous connect
580 // which was cleaned up late
581 if (socket != socketObject)
582 return;
583
584 if (inputThread) {
585 inputThread->deleteLater();
586 inputThread = 0;
587 }
588
589 inputStream = socketObject.callObjectMethod("getInputStream", "()Ljava/io/InputStream;");
590 outputStream = socketObject.callObjectMethod("getOutputStream", "()Ljava/io/OutputStream;");
591
592 if (env->ExceptionCheck() || !inputStream.isValid() || !outputStream.isValid()) {
593 env->ExceptionDescribe();
594 env->ExceptionClear();
595
596 emit closeJavaSocket();
597 socketObject = inputStream = outputStream = remoteDevice = QAndroidJniObject();
598
599
600 errorString = QBluetoothSocket::tr("Obtaining streams for service failed");
601 q->setSocketError(QBluetoothSocket::NetworkError);
602 q->setSocketState(QBluetoothSocket::UnconnectedState);
603 return;
604 }
605
606 inputThread = new InputStreamThread(this);
607 QObject::connect(inputThread, SIGNAL(dataAvailable()),
608 q, SIGNAL(readyRead()), Qt::QueuedConnection);
609 QObject::connect(inputThread, SIGNAL(error(int)),
610 this, SLOT(inputThreadError(int)), Qt::QueuedConnection);
611
612 if (!inputThread->run()) {
613 //close socket again
614 emit closeJavaSocket();
615
616 socketObject = inputStream = outputStream = remoteDevice = QAndroidJniObject();
617
618 delete inputThread;
619 inputThread = 0;
620
621 errorString = QBluetoothSocket::tr("Input stream thread cannot be started");
622 q->setSocketError(QBluetoothSocket::NetworkError);
623 q->setSocketState(QBluetoothSocket::UnconnectedState);
624 return;
625 }
626
627 // only unbuffered behavior supported at this stage
628 q->setOpenMode(QIODevice::ReadWrite|QIODevice::Unbuffered);
629
630 q->setSocketState(QBluetoothSocket::ConnectedState);
631}
632
633void QBluetoothSocketPrivateAndroid::defaultSocketConnectFailed(
634 const QAndroidJniObject &socket, const QAndroidJniObject &targetUuid,
635 const QBluetoothUuid &qtTargetUuid)
636{
637 Q_Q(QBluetoothSocket);
638
639 // test we didn't get a fail from a previous connect
640 // which was cleaned up late - should be same socket
641 if (socket != socketObject)
642 return;
643
644 bool success = false;
645 if (QtAndroid::androidSdkVersion() <= 22)
646 success = fallBackConnect(targetUuid, FALLBACK_CHANNEL);
647 else if (useReverseUuidWorkAroundConnect) // version 23+ has Android bug (see QTBUG-61392)
648 success = fallBackReversedConnect(qtTargetUuid);
649
650 if (!success) {
651 errorString = QBluetoothSocket::tr("Connection to service failed");
652 socketObject = remoteDevice = QAndroidJniObject();
653 q->setSocketError(QBluetoothSocket::ServiceNotFoundError);
654 q->setSocketState(QBluetoothSocket::UnconnectedState);
655
656 QAndroidJniEnvironment env;
657 env->ExceptionClear(); // just in case
658 qCWarning(QT_BT_ANDROID) << "Workaround failed";
659 }
660}
661
662void QBluetoothSocketPrivateAndroid::fallbackSocketConnectFailed(
663 const QAndroidJniObject &socket, const QAndroidJniObject &targetUuid)
664{
665 Q_UNUSED(targetUuid);
666 Q_Q(QBluetoothSocket);
667
668 // test we didn't get a fail from a previous connect
669 // which was cleaned up late - should be same socket
670 if (socket != socketObject)
671 return;
672
673 qCWarning(QT_BT_ANDROID) << "Socket connect via workaround failed.";
674 errorString = QBluetoothSocket::tr("Connection to service failed");
675 socketObject = remoteDevice = QAndroidJniObject();
676
677 q->setSocketError(QBluetoothSocket::ServiceNotFoundError);
678 q->setSocketState(QBluetoothSocket::UnconnectedState);
679}
680
681void QBluetoothSocketPrivateAndroid::abort()
682{
683 if (state == QBluetoothSocket::UnconnectedState)
684 return;
685
686 if (socketObject.isValid()) {
687 QAndroidJniEnvironment env;
688
689 /*
690 * BluetoothSocket.close() triggers an abort of the input stream
691 * thread because inputStream.read() throws IOException
692 * In turn the thread stops and throws an error which sets
693 * new state, error and emits relevant signals.
694 * See QBluetoothSocketPrivateAndroid::inputThreadError() for details
695 */
696
697 if (inputThread)
698 inputThread->prepareForClosure();
699
700 emit closeJavaSocket();
701
702 inputStream = outputStream = socketObject = remoteDevice = QAndroidJniObject();
703
704 if (inputThread) {
705 // inputThread exists hence we had a successful connect
706 // which means inputThread is responsible for setting Unconnected
707
708 //don't delete here as signals caused by Java Thread are still
709 //going to be emitted
710 //delete occurs in inputThreadError()
711 inputThread = 0;
712 } else {
713 // inputThread doesn't exist hence
714 // we abort in the middle of connect(). WorkerThread will do
715 // close() without further feedback. Therefore we have to set
716 // Unconnected (now) in advance
717 Q_Q(QBluetoothSocket);
718 q->setOpenMode(QIODevice::NotOpen);
719 q->setSocketState(QBluetoothSocket::UnconnectedState);
720 emit q->readChannelFinished();
721 }
722 }
723}
724
725QString QBluetoothSocketPrivateAndroid::localName() const
726{
727 if (adapter.isValid())
728 return adapter.callObjectMethod<jstring>("getName").toString();
729
730 return QString();
731}
732
733QBluetoothAddress QBluetoothSocketPrivateAndroid::localAddress() const
734{
735 QString result;
736 if (adapter.isValid())
737 result = adapter.callObjectMethod("getAddress", "()Ljava/lang/String;").toString();
738
739 return QBluetoothAddress(result);
740}
741
742quint16 QBluetoothSocketPrivateAndroid::localPort() const
743{
744 // Impossible to get channel number with current Android API (Levels 5 to 19)
745 return 0;
746}
747
748QString QBluetoothSocketPrivateAndroid::peerName() const
749{
750 if (!remoteDevice.isValid())
751 return QString();
752
753 return remoteDevice.callObjectMethod("getName", "()Ljava/lang/String;").toString();
754}
755
756QBluetoothAddress QBluetoothSocketPrivateAndroid::peerAddress() const
757{
758 if (!remoteDevice.isValid())
759 return QBluetoothAddress();
760
761 const QString address = remoteDevice.callObjectMethod("getAddress",
762 "()Ljava/lang/String;").toString();
763
764 return QBluetoothAddress(address);
765}
766
767quint16 QBluetoothSocketPrivateAndroid::peerPort() const
768{
769 // Impossible to get channel number with current Android API (Levels 5 to 13)
770 return 0;
771}
772
773qint64 QBluetoothSocketPrivateAndroid::writeData(const char *data, qint64 maxSize)
774{
775 //TODO implement buffered behavior (so far only unbuffered)
776 Q_Q(QBluetoothSocket);
777 if (state != QBluetoothSocket::ConnectedState || !outputStream.isValid()) {
778 qCWarning(QT_BT_ANDROID) << "Socket::writeData: " << state << outputStream.isValid();
779 errorString = QBluetoothSocket::tr("Cannot write while not connected");
780 q->setSocketError(QBluetoothSocket::OperationError);
781 return -1;
782 }
783
784 QAndroidJniEnvironment env;
785 jbyteArray nativeData = env->NewByteArray((qint32)maxSize);
786 env->SetByteArrayRegion(nativeData, 0, (qint32)maxSize, reinterpret_cast<const jbyte*>(data));
787 outputStream.callMethod<void>("write", "([BII)V", nativeData, 0, (qint32)maxSize);
788 env->DeleteLocalRef(nativeData);
789
790 if (env->ExceptionCheck()) {
791 qCWarning(QT_BT_ANDROID) << "Error while writing";
792 env->ExceptionDescribe();
793 env->ExceptionClear();
794 errorString = QBluetoothSocket::tr("Error during write on socket.");
795 q->setSocketError(QBluetoothSocket::NetworkError);
796 return -1;
797 }
798
799 emit q->bytesWritten(maxSize);
800 return maxSize;
801}
802
803qint64 QBluetoothSocketPrivateAndroid::readData(char *data, qint64 maxSize)
804{
805 Q_Q(QBluetoothSocket);
806 if (state != QBluetoothSocket::ConnectedState || !inputThread) {
807 qCWarning(QT_BT_ANDROID) << "Socket::readData: " << state << inputThread ;
808 errorString = QBluetoothSocket::tr("Cannot read while not connected");
809 q->setSocketError(QBluetoothSocket::OperationError);
810 return -1;
811 }
812
813 return inputThread->readData(data, maxSize);
814}
815
816void QBluetoothSocketPrivateAndroid::inputThreadError(int errorCode)
817{
818 Q_Q(QBluetoothSocket);
819
820 if (errorCode != -1) { //magic error which is expected and can be ignored
821 errorString = QBluetoothSocket::tr("Network error during read");
822 q->setSocketError(QBluetoothSocket::NetworkError);
823 }
824
825 //finally we can delete the InputStreamThread
826 InputStreamThread *client = qobject_cast<InputStreamThread *>(sender());
827 if (client)
828 client->deleteLater();
829
830 if (socketObject.isValid()) {
831 //triggered when remote side closed the socket
832 //cleanup internal objects
833 //if it was call to local close()/abort() the objects are cleaned up already
834
835 emit closeJavaSocket();
836
837 inputStream = outputStream = remoteDevice = socketObject = QAndroidJniObject();
838 if (inputThread) {
839 // deleted already above (client->deleteLater())
840 inputThread = 0;
841 }
842 }
843
844 q->setOpenMode(QIODevice::NotOpen);
845 q->setSocketState(QBluetoothSocket::UnconnectedState);
846 emit q->readChannelFinished();
847}
848
849void QBluetoothSocketPrivateAndroid::close()
850{
851 /* This function is called by QBluetoothSocket::close and softer version
852 QBluetoothSocket::disconnectFromService() which difference I do not quite fully understand.
853 Anyways we end up in Android "close" function call.
854 */
855 abort();
856}
857
858bool QBluetoothSocketPrivateAndroid::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
859 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
860{
861 Q_UNUSED(socketDescriptor);
862 Q_UNUSED(socketType)
863 Q_UNUSED(socketState);
864 Q_UNUSED(openMode);
865 qCWarning(QT_BT_ANDROID) << "No socket descriptor support on Android.";
866 return false;
867}
868
869bool QBluetoothSocketPrivateAndroid::setSocketDescriptor(const QAndroidJniObject &socket, QBluetoothServiceInfo::Protocol socketType_,
870 QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
871{
872 Q_Q(QBluetoothSocket);
873
874 if (q->state() != QBluetoothSocket::UnconnectedState || !socket.isValid())
875 return false;
876
877 if (!ensureNativeSocket(socketType_))
878 return false;
879
880 socketObject = socket;
881
882 QAndroidJniEnvironment env;
883 inputStream = socketObject.callObjectMethod("getInputStream", "()Ljava/io/InputStream;");
884 outputStream = socketObject.callObjectMethod("getOutputStream", "()Ljava/io/OutputStream;");
885
886 if (env->ExceptionCheck() || !inputStream.isValid() || !outputStream.isValid()) {
887 env->ExceptionDescribe();
888 env->ExceptionClear();
889
890 //close socket again
891 socketObject.callMethod<void>("close");
892 if (env->ExceptionCheck()) {
893 env->ExceptionDescribe();
894 env->ExceptionClear();
895 }
896
897 socketObject = inputStream = outputStream = remoteDevice = QAndroidJniObject();
898
899
900 errorString = QBluetoothSocket::tr("Obtaining streams for service failed");
901 q->setSocketError(QBluetoothSocket::NetworkError);
902 q->setSocketState(QBluetoothSocket::UnconnectedState);
903 return false;
904 }
905
906 remoteDevice = socketObject.callObjectMethod("getRemoteDevice", "()Landroid/bluetooth/BluetoothDevice;");
907
908 if (inputThread) {
909 inputThread->deleteLater();
910 inputThread = 0;
911 }
912 inputThread = new InputStreamThread(this);
913 QObject::connect(inputThread, SIGNAL(dataAvailable()),
914 q, SIGNAL(readyRead()), Qt::QueuedConnection);
915 QObject::connect(inputThread, SIGNAL(error(int)),
916 this, SLOT(inputThreadError(int)), Qt::QueuedConnection);
917 inputThread->run();
918
919 // WorkerThread manages all sockets for us
920 // When we come through here the socket was already connected by
921 // server socket listener (see QBluetoothServer)
922 // Therefore we only use WorkerThread to potentially close it later on
923 WorkerThread *workerThread = new WorkerThread();
924 workerThread->setupWorker(this, socketObject, QAndroidJniObject(), !USE_FALLBACK);
925 workerThread->start();
926
927 q->setOpenMode(openMode | QIODevice::Unbuffered);
928 q->setSocketState(socketState);
929
930 return true;
931}
932
933qint64 QBluetoothSocketPrivateAndroid::bytesAvailable() const
934{
935 //We cannot access buffer directly as it is part of different thread
936 if (inputThread)
937 return inputThread->bytesAvailable();
938
939 return 0;
940}
941
942qint64 QBluetoothSocketPrivateAndroid::bytesToWrite() const
943{
944 return 0; // nothing because always unbuffered
945}
946
947/*
948 * This function is part of a workaround for QTBUG-61392
949 *
950 * Returns null uuid if the given \a serviceUuid is not a uuid
951 * derived from the Bluetooth base uuid.
952 */
953QBluetoothUuid QBluetoothSocketPrivateAndroid::reverseUuid(const QBluetoothUuid &serviceUuid)
954{
955 if (QtAndroid::androidSdkVersion() < 23)
956 return serviceUuid;
957
958 if (serviceUuid.isNull())
959 return QBluetoothUuid();
960
961 bool isBaseUuid = false;
962 serviceUuid.toUInt32(&isBaseUuid);
963 if (isBaseUuid)
964 return serviceUuid;
965
966 const quint128 original = serviceUuid.toUInt128();
967 quint128 reversed;
968 for (int i = 0; i < 16; i++)
969 reversed.data[15-i] = original.data[i];
970 return QBluetoothUuid{reversed};
971}
972
973bool QBluetoothSocketPrivateAndroid::canReadLine() const
974{
975 // We cannot access buffer directly as it is part of different thread
976 if (inputThread)
977 return inputThread->canReadLine();
978
979 return false;
980}
981
982QT_END_NAMESPACE
983
984#include <qbluetoothsocket_android.moc>
985

Warning: That file was not part of the compilation database. It may have many parsing errors.