summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/qbluetoothsocket_android.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth/qbluetoothsocket_android.cpp')
-rw-r--r--src/bluetooth/qbluetoothsocket_android.cpp137
1 files changed, 123 insertions, 14 deletions
diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp
index 56d4f77b..d0b901ae 100644
--- a/src/bluetooth/qbluetoothsocket_android.cpp
+++ b/src/bluetooth/qbluetoothsocket_android.cpp
@@ -46,6 +46,7 @@
#include <QtCore/QTime>
#include <QtCore/private/qjni_p.h>
#include <QtAndroidExtras/QAndroidJniEnvironment>
+#include <QtAndroid>
QT_BEGIN_NAMESPACE
@@ -56,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
@@ -78,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);
@@ -90,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()
{
@@ -102,7 +107,7 @@ public slots:
env->ExceptionDescribe();
env->ExceptionClear();
- emit socketConnectFailed(mSocketObject, mTargetUuid);
+ emit socketConnectFailed(mSocketObject, mTargetUuid, mQtTargetUuid);
QThread::currentThread()->quit();
return;
}
@@ -130,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
@@ -143,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);
@@ -172,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),
@@ -206,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;
@@ -320,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:
@@ -329,11 +415,14 @@ bool QBluetoothSocketPrivate::fallBackConnect(QAndroidJniObject uuid, int channe
* 3. if threaded connect succeeds call socketConnectSuccess() via signals
* -> done
* 4. if threaded connect fails call defaultSocketConnectFailed() via signals
- * 5. call fallBackConnect()
- * 6. if threaded connect on fallback channel succeeds call socketConnectSuccess()
+ * 5. call fallBackConnect() if Android version 22 or below
+ * -> Android 23+ complete failure of entire connectToService()
+ * 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,
@@ -414,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();
}
@@ -480,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);
@@ -489,7 +579,12 @@ void QBluetoothSocketPrivate::defaultSocketConnectFailed(
if (socket != socketObject)
return;
- bool success = fallBackConnect(targetUuid, FALLBACK_CHANNEL);
+ 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();
@@ -627,7 +722,6 @@ qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
env->SetByteArrayRegion(nativeData, 0, (qint32)maxSize, reinterpret_cast<const jbyte*>(data));
outputStream.callMethod<void>("write", "([BII)V", nativeData, 0, (qint32)maxSize);
env->DeleteLocalRef(nativeData);
- emit q->bytesWritten(maxSize);
if (env->ExceptionCheck()) {
qCWarning(QT_BT_ANDROID) << "Error while writing";
@@ -638,6 +732,7 @@ qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
return -1;
}
+ emit q->bytesWritten(maxSize);
return maxSize;
}
@@ -784,6 +879,20 @@ qint64 QBluetoothSocketPrivate::bytesAvailable() const
return 0;
}
+qint64 QBluetoothSocketPrivate::bytesToWrite() const
+{
+ return 0; // nothing because always unbuffered
+}
+
+bool QBluetoothSocketPrivate::canReadLine() const
+{
+ // We cannot access buffer directly as it is part of different thread
+ if (inputThread)
+ return inputThread->canReadLine();
+
+ return false;
+}
+
QT_END_NAMESPACE
#include <qbluetoothsocket_android.moc>