diff options
author | Alex Blasche <alexander.blasche@digia.com> | 2014-07-23 15:19:04 +0200 |
---|---|---|
committer | Alex Blasche <alexander.blasche@digia.com> | 2014-07-24 16:26:54 +0200 |
commit | de6e3732ab6884e8f650cbbf99380800dd305636 (patch) | |
tree | a68a5f23ccd7a7732e38ccf831cf1c2ae48a8c4b /src | |
parent | d3298bd60f7580a22b95d34120cf57270a7c5888 (diff) |
Move socket workaround into own fctn and fix possible channel mismatch
Previously the remote rfcomm channel was fixed to 1 which may not
always match the correct rfcomm port.
At the end of the day this is still a workaround for failure to connect
when socket creation via UUID fails. We assume that the remote rfcomm
service can be reached via the channel number.
There is the remote chance that we connect to the wrong remote channel
because getServiceChannel() fails and we attempt to connect to the
hardcoded channel 1. However the workaround significantly reduces the
chance of failure as a few devices (especially on the low end margin)
cannot successfully connect via the UUID.
The patch improves code readability and formatting too.
Task-number: QTBUG-40172
Change-Id: Iae90252c877ca13953a81ea1ed83cc2c73abdf2a
Reviewed-by: firatagdas <firatagdas@gmail.com>
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/bluetooth/qbluetoothsocket_android.cpp | 236 | ||||
-rw-r--r-- | src/bluetooth/qbluetoothsocket_p.h | 7 |
2 files changed, 121 insertions, 122 deletions
diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index 2738a92d..8d9cba2a 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -90,7 +90,117 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, Q_Q(QBluetoothSocket); q->setSocketState(QBluetoothSocket::ConnectingState); - QtConcurrent::run(this, &QBluetoothSocketPrivate::connectToServiceConc, address, uuid, openMode, fallbackServiceChannel); + QtConcurrent::run(this, &QBluetoothSocketPrivate::connectToServiceConc, + address, uuid, openMode, fallbackServiceChannel); +} + +bool QBluetoothSocketPrivate::fallBackConnect(QAndroidJniObject uuid, int channel) +{ + qCWarning(QT_BT_ANDROID) << "Falling back to workaround."; + + QAndroidJniEnvironment env; + jclass remoteDeviceClazz = env->GetObjectClass(remoteDevice.object()); + jmethodID getClassMethod = env->GetMethodID(remoteDeviceClazz, "getClass", "()Ljava/lang/Class;"); + if (!getClassMethod) { + qCWarning(QT_BT_ANDROID) << "BluetoothDevice.getClass method could not be found."; + return false; + } + + + QAndroidJniObject remoteDeviceClass = QAndroidJniObject(env->CallObjectMethod(remoteDevice.object(), getClassMethod)); + if (!remoteDeviceClass.isValid()) { + qCWarning(QT_BT_ANDROID) << "Could not invoke BluetoothDevice.getClass."; + return false; + } + + 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"; + return false; + } + + jobjectArray paramTypes = env->NewObjectArray(1, classClass, integerObject); + if (!paramTypes) { + qCWarning(QT_BT_ANDROID) << "Could not create new Class[]{Integer.TYPE}"; + return false; + } + + QAndroidJniObject parcelUuid("android/os/ParcelUuid", "(Ljava/util/UUID;)V", + uuid.object()); + if (parcelUuid.isValid()) { + jint socketChannel = remoteDevice.callMethod<jint>("getServiceChannel", + "(Landroid/os/ParcelUuid;)I", + parcelUuid.object()); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + if (socketChannel + == remoteDevice.getStaticField<jint>("android/bluetooth/BluetoothDevice", "ERROR")) { + qCWarning(QT_BT_ANDROID) << "Cannot determine RFCOMM service channel."; + } else { + qCWarning(QT_BT_ANDROID) << "Using found rfcomm channel" << socketChannel; + channel = socketChannel; + } + } + + QAndroidJniObject method = remoteDeviceClass.callObjectMethod( + "getMethod", + "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", + QAndroidJniObject::fromString(QLatin1String("createRfcommSocket")).object<jstring>(), + paramTypes); + if (!method.isValid() || env->ExceptionCheck()) { + qCWarning(QT_BT_ANDROID) << "Could not invoke getMethod"; + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + return false; + } + + 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."; + return false; + } + + 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, channel)); + + + 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(); + } + return false; + } + + socketObject = QAndroidJniObject(invokeResult); + socketObject.callMethod<void>("connect"); + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + + qCWarning(QT_BT_ANDROID) << "Socket connect via workaround failed."; + + return false; + } + + qCWarning(QT_BT_ANDROID) << "Workaround invoked."; + return true; } void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &address, @@ -161,131 +271,17 @@ void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &addr socketObject.callMethod<void>("connect"); if (env->ExceptionCheck()) { - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } - - 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<jstring>(), - paramTypes); - if (!method.isValid() || env->ExceptionCheck()) { - qCWarning(QT_BT_ANDROID) << "Could not invoke getMethod"; - if (env->ExceptionCheck()) - { - env->ExceptionDescribe(); - env->ExceptionClear(); - } + env->ExceptionDescribe(); + env->ExceptionClear(); + bool success = fallBackConnect(uuidObject, fallbackServiceChannel); + if (!success) { 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<void>("connect"); - if (env->ExceptionCheck() || socketObject.callMethod<jboolean>("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); + env->ExceptionClear(); //just in case return; } } diff --git a/src/bluetooth/qbluetoothsocket_p.h b/src/bluetooth/qbluetoothsocket_p.h index cde9d33b..bef11327 100644 --- a/src/bluetooth/qbluetoothsocket_p.h +++ b/src/bluetooth/qbluetoothsocket_p.h @@ -98,12 +98,15 @@ public: #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); + void connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, + QIODevice::OpenMode openMode, int fallbackServiceChannel = 1); + bool fallBackConnect(QAndroidJniObject uuid, int channel); #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, int fallbackServiceChannel = 1); + void connectToServiceConc(const QBluetoothAddress &address, const QBluetoothUuid &uuid, + QIODevice::OpenMode openMode, int fallbackServiceChannel = 1); #endif |