summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@qt.io>2017-06-13 15:24:55 +0200
committerAlex Blasche <alexander.blasche@qt.io>2017-06-16 07:35:24 +0000
commit2acddda12f98a5022613d05bd62a3133c7de212b (patch)
treeacc71b9c940c6b9a141c241a40afb0e5eb5aff03 /src
parentffa9e1978bfd168e961bc86301f10aad0f5aa6d6 (diff)
Workaround for Android SDP discovery bug
Due to an Android platform bug, SDP discovery may return the wrong uuid for the remote service. This bug was introduced by Android 6.0.1 and tracked by https://issuetracker.google.com/issues/37076498. The returned UUID is byte swapped. To increase the QBluetoothSocket::connectToService() convenience QBluetoothSocket uses a fallback which attempts to connect to the remote service assuming the uuid was byte swapped. This will only happen if the uuid is not derived from the official Bluetooth base UUID (aka the given UUID is truly custom). There is the slight chance that the reversed UUID is a different service but that chance is very marginal when considering the amount of possible custom UUIDs. Task-number: QTBUG-61392 Change-Id: Ia41d670ab8d0666628f067e174965b698d0f26b0 Reviewed-by: Christian Stromme <christian.stromme@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/bluetooth/qbluetoothsocket_android.cpp113
-rw-r--r--src/bluetooth/qbluetoothsocket_p.h13
2 files changed, 114 insertions, 12 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();
diff --git a/src/bluetooth/qbluetoothsocket_p.h b/src/bluetooth/qbluetoothsocket_p.h
index 956f8f02..c1af9471 100644
--- a/src/bluetooth/qbluetoothsocket_p.h
+++ b/src/bluetooth/qbluetoothsocket_p.h
@@ -127,6 +127,7 @@ public:
#endif
#ifdef QT_ANDROID_BLUETOOTH
bool fallBackConnect(QAndroidJniObject uuid, int channel);
+ bool fallBackReversedConnect(const QBluetoothUuid &uuid);
#endif
@@ -197,7 +198,8 @@ public:
public slots:
void socketConnectSuccess(const QAndroidJniObject &socket);
void defaultSocketConnectFailed(const QAndroidJniObject & socket,
- const QAndroidJniObject &targetUuid);
+ const QAndroidJniObject &targetUuid,
+ const QBluetoothUuid &qtTargetUuid);
void fallbackSocketConnectFailed(const QAndroidJniObject &socket,
const QAndroidJniObject &targetUuid);
void inputThreadError(int errorCode);
@@ -286,6 +288,15 @@ static inline quint64 convertAddress(quint8 (&from)[6], quint64 *to = 0)
return result;
}
+#ifdef Q_OS_ANDROID
+// QTBUG-61392 related
+// Private API to disable the silent behavior to reverse a remote service's
+// UUID. In rare cases the workaround behavior might not be desirable as
+// it may lead to connects to incorrect services.
+extern bool useReverseUuidWorkAroundConnect;
+
+#endif
+
QT_END_NAMESPACE