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.cpp174
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)