diff options
author | Alex Blasche <alexander.blasche@digia.com> | 2014-02-10 15:37:17 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-02-13 09:47:27 +0100 |
commit | 173d16efb54ccc152f19afb9b1c2a87915fb414b (patch) | |
tree | f07ce85ba2cb973e3c08f3ed84252d92ee1c16de /src/bluetooth/android | |
parent | dd75b1f776695006ca96fd43f995c3ba0549b968 (diff) |
Port QtBluetooth to Android
This is a feature merge to dev targeting Qt 5.3.
Known issues:
-QTBUG-36754: QBluetoothServer::close() crashes
-QTBUG-36763: QBluetothTransferManager port to Android not possible
-QTBUG-36764: Improve QBluetoothLocalDevice::connectedDevices()
-QTBUG-36810: Remove direct use of Android action strings
The above issues and some other minor TODO's will be addressed
until final release time.
Task-number: QTBUG-33792
[ChangeLog][QtBluetooth][Android] QtBluetooth has been ported to
Android.
Change-Id: I31ba83e3b7d6aa68e7258b7e43235de7d1a6e68a
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
Diffstat (limited to 'src/bluetooth/android')
-rw-r--r-- | src/bluetooth/android/android.pri | 17 | ||||
-rw-r--r-- | src/bluetooth/android/androidbroadcastreceiver.cpp | 109 | ||||
-rw-r--r-- | src/bluetooth/android/androidbroadcastreceiver_p.h | 77 | ||||
-rw-r--r-- | src/bluetooth/android/devicediscoverybroadcastreceiver.cpp | 132 | ||||
-rw-r--r-- | src/bluetooth/android/devicediscoverybroadcastreceiver_p.h | 66 | ||||
-rw-r--r-- | src/bluetooth/android/inputstreamthread.cpp | 150 | ||||
-rw-r--r-- | src/bluetooth/android/inputstreamthread_p.h | 78 | ||||
-rw-r--r-- | src/bluetooth/android/jni_android.cpp | 111 | ||||
-rw-r--r-- | src/bluetooth/android/localdevicebroadcastreceiver.cpp | 211 | ||||
-rw-r--r-- | src/bluetooth/android/localdevicebroadcastreceiver_p.h | 72 | ||||
-rw-r--r-- | src/bluetooth/android/serveracceptancethread.cpp | 235 | ||||
-rw-r--r-- | src/bluetooth/android/serveracceptancethread_p.h | 92 | ||||
-rw-r--r-- | src/bluetooth/android/servicediscoverybroadcastreceiver.cpp | 115 | ||||
-rw-r--r-- | src/bluetooth/android/servicediscoverybroadcastreceiver_p.h | 68 |
14 files changed, 1533 insertions, 0 deletions
diff --git a/src/bluetooth/android/android.pri b/src/bluetooth/android/android.pri new file mode 100644 index 00000000..3c8a0380 --- /dev/null +++ b/src/bluetooth/android/android.pri @@ -0,0 +1,17 @@ +PRIVATE_HEADERS += \ + android/inputstreamthread_p.h \ + android/devicediscoverybroadcastreceiver_p.h \ + android/servicediscoverybroadcastreceiver_p.h \ + android/androidbroadcastreceiver_p.h \ + android/localdevicebroadcastreceiver_p.h \ + android/serveracceptancethread_p.h + + +SOURCES += \ + android/inputstreamthread.cpp \ + android/devicediscoverybroadcastreceiver.cpp \ + android/servicediscoverybroadcastreceiver.cpp \ + android/jni_android.cpp \ + android/androidbroadcastreceiver.cpp \ + android/localdevicebroadcastreceiver.cpp \ + android/serveracceptancethread.cpp diff --git a/src/bluetooth/android/androidbroadcastreceiver.cpp b/src/bluetooth/android/androidbroadcastreceiver.cpp new file mode 100644 index 00000000..affa7683 --- /dev/null +++ b/src/bluetooth/android/androidbroadcastreceiver.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee> +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include <android/log.h> +#include "android/androidbroadcastreceiver_p.h" +#include <QtCore/QLoggingCategory> +#include <QtCore/private/qjnihelpers_p.h> +#include <QtGui/QGuiApplication> +#include <QtAndroidExtras/QAndroidJniEnvironment> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + + +AndroidBroadcastReceiver::AndroidBroadcastReceiver(QObject* parent) + : QObject(parent), valid(false) +{ + //get QtActivity + activityObject = QAndroidJniObject(QtAndroidPrivate::activity()); + + broadcastReceiverObject = QAndroidJniObject("org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver"); + if (!broadcastReceiverObject.isValid()) + return; + broadcastReceiverObject.setField<jlong>("qtObject", reinterpret_cast<long>(this)); + + intentFilterObject = QAndroidJniObject("android/content/IntentFilter"); + if (!intentFilterObject.isValid()) + return; + + valid = true; +} + +AndroidBroadcastReceiver::~AndroidBroadcastReceiver() +{ + unregisterReceiver(); +} + +bool AndroidBroadcastReceiver::isValid() const +{ + return valid; +} + +void AndroidBroadcastReceiver::unregisterReceiver() +{ + if (!valid) + return; + + activityObject.callObjectMethod( + "unregisterReceiver", + "(Landroid/content/BroadcastReceiver;)V", + broadcastReceiverObject.object<jobject>()); +} + +void AndroidBroadcastReceiver::addAction(const QString &action) +{ + if (!valid) + return; + + QAndroidJniObject actionString = QAndroidJniObject::fromString(action); + intentFilterObject.callMethod<void>("addAction", "(Ljava/lang/String;)V", actionString.object<jstring>()); + + activityObject.callObjectMethod( + "registerReceiver", + "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;", + broadcastReceiverObject.object<jobject>(), + intentFilterObject.object<jobject>()); +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/android/androidbroadcastreceiver_p.h b/src/bluetooth/android/androidbroadcastreceiver_p.h new file mode 100644 index 00000000..baae6798 --- /dev/null +++ b/src/bluetooth/android/androidbroadcastreceiver_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee> +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#ifndef JNIBROADCASTRECEIVER_H +#define JNIBROADCASTRECEIVER_H +#include <jni.h> +#include <QtCore/QObject> +#include <android/log.h> +#include <QtAndroidExtras/QAndroidJniObject> + +QT_BEGIN_NAMESPACE + +void QtBroadcastReceiver_jniOnReceive(JNIEnv *, jobject, jlong, jobject, jobject); + +class AndroidBroadcastReceiver: public QObject +{ + Q_OBJECT +public: + AndroidBroadcastReceiver(QObject* parent = 0); + virtual ~AndroidBroadcastReceiver(); + + void addAction(const QString &filter); + bool isValid() const; + +protected: + friend void QtBroadcastReceiver_jniOnReceive(JNIEnv *, jobject, jlong, jobject, jobject); + virtual void onReceive(JNIEnv *env, jobject context, jobject intent) = 0; + + void unregisterReceiver(); + + QAndroidJniObject activityObject; + QAndroidJniObject intentFilterObject; + QAndroidJniObject broadcastReceiverObject; + bool valid; +}; + +QT_END_NAMESPACE +#endif // JNIBROADCASTRECEIVER_H diff --git a/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp new file mode 100644 index 00000000..16a6afe4 --- /dev/null +++ b/src/bluetooth/android/devicediscoverybroadcastreceiver.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee> +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include "android/devicediscoverybroadcastreceiver_p.h" +#include <QtCore/QLoggingCategory> +#include <QtBluetooth/QBluetoothAddress> +#include <QtBluetooth/QBluetoothDeviceInfo> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + +DeviceDiscoveryBroadcastReceiver::DeviceDiscoveryBroadcastReceiver(QObject* parent): AndroidBroadcastReceiver(parent) +{ + addAction(QStringLiteral("android.bluetooth.device.action.FOUND")); + addAction(QStringLiteral("android.bluetooth.adapter.action.DISCOVERY_STARTED")); + addAction(QStringLiteral("android.bluetooth.adapter.action.DISCOVERY_FINISHED")); +} + +void DeviceDiscoveryBroadcastReceiver::onReceive(JNIEnv *env, jobject context, jobject intent) +{ + Q_UNUSED(context); + Q_UNUSED(env); + + QAndroidJniObject intentObject(intent); + const QString action = intentObject.callObjectMethod("getAction", "()Ljava/lang/String;").toString(); + + qCDebug(QT_BT_ANDROID) << "DeviceDiscoveryBroadcastReceiver::onReceive() - event:" << action; + + if (action == QStringLiteral("android.bluetooth.adapter.action.DISCOVERY_FINISHED") ) { + emit finished(); + } else if (action == QStringLiteral("android.bluetooth.adapter.action.DISCOVERY_STARTED") ) { + + } else if (action == QStringLiteral("android.bluetooth.device.action.FOUND")) { + //get BluetoothDevice + QAndroidJniObject keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.DEVICE")); + QAndroidJniObject bluetoothDevice = + intentObject.callObjectMethod("getParcelableExtra", + "(Ljava/lang/String;)Landroid/os/Parcelable;", + keyExtra.object<jstring>()); + + if (!bluetoothDevice.isValid()) + return; + + const QString deviceName = bluetoothDevice.callObjectMethod<jstring>("getName").toString(); + const QBluetoothAddress deviceAddress(bluetoothDevice.callObjectMethod<jstring>("getAddress").toString()); + keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.RSSI")); + int rssi = intentObject.callMethod<jshort>("getShortExtra", + "(Ljava/lang/String;S)S", + keyExtra.object<jstring>(), + 0); + QAndroidJniObject bluetoothClass = bluetoothDevice.callObjectMethod("getBluetoothClass", + "()Landroid/bluetooth/BluetoothClass;"); + if (!bluetoothClass.isValid()) + return; + int classType = bluetoothClass.callMethod<jint>("getDeviceClass"); + + + static QList<qint32> services; + if (services.count() == 0) + services << QBluetoothDeviceInfo::PositioningService + << QBluetoothDeviceInfo::NetworkingService + << QBluetoothDeviceInfo::RenderingService + << QBluetoothDeviceInfo::CapturingService + << QBluetoothDeviceInfo::ObjectTransferService + << QBluetoothDeviceInfo::AudioService + << QBluetoothDeviceInfo::TelephonyService + << QBluetoothDeviceInfo::InformationService; + + //Matching BluetoothClass.Service values + qint32 result = 0; + qint32 current = 0; + for (int i = 0; i < services.count(); i++) { + current = services.at(i); + int id = (current << 16); + if (bluetoothClass.callMethod<jboolean>("hasService", "(I)Z", id)) + result |= current; + } + + result = result << 13; + classType |= result; + + QBluetoothDeviceInfo info(deviceAddress, deviceName, classType); + info.setRssi(rssi); + + emit deviceDiscovered(info); + } +} + +QT_END_NAMESPACE + diff --git a/src/bluetooth/android/devicediscoverybroadcastreceiver_p.h b/src/bluetooth/android/devicediscoverybroadcastreceiver_p.h new file mode 100644 index 00000000..6c08b22e --- /dev/null +++ b/src/bluetooth/android/devicediscoverybroadcastreceiver_p.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee> +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#ifndef DEVICEDISCOVERYBROADCASTRECEIVER_H +#define DEVICEDISCOVERYBROADCASTRECEIVER_H + +#include "android/androidbroadcastreceiver_p.h" +#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent> + +QT_BEGIN_NAMESPACE + +class QBluetoothDeviceInfo; + +class DeviceDiscoveryBroadcastReceiver : public AndroidBroadcastReceiver +{ + Q_OBJECT +public: + DeviceDiscoveryBroadcastReceiver(QObject* parent = 0); + virtual void onReceive(JNIEnv *env, jobject context, jobject intent); + +signals: + void deviceDiscovered(const QBluetoothDeviceInfo &info); + void finished(); +}; + +QT_END_NAMESPACE +#endif // DEVICEDISCOVERYBROADCASTRECEIVER_H diff --git a/src/bluetooth/android/inputstreamthread.cpp b/src/bluetooth/android/inputstreamthread.cpp new file mode 100644 index 00000000..df32ee62 --- /dev/null +++ b/src/bluetooth/android/inputstreamthread.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee> +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include <QtAndroidExtras/QAndroidJniEnvironment> + +#include "android/inputstreamthread_p.h" +#include "qbluetoothsocket_p.h" + +QT_BEGIN_NAMESPACE + + +InputStreamThread::InputStreamThread(QBluetoothSocketPrivate *socket) + : QThread(), m_stop(false) +{ + m_socket_p = socket; +} + +void InputStreamThread::run() +{ + qint32 byte; + Q_UNUSED(byte) + while (1) { + { + QMutexLocker locker(&m_mutex); + if (m_stop) + break; + } + readFromInputStream(); + } + + QAndroidJniEnvironment env; + if (m_socket_p->inputStream.isValid()) + m_socket_p->inputStream.callMethod<void>("close"); + + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } +} + +bool InputStreamThread::bytesAvailable() const +{ + QMutexLocker locker(&m_mutex); + return m_socket_p->buffer.size(); +} + +//This runs inside the thread. +void InputStreamThread::readFromInputStream() +{ + QAndroidJniEnvironment env; + + int bufLen = 1000; // Seems to magical number that also low-end products can survive. + jbyteArray nativeArray = env->NewByteArray(bufLen); + + + jint ret = m_socket_p->inputStream.callMethod<jint>("read", "([BII)I", nativeArray, 0, bufLen); + + if (env->ExceptionCheck() || ret < 0) { + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + } + env->DeleteLocalRef(nativeArray); + QMutexLocker lock(&m_mutex); + m_stop = true; + + /* + * We cannot distinguish IOException due to valid closure or due to other error + * Therefore we always have to throw an error and a disconnect signal + * A genuine disconnect wouldn't need the error signal. + * For now we always signal error which implicitly emits disconnect() too. + */ + + emit error(); + return; + } + + if (ret == 0) { + qDebug() << "Nothing to read"; + env->DeleteLocalRef(nativeArray); + return; + } + + QMutexLocker lock(&m_mutex); + char *writePtr = m_socket_p->buffer.reserve(bufLen); + env->GetByteArrayRegion(nativeArray, 0, ret, reinterpret_cast<jbyte*>(writePtr)); + env->DeleteLocalRef(nativeArray); + m_socket_p->buffer.chop(bufLen - ret); + emit dataAvailable(); +} + +void InputStreamThread::stop() +{ + QMutexLocker locker(&m_mutex); + m_stop = true; +} + +qint64 InputStreamThread::readData(char *data, qint64 maxSize) +{ + QMutexLocker locker(&m_mutex); + + if (m_stop) + return -1; + + if (!m_socket_p->buffer.isEmpty()) + return m_socket_p->buffer.read(data, maxSize); + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/android/inputstreamthread_p.h b/src/bluetooth/android/inputstreamthread_p.h new file mode 100644 index 00000000..85852534 --- /dev/null +++ b/src/bluetooth/android/inputstreamthread_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee> +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#ifndef INPUTSTREAMTHREAD_H +#define INPUTSTREAMTHREAD_H + +#include <QtCore/QThread> +#include <QtCore/QMutex> + +QT_BEGIN_NAMESPACE + +class QBluetoothSocketPrivate; + +class InputStreamThread : public QThread +{ + Q_OBJECT +public: + explicit InputStreamThread(QBluetoothSocketPrivate *socket_p); + virtual void run(); + + bool bytesAvailable() const; + void stop(); + + qint64 readData(char *data, qint64 maxSize); +signals: + void dataAvailable(); + void error(); + +private: + void readFromInputStream(); + + QBluetoothSocketPrivate *m_socket_p; + mutable QMutex m_mutex; + bool m_stop; +}; + +QT_END_NAMESPACE + +#endif // INPUTSTREAMTHREAD_H diff --git a/src/bluetooth/android/jni_android.cpp b/src/bluetooth/android/jni_android.cpp new file mode 100644 index 00000000..eb1fc2dd --- /dev/null +++ b/src/bluetooth/android/jni_android.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Lauri Laanmets (Proekspert AS) <lauri.laanmets@eesti.ee> +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include <jni.h> +#include <android/log.h> +#include <QtCore/QLoggingCategory> +#include <QtBluetooth/qbluetoothglobal.h> +#include <QtAndroidExtras/QAndroidJniObject> +#include "android/androidbroadcastreceiver_p.h" + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + +void QtBroadcastReceiver_jniOnReceive(JNIEnv *env, jobject /*javaObject*/, + jlong qtObject, jobject context, jobject intent) +{ + reinterpret_cast<AndroidBroadcastReceiver*>(qtObject)->onReceive(env, context, intent); +} + +static JNINativeMethod methods[] = { + {"jniOnReceive", "(JLandroid/content/Context;Landroid/content/Intent;)V", + (void *) QtBroadcastReceiver_jniOnReceive}, +}; + +static const char logTag[] = "QtBluetooth"; +static const char classErrorMsg[] = "Can't find class \"%s\""; + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ +clazz = env->FindClass(CLASS_NAME); \ +if (!clazz) { \ + __android_log_print(ANDROID_LOG_FATAL, logTag, classErrorMsg, CLASS_NAME); \ + return JNI_FALSE; \ +} + +static bool registerNatives(JNIEnv *env) +{ + jclass clazz; + FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/bluetooth/QtBluetoothBroadcastReceiver"); + + if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "RegisterNatives failed"); + return false; + } + + return true; +} + +Q_BLUETOOTH_EXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* /*reserved*/) +{ + typedef union { + JNIEnv *nativeEnvironment; + void *venv; + } UnionJNIEnvToVoid; + + UnionJNIEnvToVoid uenv; + uenv.venv = 0; + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed"); + return -1; + } + + JNIEnv *env = uenv.nativeEnvironment; + if (!registerNatives(env)) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "registerNatives failed"); + return -1; + } + + if (QT_BT_ANDROID().isDebugEnabled()) + __android_log_print(ANDROID_LOG_INFO, logTag, "Bluetooth start"); + + return JNI_VERSION_1_4; +} diff --git a/src/bluetooth/android/localdevicebroadcastreceiver.cpp b/src/bluetooth/android/localdevicebroadcastreceiver.cpp new file mode 100644 index 00000000..0e81ef22 --- /dev/null +++ b/src/bluetooth/android/localdevicebroadcastreceiver.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include <QtCore/QLoggingCategory> +#include "localdevicebroadcastreceiver_p.h" + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + +LocalDeviceBroadcastReceiver::LocalDeviceBroadcastReceiver(QObject *parent) : + AndroidBroadcastReceiver(parent), previousScanMode(0) +{ + addAction(QStringLiteral("android.bluetooth.device.action.BOND_STATE_CHANGED")); + addAction(QStringLiteral("android.bluetooth.adapter.action.SCAN_MODE_CHANGED")); + addAction(QStringLiteral("android.bluetooth.device.action.ACL_CONNECTED")); + addAction(QStringLiteral("android.bluetooth.device.action.ACL_DISCONNECTED")); + addAction(QStringLiteral("android.bluetooth.device.action.PAIRING_REQUEST")); //API 19 + +} + +void LocalDeviceBroadcastReceiver::onReceive(JNIEnv *env, jobject context, jobject intent) +{ + Q_UNUSED(context); + Q_UNUSED(env); + + QAndroidJniObject intentObject(intent); + const QString action = intentObject.callObjectMethod("getAction", "()Ljava/lang/String;").toString(); + qCDebug(QT_BT_ANDROID) << QStringLiteral("LocalDeviceBroadcastReceiver::onReceive() - event: %1").arg(action); + + if (action == QStringLiteral("android.bluetooth.adapter.action.SCAN_MODE_CHANGED")) { + QAndroidJniObject extrasBundle = + intentObject.callObjectMethod("getExtras","()Landroid/os/Bundle;"); + QAndroidJniObject keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.adapter.extra.SCAN_MODE")); + + int extra = extrasBundle.callMethod<jint>("getInt", + "(Ljava/lang/String;)I", + keyExtra.object<jstring>()); + + if (previousScanMode != extra) { + previousScanMode = extra; + + switch (extra) { + case 20: //BluetoothAdapter.SCAN_MODE_NONE + emit hostModeStateChanged(QBluetoothLocalDevice::HostPoweredOff); + break; + case 21: //BluetoothAdapter.SCAN_MODE_CONNECTABLE + emit hostModeStateChanged(QBluetoothLocalDevice::HostConnectable); + break; + case 23: //BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE + emit hostModeStateChanged(QBluetoothLocalDevice::HostDiscoverable); + break; + default: + qCWarning(QT_BT_ANDROID) << "Unknown Host State"; + break; + } + } + } else if (action == QStringLiteral("android.bluetooth.device.action.BOND_STATE_CHANGED")) { + //get BluetoothDevice + QAndroidJniObject keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.DEVICE")); + QAndroidJniObject bluetoothDevice = + intentObject.callObjectMethod("getParcelableExtra", + "(Ljava/lang/String;)Landroid/os/Parcelable;", + keyExtra.object<jstring>()); + + //get new bond state + keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.BOND_STATE")); + QAndroidJniObject extrasBundle = + intentObject.callObjectMethod("getExtras","()Landroid/os/Bundle;"); + int bondState = extrasBundle.callMethod<jint>("getInt", + "(Ljava/lang/String;)I", + keyExtra.object<jstring>()); + + QBluetoothAddress address(bluetoothDevice.callObjectMethod<jstring>("getAddress").toString()); + if (address.isNull()) + return; + + switch (bondState) { + case 10: //BluetoothDevice.BOND_NONE + emit pairingStateChanged(address, QBluetoothLocalDevice::Unpaired); + break; + case 11: //BluetoothDevice.BOND_BONDING + //we ignore this as Qt doesn't have equivalent API. + break; + case 12: //BluetoothDevice.BOND_BONDED + emit pairingStateChanged(address, QBluetoothLocalDevice::Paired); + break; + default: + qCWarning(QT_BT_ANDROID) << "Unknown BOND_STATE_CHANGED value:" << bondState; + break; + } + } else if (action == QStringLiteral("android.bluetooth.device.action.ACL_DISCONNECTED") || + action == QStringLiteral("android.bluetooth.device.action.ACL_CONNECTED")) { + + const bool isConnectEvent = + action == QStringLiteral("android.bluetooth.device.action.ACL_CONNECTED") ? true : false; + + //get BluetoothDevice + QAndroidJniObject keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.DEVICE")); + QAndroidJniObject bluetoothDevice = + intentObject.callObjectMethod("getParcelableExtra", + "(Ljava/lang/String;)Landroid/os/Parcelable;", + keyExtra.object<jstring>()); + + QBluetoothAddress address(bluetoothDevice.callObjectMethod<jstring>("getAddress").toString()); + if (address.isNull()) + return; + + emit connectDeviceChanges(address, isConnectEvent); + } else if (action == QStringLiteral("android.bluetooth.device.action.PAIRING_REQUEST")) { + + QAndroidJniObject keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.PAIRING_VARIANT")); + int variant = intentObject.callMethod<jint>("getIntExtra", + "(Ljava/lang/String;I)I", + keyExtra.object<jstring>(), + -1); + + int key = -1; + switch (variant) { + case -1: //ignore -> no pairing variant set + return; + case 2: //BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION + { + keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.PAIRING_KEY")); + key = intentObject.callMethod<jint>("getIntExtra", + "(Ljava/lang/String;I)I", + keyExtra.object<jstring>(), + -1); + if (key == -1) + return; + + keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.DEVICE")); + QAndroidJniObject bluetoothDevice = + intentObject.callObjectMethod("getParcelableExtra", + "(Ljava/lang/String;)Landroid/os/Parcelable;", + keyExtra.object<jstring>()); + + //we need to keep a reference around in case the user confirms later on + pairingDevice = bluetoothDevice; + + QBluetoothAddress address(bluetoothDevice.callObjectMethod<jstring>("getAddress").toString()); + + //User has choice to confirm or not. If no confirmation is happening + //the OS default pairing dialog can be used or timeout occurs. + emit pairingDisplayConfirmation(address, QString::number(key)); + break; + } + default: + qCWarning(QT_BT_ANDROID) << "Unknown pairing variant: " << variant; + return; + } + } +} + +bool LocalDeviceBroadcastReceiver::pairingConfirmation(bool accept) +{ + if (!pairingDevice.isValid()) + return false; + + bool success = pairingDevice.callMethod<jboolean>("setPairingConfirmation", + "(Z)Z", accept); + pairingDevice = QAndroidJniObject(); + return success; +} + +QT_END_NAMESPACE diff --git a/src/bluetooth/android/localdevicebroadcastreceiver_p.h b/src/bluetooth/android/localdevicebroadcastreceiver_p.h new file mode 100644 index 00000000..ddd65d92 --- /dev/null +++ b/src/bluetooth/android/localdevicebroadcastreceiver_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include "android/androidbroadcastreceiver_p.h" +#include <QtBluetooth/QBluetoothAddress> +#include <QtBluetooth/QBluetoothLocalDevice> + +#ifndef LOCALDEVICEBROADCASTRECEIVER_H +#define LOCALDEVICEBROADCASTRECEIVER_H + +QT_BEGIN_NAMESPACE + +class LocalDeviceBroadcastReceiver : public AndroidBroadcastReceiver +{ + Q_OBJECT +public: + explicit LocalDeviceBroadcastReceiver(QObject *parent = 0); + virtual ~LocalDeviceBroadcastReceiver() {} + virtual void onReceive(JNIEnv *env, jobject context, jobject intent); + bool pairingConfirmation(bool accept); + +signals: + void hostModeStateChanged(QBluetoothLocalDevice::HostMode state); + void pairingStateChanged(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing); + void connectDeviceChanges(const QBluetoothAddress &address, bool isConnectEvent); + void pairingDisplayConfirmation(const QBluetoothAddress &address, const QString& pin); +private: + int previousScanMode; + QAndroidJniObject pairingDevice; +}; + +QT_END_NAMESPACE + +#endif // LOCALDEVICEBROADCASTRECEIVER_H diff --git a/src/bluetooth/android/serveracceptancethread.cpp b/src/bluetooth/android/serveracceptancethread.cpp new file mode 100644 index 00000000..88a92478 --- /dev/null +++ b/src/bluetooth/android/serveracceptancethread.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include <QtCore/QLoggingCategory> +#include <QtAndroidExtras/QAndroidJniEnvironment> + +#include "android/serveracceptancethread_p.h" + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + +ServerAcceptanceThread::ServerAcceptanceThread(QObject *parent) : + QThread(parent), m_stop(false), maxPendingConnections(1) +{ + btAdapter = QAndroidJniObject::callStaticObjectMethod("android/bluetooth/BluetoothAdapter", + "getDefaultAdapter", + "()Landroid/bluetooth/BluetoothAdapter;"); +} + +ServerAcceptanceThread::~ServerAcceptanceThread() +{ + Q_ASSERT(!isRunning()); + QMutexLocker lock(&m_mutex); + shutdownPendingConnections(); +} + +void ServerAcceptanceThread::setServiceDetails(const QBluetoothUuid &uuid, + const QString &serviceName, + QBluetooth::SecurityFlags securityFlags) +{ + QMutexLocker lock(&m_mutex); + m_uuid = uuid; + m_serviceName = serviceName; + secFlags = securityFlags; +} + +void ServerAcceptanceThread::run() +{ + m_mutex.lock(); + + qCDebug(QT_BT_ANDROID) << "Starting ServerSocketAccept thread"; + if (!validSetup()) { + qCWarning(QT_BT_ANDROID) << "Invalid Server Socket setup"; + m_mutex.unlock(); + return; + } + + shutdownPendingConnections(); + + m_stop = false; + + QString tempUuid = m_uuid.toString(); + tempUuid.chop(1); //remove trailing '}' + tempUuid.remove(0,1); //remove first '{' + + QAndroidJniEnvironment env; + QAndroidJniObject inputString = QAndroidJniObject::fromString(tempUuid); + QAndroidJniObject uuidObject = QAndroidJniObject::callStaticObjectMethod( + "java/util/UUID", "fromString", + "(Ljava/lang/String;)Ljava/util/UUID;", + inputString.object<jstring>()); + inputString = QAndroidJniObject::fromString(m_serviceName); + if (((int)secFlags) == 0) { //no form of security flag set + qCDebug(QT_BT_ANDROID) << "InSecure listening"; + btServerSocket = btAdapter.callObjectMethod("listenUsingInsecureRfcommWithServiceRecord", + "(Ljava/lang/String;Ljava/util/UUID;)Landroid/bluetooth/BluetoothServerSocket;", + inputString.object<jstring>(), + uuidObject.object<jobject>()); + } else { + qCDebug(QT_BT_ANDROID) << "Secure listening"; + btServerSocket = btAdapter.callObjectMethod("listenUsingRfcommWithServiceRecord", + "(Ljava/lang/String;Ljava/util/UUID;)Landroid/bluetooth/BluetoothServerSocket;", + inputString.object<jstring>(), + uuidObject.object<jobject>()); + } + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + qCWarning(QT_BT_ANDROID) << "Cannot setup rfcomm socket listener"; + m_mutex.unlock(); + return; + } + + if (!btServerSocket.isValid()) { + qCWarning(QT_BT_ANDROID) << "Invalid BluetoothServerSocket"; + m_mutex.unlock(); + return; + } + + while (!m_stop) { + m_mutex.unlock(); + + qCDebug(QT_BT_ANDROID) << "Waiting for new incoming socket"; + QAndroidJniEnvironment env; + + //this call blocks until we see an incoming connection + QAndroidJniObject socket = btServerSocket.callObjectMethod("accept", + "()Landroid/bluetooth/BluetoothSocket;"); + + qCDebug(QT_BT_ANDROID) << "New socket accepted: ->" << socket.isValid(); + bool exceptionOccurred = false; + if (env->ExceptionCheck()) { + env->ExceptionDescribe(); + env->ExceptionClear(); + exceptionOccurred = true; + } + + m_mutex.lock(); + + if (exceptionOccurred || m_stop) { + //if m_stop is true there is nothing really to be done but exit + m_stop = true; + } else if (socket.isValid()){ + if (pendingSockets.count() < maxPendingConnections) { + pendingSockets.append(socket); + emit newConnection(); + } else { + qCWarning(QT_BT_ANDROID) << "Refusing connection due to limited pending socket queue"; + socket.callMethod<void>("close"); + if (env->ExceptionCheck()) { + qCWarning(QT_BT_ANDROID) << "Error during refusal of new socket"; + env->ExceptionDescribe(); + env->ExceptionClear(); + } + + + } + } else { + //should never happen as invalid socket should cause exception + qCWarning(QT_BT_ANDROID) << "Invalid state during server socket accept"; + } + } + + m_uuid = QBluetoothUuid(); + m_serviceName = QString(); + btServerSocket = QAndroidJniObject(); + m_mutex.unlock(); +} + +void ServerAcceptanceThread::stop() +{ + QMutexLocker lock(&m_mutex); + m_stop = true; + + QAndroidJniEnvironment env; + if (btServerSocket.isValid()) { + qCDebug(QT_BT_ANDROID) << "Closing server socket"; + btServerSocket.callMethod<void>("close"); + if (env->ExceptionCheck()) { + qCWarning(QT_BT_ANDROID) << "Exception during closure of server socket"; + env->ExceptionDescribe(); + env->ExceptionClear(); + } + qCDebug(QT_BT_ANDROID) << "Closing server socket111"; + } +} + +bool ServerAcceptanceThread::hasPendingConnections() const +{ + QMutexLocker lock(&m_mutex); + return (pendingSockets.count() > 0); +} + +/* + * Returns the next pending connection or an invalid JNI object. + * Note that even a stopped thread may still have pending + * connections. Pending connections are only terminated upon + * thread restart or destruction. + */ +QAndroidJniObject ServerAcceptanceThread::nextPendingConnection() +{ + QMutexLocker lock(&m_mutex); + if (pendingSockets.isEmpty()) + return QAndroidJniObject(); + else + return pendingSockets.takeFirst(); +} + +void ServerAcceptanceThread::setMaxPendingConnections(int maximumCount) +{ + QMutexLocker lock(&m_mutex); + maxPendingConnections = maximumCount; +} + +//must be run inside the lock but doesn't lock by itself +bool ServerAcceptanceThread::validSetup() const +{ + return (!m_uuid.isNull() && !m_serviceName.isEmpty()); +} + +//must be run inside the lock but doesn't lock by itself +void ServerAcceptanceThread::shutdownPendingConnections() +{ + while (!pendingSockets.isEmpty()) { + QAndroidJniObject socket = pendingSockets.takeFirst(); + socket.callMethod<void>("close"); + } +} diff --git a/src/bluetooth/android/serveracceptancethread_p.h b/src/bluetooth/android/serveracceptancethread_p.h new file mode 100644 index 00000000..1297e48f --- /dev/null +++ b/src/bluetooth/android/serveracceptancethread_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#ifndef SERVERACCEPTANCETHREAD_H +#define SERVERACCEPTANCETHREAD_H + +#include <QtCore/QMutex> +#include <QtCore/QThread> +#include <QtAndroidExtras/QAndroidJniObject> +#include <QtBluetooth/QBluetoothUuid> +#include "qbluetooth.h" + + +class ServerAcceptanceThread : public QThread +{ + Q_OBJECT +public: + enum AndroidError { + AndroidNoError + }; + + explicit ServerAcceptanceThread(QObject *parent = 0); + ~ServerAcceptanceThread(); + void setServiceDetails(const QBluetoothUuid &uuid, const QString &serviceName, + QBluetooth::SecurityFlags securityFlags); + virtual void run(); + + void stop(); + bool hasPendingConnections() const; + QAndroidJniObject nextPendingConnection(); + void setMaxPendingConnections(int maximumCount); + +signals: + void newConnection(); + void error(ServerAcceptanceThread::AndroidError); + +private: + bool validSetup() const; + void shutdownPendingConnections(); + + QList<QAndroidJniObject> pendingSockets; + QAndroidJniObject btAdapter; + QAndroidJniObject btServerSocket; + mutable QMutex m_mutex; + QString m_serviceName; + QBluetoothUuid m_uuid; + bool m_stop; + AndroidError lastError; + int maxPendingConnections; + QBluetooth::SecurityFlags secFlags; + +}; + +#endif // SERVERACCEPTANCETHREAD_H diff --git a/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp b/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp new file mode 100644 index 00000000..341617bc --- /dev/null +++ b/src/bluetooth/android/servicediscoverybroadcastreceiver.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#include "android/servicediscoverybroadcastreceiver_p.h" +#include <QtCore/QLoggingCategory> +#include <QtAndroidExtras/QAndroidJniEnvironment> +#include <QtBluetooth/QBluetoothAddress> +#include <QtBluetooth/QBluetoothDeviceInfo> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) + +ServiceDiscoveryBroadcastReceiver::ServiceDiscoveryBroadcastReceiver(QObject* parent): AndroidBroadcastReceiver(parent) +{ + addAction(QStringLiteral("android.bluetooth.device.action.UUID")); +} + +void ServiceDiscoveryBroadcastReceiver::onReceive(JNIEnv *env, jobject context, jobject intent) +{ + Q_UNUSED(context); + Q_UNUSED(env); + + QAndroidJniObject intentObject(intent); + const QString action = intentObject.callObjectMethod("getAction", "()Ljava/lang/String;").toString(); + + qCDebug(QT_BT_ANDROID) << "ServiceDiscoveryBroadcastReceiver::onReceive() - event:" << action; + + if (action == QStringLiteral("android.bluetooth.device.action.UUID")) { + QAndroidJniObject keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.UUID")); + QAndroidJniObject parcelableUuids = intentObject.callObjectMethod( + "getParcelableArrayExtra", + "(Ljava/lang/String;)[Landroid/os/Parcelable;", + keyExtra.object<jstring>()); + if (!parcelableUuids.isValid()) + return; + QList<QBluetoothUuid> result = ServiceDiscoveryBroadcastReceiver::convertParcelableArray(parcelableUuids); + + keyExtra = QAndroidJniObject::fromString( + QStringLiteral("android.bluetooth.device.extra.DEVICE")); + QAndroidJniObject bluetoothDevice = + intentObject.callObjectMethod("getParcelableExtra", + "(Ljava/lang/String;)Landroid/os/Parcelable;", + keyExtra.object<jstring>()); + QBluetoothAddress address; + if (bluetoothDevice.isValid()) { + address = QBluetoothAddress(bluetoothDevice.callObjectMethod<jstring>("getAddress").toString()); + emit uuidFetchFinished(address, result); + } + } +} + +QList<QBluetoothUuid> ServiceDiscoveryBroadcastReceiver::convertParcelableArray(const QAndroidJniObject &parcelUuidArray) +{ + QList<QBluetoothUuid> result; + QAndroidJniEnvironment env; + QAndroidJniObject p; + + jobjectArray parcels = parcelUuidArray.object<jobjectArray>(); + if (!parcels) + return result; + + jint size = env->GetArrayLength(parcels); + for (int i = 0; i < size; i++) { + p = env->GetObjectArrayElement(parcels, i); + + QBluetoothUuid uuid(p.callObjectMethod<jstring>("toString").toString()); + //qCDebug(QT_BT_ANDROID) << uuid.toString(); + result.append(uuid); + } + + return result; +} + +QT_END_NAMESPACE + diff --git a/src/bluetooth/android/servicediscoverybroadcastreceiver_p.h b/src/bluetooth/android/servicediscoverybroadcastreceiver_p.h new file mode 100644 index 00000000..e75095e8 --- /dev/null +++ b/src/bluetooth/android/servicediscoverybroadcastreceiver_p.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtBluetooth module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 +** 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. +** +** In addition, as a special exception, Digia gives you certain additional +** 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$ +** +****************************************************************************/ + +#ifndef SERVICEDISCOVERYBROADCASTRECEIVER_H +#define SERVICEDISCOVERYBROADCASTRECEIVER_H + +#include "android/androidbroadcastreceiver_p.h" +#include <QtCore/QList> +#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent> +#include <QtBluetooth/QBluetoothUuid> + +QT_BEGIN_NAMESPACE + +class QBluetoothDeviceInfo; + +class ServiceDiscoveryBroadcastReceiver : public AndroidBroadcastReceiver +{ + Q_OBJECT +public: + ServiceDiscoveryBroadcastReceiver(QObject* parent = 0); + virtual void onReceive(JNIEnv *env, jobject context, jobject intent); + + static QList<QBluetoothUuid> convertParcelableArray(const QAndroidJniObject &obj); + +signals: + void uuidFetchFinished(const QBluetoothAddress &addr, const QList<QBluetoothUuid> &serviceUuid); +}; + +QT_END_NAMESPACE +#endif // SERVICEDISCOVERYBROADCASTRECEIVER_H |