diff options
Diffstat (limited to 'src/bluetooth/qbluetoothsocket_android.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothsocket_android.cpp | 113 |
1 files changed, 102 insertions, 11 deletions
diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index 85b69882..87c9ffc8 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -57,6 +57,7 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) Q_DECLARE_METATYPE(QAndroidJniObject) +Q_BLUETOOTH_EXPORT bool useReverseUuidWorkAroundConnect = true; /* BluetoothSocket.connect() can block up to 10s. Therefore it must be * in a separate thread. Unfortunately if BluetoothSocket.close() is @@ -79,10 +80,12 @@ class SocketConnectWorker : public QObject Q_OBJECT public: SocketConnectWorker(const QAndroidJniObject& socket, - const QAndroidJniObject& targetUuid) + const QAndroidJniObject& targetUuid, + const QBluetoothUuid& qtTargetUuid) : QObject(), mSocketObject(socket), - mTargetUuid(targetUuid) + mTargetUuid(targetUuid), + mQtTargetUuid(qtTargetUuid) { static int t = qRegisterMetaType<QAndroidJniObject>(); Q_UNUSED(t); @@ -91,7 +94,8 @@ public: signals: void socketConnectDone(const QAndroidJniObject &socket); void socketConnectFailed(const QAndroidJniObject &socket, - const QAndroidJniObject &targetUuid); + const QAndroidJniObject &targetUuid, + const QBluetoothUuid &qtUuid); public slots: void connectSocket() { @@ -103,7 +107,7 @@ public slots: env->ExceptionDescribe(); env->ExceptionClear(); - emit socketConnectFailed(mSocketObject, mTargetUuid); + emit socketConnectFailed(mSocketObject, mTargetUuid, mQtTargetUuid); QThread::currentThread()->quit(); return; } @@ -131,6 +135,8 @@ public slots: private: QAndroidJniObject mSocketObject; QAndroidJniObject mTargetUuid; + // same as mTargetUuid above - just the Qt C++ version rather than jni uuid + QBluetoothUuid mQtTargetUuid; }; class WorkerThread: public QThread @@ -144,10 +150,11 @@ public: // Runs in same thread as QBluetoothSocketPrivate void setupWorker(QBluetoothSocketPrivate* d_ptr, const QAndroidJniObject& socketObject, - const QAndroidJniObject& uuidObject, bool useFallback) + const QAndroidJniObject& uuidObject, bool useFallback, + const QBluetoothUuid& qtUuid = QBluetoothUuid()) { SocketConnectWorker* worker = new SocketConnectWorker( - socketObject, uuidObject); + socketObject, uuidObject, qtUuid); worker->moveToThread(this); connect(this, &QThread::finished, worker, &QObject::deleteLater); @@ -173,6 +180,30 @@ private: QPointer<SocketConnectWorker> workerPointer; }; +/* + * This function is part of a workaround for QTBUG-61392 + * + * Returns null uuid if the given \a serviceUuid is not a uuid + * derived from the Bluetooth base uuid. + */ +static QBluetoothUuid reverseUuid(const QBluetoothUuid &serviceUuid) +{ + if (serviceUuid.isNull()) + return QBluetoothUuid(); + + bool isBaseUuid = false; + serviceUuid.toUInt32(&isBaseUuid); + if (isBaseUuid) + return QBluetoothUuid(); + + const quint128 original = serviceUuid.toUInt128(); + quint128 reversed; + for (int i = 0; i < 16; i++) + reversed.data[15-i] = original.data[i]; + + return QBluetoothUuid(reversed); +} + QBluetoothSocketPrivate::QBluetoothSocketPrivate() : socket(-1), socketType(QBluetoothServiceInfo::UnknownProtocol), @@ -207,7 +238,7 @@ bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol bool QBluetoothSocketPrivate::fallBackConnect(QAndroidJniObject uuid, int channel) { - qCWarning(QT_BT_ANDROID) << "Falling back to workaround."; + qCWarning(QT_BT_ANDROID) << "Falling back to getServiceChannel() workaround."; QAndroidJniEnvironment env; @@ -321,6 +352,60 @@ bool QBluetoothSocketPrivate::fallBackConnect(QAndroidJniObject uuid, int channe return true; } +/* + * Workaround for QTBUG-61392 + */ +bool QBluetoothSocketPrivate::fallBackReversedConnect(const QBluetoothUuid &uuid) +{ + Q_Q(QBluetoothSocket); + + qCWarning(QT_BT_ANDROID) << "Falling back to reverse uuid workaround."; + const QBluetoothUuid reverse = reverseUuid(uuid); + if (reverse.isNull()) + return false; + + //cut leading { and trailing } {xxx-xxx} + QString tempUuid = reverse.toString(); + tempUuid.chop(1); //remove trailing '}' + tempUuid.remove(0, 1); //remove first '{' + + QAndroidJniEnvironment env; + const QAndroidJniObject inputString = QAndroidJniObject::fromString(tempUuid); + const QAndroidJniObject uuidObject = QAndroidJniObject::callStaticObjectMethod("java/util/UUID", "fromString", + "(Ljava/lang/String;)Ljava/util/UUID;", + inputString.object<jstring>()); + + if (secFlags == QBluetooth::NoSecurity) { + qCDebug(QT_BT_ANDROID) << "Connnecting via insecure rfcomm"; + socketObject = remoteDevice.callObjectMethod("createInsecureRfcommSocketToServiceRecord", + "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;", + uuidObject.object<jobject>()); + } else { + qCDebug(QT_BT_ANDROID) << "Connnecting via secure rfcomm"; + socketObject = remoteDevice.callObjectMethod("createRfcommSocketToServiceRecord", + "(Ljava/util/UUID;)Landroid/bluetooth/BluetoothSocket;", + uuidObject.object<jobject>()); + } + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + + socketObject = remoteDevice = QAndroidJniObject(); + errorString = QBluetoothSocket::tr("Cannot connect to %1", + "%1 = uuid").arg(reverse.toString()); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return false; + } + + WorkerThread *workerThread = new WorkerThread(); + workerThread->setupWorker(this, socketObject, uuidObject, USE_FALLBACK); + workerThread->start(); + emit connectJavaSocket(); + + return true; +} /* * The call order during a connectToService() is as follows: @@ -332,10 +417,12 @@ bool QBluetoothSocketPrivate::fallBackConnect(QAndroidJniObject uuid, int channe * 4. if threaded connect fails call defaultSocketConnectFailed() via signals * 5. call fallBackConnect() if Android version 22 or below * -> Android 23+ complete failure of entire connectToService() - * 6. if threaded connect on fallback channel succeeds call socketConnectSuccess() + * 6. call fallBackReversedConnect() if Android version 23 or above + * -> if failure entire connectToService() fails + * 7. if threaded connect on one of above fallbacks succeeds call socketConnectSuccess() * via signals * -> done - * 7. if threaded connect on fallback channel fails call fallbackSocketConnectFailed() + * 8. if threaded connect on fallback channel fails call fallbackSocketConnectFailed() * -> complete failure of entire connectToService() * */ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, @@ -416,7 +503,7 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, } WorkerThread *workerThread = new WorkerThread(); - workerThread->setupWorker(this, socketObject, uuidObject, !USE_FALLBACK); + workerThread->setupWorker(this, socketObject, uuidObject, !USE_FALLBACK, uuid); workerThread->start(); emit connectJavaSocket(); } @@ -482,7 +569,8 @@ void QBluetoothSocketPrivate::socketConnectSuccess(const QAndroidJniObject &sock } void QBluetoothSocketPrivate::defaultSocketConnectFailed( - const QAndroidJniObject &socket, const QAndroidJniObject &targetUuid) + const QAndroidJniObject &socket, const QAndroidJniObject &targetUuid, + const QBluetoothUuid &qtTargetUuid) { Q_Q(QBluetoothSocket); @@ -494,6 +582,9 @@ void QBluetoothSocketPrivate::defaultSocketConnectFailed( bool success = false; if (QtAndroid::androidSdkVersion() <= 22) success = fallBackConnect(targetUuid, FALLBACK_CHANNEL); + else if (useReverseUuidWorkAroundConnect) // version 23+ has Android bug (see QTBUG-61392) + success = fallBackReversedConnect(qtTargetUuid); + if (!success) { errorString = QBluetoothSocket::tr("Connection to service failed"); socketObject = remoteDevice = QAndroidJniObject(); |