summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/qbluetoothsocket_android.cpp
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@digia.com>2014-07-23 15:19:04 +0200
committerAlex Blasche <alexander.blasche@digia.com>2014-07-24 16:26:54 +0200
commitde6e3732ab6884e8f650cbbf99380800dd305636 (patch)
treea68a5f23ccd7a7732e38ccf831cf1c2ae48a8c4b /src/bluetooth/qbluetoothsocket_android.cpp
parentd3298bd60f7580a22b95d34120cf57270a7c5888 (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/bluetooth/qbluetoothsocket_android.cpp')
-rw-r--r--src/bluetooth/qbluetoothsocket_android.cpp236
1 files changed, 116 insertions, 120 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;
}
}