diff options
Diffstat (limited to 'src/bluetooth/qbluetoothsocket_android.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothsocket_android.cpp | 174 |
1 files changed, 136 insertions, 38 deletions
diff --git a/src/bluetooth/qbluetoothsocket_android.cpp b/src/bluetooth/qbluetoothsocket_android.cpp index d499b6ad..ce68d236 100644 --- a/src/bluetooth/qbluetoothsocket_android.cpp +++ b/src/bluetooth/qbluetoothsocket_android.cpp @@ -6,36 +6,28 @@ ** ** This file is part of the QtBluetooth module of the Qt Toolkit. ** -** $QT_BEGIN_LICENSE:LGPL$ +** $QT_BEGIN_LICENSE:LGPL21$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception +** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** ** $QT_END_LICENSE$ ** ****************************************************************************/ @@ -84,16 +76,127 @@ 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); +} + +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, - const QBluetoothUuid &uuid, QIODevice::OpenMode openMode) + const QBluetoothUuid &uuid, QIODevice::OpenMode openMode, int fallbackServiceChannel) { Q_Q(QBluetoothSocket); Q_UNUSED(openMode); @@ -160,16 +263,19 @@ void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &addr socketObject.callMethod<void>("connect"); if (env->ExceptionCheck()) { - if (env->ExceptionCheck()) { - env->ExceptionDescribe(); - env->ExceptionClear(); - } + env->ExceptionDescribe(); + env->ExceptionClear(); - socketObject = remoteDevice = QAndroidJniObject(); - errorString = QBluetoothSocket::tr("Connection to service failed"); - q->setSocketError(QBluetoothSocket::ServiceNotFoundError); - q->setSocketState(QBluetoothSocket::UnconnectedState); - return; + 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); + + env->ExceptionClear(); //just in case + return; + } } if (inputThread) { @@ -229,14 +335,6 @@ void QBluetoothSocketPrivate::connectToServiceConc(const QBluetoothAddress &addr emit q->connected(); } -void QBluetoothSocketPrivate::_q_writeNotify() -{ -} - -void QBluetoothSocketPrivate::_q_readNotify() -{ -} - void QBluetoothSocketPrivate::abort() { if (state == QBluetoothSocket::UnconnectedState) |