From d3298bd60f7580a22b95d34120cf57270a7c5888 Mon Sep 17 00:00:00 2001 From: Firat Agdas Date: Wed, 16 Jul 2014 21:27:31 +0300 Subject: Fixed QTBUG-40172 Android Bluetooth Socket Connection fails in some cases. Change-Id: Ibc4c835a9778bfa220ad6553f2c0140a7727d75d Reviewed-by: Alex Blasche --- src/bluetooth/qbluetoothsocket_android.cpp | 134 +++++++++++++++++++++++++++-- src/bluetooth/qbluetoothsocket_p.h | 6 +- 2 files changed, 130 insertions(+), 10 deletions(-) diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index d499b6ad..2738a92d 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -84,16 +84,17 @@ bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, - QIODevice::OpenMode openMode) + QIODevice::OpenMode openMode, + int fallbackServiceChannel) { Q_Q(QBluetoothSocket); q->setSocketState(QBluetoothSocket::ConnectingState); - QtConcurrent::run(this, &QBluetoothSocketPrivate::connectToServiceConc, address, uuid, openMode); + QtConcurrent::run(this, &QBluetoothSocketPrivate::connectToServiceConc, address, uuid, openMode, fallbackServiceChannel); } void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &address, - const QBluetoothUuid &uuid, QIODevice::OpenMode openMode) + const QBluetoothUuid &uuid, QIODevice::OpenMode openMode, int fallbackServiceChannel) { Q_Q(QBluetoothSocket); Q_UNUSED(openMode); @@ -165,11 +166,128 @@ void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &addr env->ExceptionClear(); } - socketObject = remoteDevice = QAndroidJniObject(); - errorString = QBluetoothSocket::tr("Connection to service failed"); - q->setSocketError(QBluetoothSocket::ServiceNotFoundError); - q->setSocketState(QBluetoothSocket::UnconnectedState); - return; + qCWarning(QT_BT_ANDROID) << "Falling back to workaround."; + + jclass remoteDeviceClazz = env->GetObjectClass(remoteDevice.object()); + jmethodID getClassMethod = env->GetMethodID(remoteDeviceClazz, "getClass", "()Ljava/lang/Class;"); + if (!getClassMethod) { + qCWarning(QT_BT_ANDROID) << "getClass method could not found."; + + errorString = QBluetoothSocket::tr("Connection to service failed"); + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } + + QAndroidJniObject remoteDeviceClass = QAndroidJniObject(env->CallObjectMethod(remoteDevice.object(), getClassMethod)); + if (!remoteDeviceClass.isValid()) { + qCWarning(QT_BT_ANDROID) << "Could not invoke getClass from BluetoothDevice."; + + errorString = QBluetoothSocket::tr("Connection to service failed"); + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } + + jclass classClass = env->FindClass("java/lang/Class"); + jclass integerClass = env->FindClass("java/lang/Integer"); + jfieldID integerType = env->GetStaticFieldID(integerClass, "TYPE", "Ljava/lang/Class;"); + jobject integerObject = env->GetStaticObjectField(integerClass, integerType); + if (!integerObject) { + qCWarning(QT_BT_ANDROID) << "Could not get Integer.TYPE"; + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } + + jobjectArray paramTypes = env->NewObjectArray(1, classClass, integerObject); + if (!paramTypes) { + qCWarning(QT_BT_ANDROID) << "Could not create new Class[]{Integer.TYPE}"; + + errorString = QBluetoothSocket::tr("Connection to service failed"); + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } + + QAndroidJniObject method = remoteDeviceClass.callObjectMethod( + "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", + QAndroidJniObject::fromString(QLatin1String("createRfcommSocket")).object(), + paramTypes); + if (!method.isValid() || env->ExceptionCheck()) { + qCWarning(QT_BT_ANDROID) << "Could not invoke getMethod"; + if (env->ExceptionCheck()) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + errorString = QBluetoothSocket::tr("Connection to service failed"); + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } + + jclass methodClass = env->GetObjectClass(method.object()); + jmethodID invokeMethodId = env->GetMethodID(methodClass, "invoke", "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"); + if (!invokeMethodId) + { + qCWarning(QT_BT_ANDROID) << "Could not invoke method."; + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } + + jmethodID valueOfMethodId = env->GetStaticMethodID(integerClass, "valueOf", "(I)Ljava/lang/Integer;"); + jclass objectClass = env->FindClass("java/lang/Object"); + jobjectArray invokeParams = env->NewObjectArray(1, objectClass, env->CallStaticObjectMethod(integerClass, valueOfMethodId, fallbackServiceChannel)); + + jobject invokeResult = env->CallObjectMethod(method.object(), invokeMethodId, remoteDevice.object(), invokeParams); + if (!invokeResult) + { + qCWarning(QT_BT_ANDROID) << "Invoke Resulted with error."; + if (env->ExceptionCheck()) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } + + socketObject = QAndroidJniObject(invokeResult); + socketObject.callMethod("connect"); + if (env->ExceptionCheck() || socketObject.callMethod("isConnected") == JNI_FALSE) + { + if (env->ExceptionCheck()) + { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + errorString = QBluetoothSocket::tr("Connection to service failed"); + + socketObject = remoteDevice = QAndroidJniObject(); + q->setSocketError(QBluetoothSocket::ServiceNotFoundError); + q->setSocketState(QBluetoothSocket::UnconnectedState); + return; + } } if (inputThread) { diff --git a/src/bluetooth/qbluetoothsocket_p.h b/src/bluetooth/qbluetoothsocket_p.h index a93029de..cde9d33b 100644 --- a/src/bluetooth/qbluetoothsocket_p.h +++ b/src/bluetooth/qbluetoothsocket_p.h @@ -95,13 +95,15 @@ public: ~QBluetoothSocketPrivate(); //On QNX and Android we connect using the uuid not the port -#if defined(QT_QNX_BLUETOOTH) || defined(QT_ANDROID_BLUETOOTH) +#if defined(QT_QNX_BLUETOOTH) void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode); +#elif defined(QT_ANDROID_BLUETOOTH) + void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode, int fallbackServiceChannel = 1); #else void connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode); #endif #ifdef QT_ANDROID_BLUETOOTH - void connectToServiceConc(const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode); + void connectToServiceConc(const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode, int fallbackServiceChannel = 1); #endif -- cgit v1.2.3