summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@theqtcompany.com>2016-01-12 12:22:11 +0100
committerAlex Blasche <alexander.blasche@theqtcompany.com>2016-01-18 07:46:35 +0000
commite94fe90e7d2e1df9bd7d1d014fb468e5e73da834 (patch)
tree440a4e0359661b6cc4ac2e95455475ff0d64ebcd
parent1046db1a91f264db6714975daa27f56a457a6778 (diff)
Bluetooth LE: Add connection update functionality.
Implemented for BlueZ only. Change-Id: I358a98bbc7499d5ce5437fb0d4672fde46c3b831 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
-rw-r--r--src/bluetooth/bluetooth.pro2
-rw-r--r--src/bluetooth/bluez/bluez_data_p.h1
-rw-r--r--src/bluetooth/bluez/hcimanager.cpp140
-rw-r--r--src/bluetooth/bluez/hcimanager_p.h12
-rw-r--r--src/bluetooth/qlowenergyconnectionparameters.cpp202
-rw-r--r--src/bluetooth/qlowenergyconnectionparameters.h86
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp30
-rw-r--r--src/bluetooth/qlowenergycontroller.h4
-rw-r--r--src/bluetooth/qlowenergycontroller_android.cpp6
-rw-r--r--src/bluetooth/qlowenergycontroller_bluez.cpp26
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm6
-rw-r--r--src/bluetooth/qlowenergycontroller_p.cpp4
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h3
-rw-r--r--tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp6
-rw-r--r--tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp27
15 files changed, 555 insertions, 0 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 920fbe9a..ef5337b5 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -32,6 +32,7 @@ PUBLIC_HEADERS += \
qbluetoothtransferreply.h \
qlowenergyadvertisingdata.h \
qlowenergyadvertisingparameters.h \
+ qlowenergyconnectionparameters.h \
qlowenergycontroller.h
PRIVATE_HEADERS += \
@@ -68,6 +69,7 @@ SOURCES += \
qbluetoothtransferreply.cpp \
qlowenergyadvertisingdata.cpp \
qlowenergyadvertisingparameters.cpp \
+ qlowenergyconnectionparameters.cpp \
qlowenergyservice.cpp \
qlowenergyservicedata.cpp \
qlowenergycharacteristic.cpp \
diff --git a/src/bluetooth/bluez/bluez_data_p.h b/src/bluetooth/bluez/bluez_data_p.h
index 7db0e50b..aa0d0166 100644
--- a/src/bluetooth/bluez/bluez_data_p.h
+++ b/src/bluetooth/bluez/bluez_data_p.h
@@ -348,6 +348,7 @@ enum OpCodeCommandField {
OcfLeSetAdvEnable = 0xa,
OcfLeClearWhiteList = 0x10,
OcfLeAddToWhiteList = 0x11,
+ OcfLeConnectionUpdate = 0x13,
};
/* Command opcode pack/unpack */
diff --git a/src/bluetooth/bluez/hcimanager.cpp b/src/bluetooth/bluez/hcimanager.cpp
index 388f3e0c..3ff03fd4 100644
--- a/src/bluetooth/bluez/hcimanager.cpp
+++ b/src/bluetooth/bluez/hcimanager.cpp
@@ -35,6 +35,7 @@
#include "hcimanager_p.h"
#include "qbluetoothsocket_p.h"
+#include "qlowenergyconnectionparameters.h"
#include <QtCore/qloggingcategory.h>
@@ -258,6 +259,108 @@ QBluetoothAddress HciManager::addressForConnectionHandle(quint16 handle) const
return QBluetoothAddress();
}
+quint16 forceIntervalIntoRange(double connectionInterval)
+{
+ return qMin<double>(qMax<double>(7.5, connectionInterval), 4000) / 1.25;
+}
+
+struct ConnectionUpdateData {
+ quint16 minInterval;
+ quint16 maxInterval;
+ quint16 slaveLatency;
+ quint16 timeout;
+};
+ConnectionUpdateData connectionUpdateData(const QLowEnergyConnectionParameters &params)
+{
+ ConnectionUpdateData data;
+ const quint16 minInterval = forceIntervalIntoRange(params.minimumInterval());
+ const quint16 maxInterval = forceIntervalIntoRange(params.maximumInterval());
+ data.minInterval = qToLittleEndian(minInterval);
+ data.maxInterval = qToLittleEndian(maxInterval);
+ const quint16 latency = qMax<quint16>(0, qMin<quint16>(params.latency(), 499));
+ data.slaveLatency = qToLittleEndian(latency);
+ const quint16 timeout
+ = qMax<quint16>(100, qMin<quint16>(32000, params.supervisionTimeout())) / 10;
+ data.timeout = qToLittleEndian(timeout);
+ return data;
+}
+
+bool HciManager::sendConnectionUpdateCommand(quint16 handle,
+ const QLowEnergyConnectionParameters &params)
+{
+ struct CommandParams {
+ quint16 handle;
+ ConnectionUpdateData data;
+ quint16 minCeLength;
+ quint16 maxCeLength;
+ } commandParams;
+ commandParams.handle = qToLittleEndian(handle);
+ commandParams.data = connectionUpdateData(params);
+ commandParams.minCeLength = 0;
+ commandParams.maxCeLength = qToLittleEndian(quint16(0xffff));
+ const QByteArray data = QByteArray::fromRawData(reinterpret_cast<char *>(&commandParams),
+ sizeof commandParams);
+ return sendCommand(OgfLinkControl, OcfLeConnectionUpdate, data);
+}
+
+bool HciManager::sendConnectionParameterUpdateRequest(quint16 handle,
+ const QLowEnergyConnectionParameters &params)
+{
+ ConnectionUpdateData connUpdateData = connectionUpdateData(params);
+
+ // Vol 3, part A, 4
+ struct SignalingPacket {
+ quint8 code;
+ quint8 identifier;
+ quint16 length;
+ } signalingPacket;
+ signalingPacket.code = 0x12;
+ signalingPacket.identifier = ++sigPacketIdentifier;
+ const quint16 sigPacketLen = sizeof connUpdateData;
+ signalingPacket.length = qToLittleEndian(sigPacketLen);
+
+ struct L2CapHeader {
+ quint16 length;
+ quint16 channelId;
+ } l2CapHeader;
+ const quint16 l2CapHeaderLen = sizeof signalingPacket + sigPacketLen;
+ l2CapHeader.length = qToLittleEndian(l2CapHeaderLen);
+ l2CapHeader.channelId = qToLittleEndian(quint16(5));
+
+ // Vol 2, part E, 5.4.2
+ struct AclData {
+ quint16 handle: 12;
+ quint16 pbFlag: 2;
+ quint16 bcFlag: 2;
+ quint16 dataLen;
+ } aclData;
+ aclData.handle = qToLittleEndian(handle); // Works because the next two values are zero.
+ aclData.pbFlag = 0;
+ aclData.bcFlag = 0;
+ aclData.dataLen = qToLittleEndian(quint16(sizeof l2CapHeader + l2CapHeaderLen));
+
+ struct iovec iv[5];
+ quint8 packetType = 2;
+ iv[0].iov_base = &packetType;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = &aclData;
+ iv[1].iov_len = sizeof aclData;
+ iv[2].iov_base = &l2CapHeader;
+ iv[2].iov_len = sizeof l2CapHeader;
+ iv[3].iov_base = &signalingPacket;
+ iv[3].iov_len = sizeof signalingPacket;
+ iv[4].iov_base = &connUpdateData;
+ iv[4].iov_len = sizeof connUpdateData;
+ while (writev(hciSocket, iv, sizeof iv / sizeof *iv) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ qCDebug(QT_BT_BLUEZ()) << "failure writing HCI ACL packet:" << strerror(errno);
+ return false;
+ }
+ qCDebug(QT_BT_BLUEZ) << "Connection Update Request packet sent successfully";
+ return true;
+}
+
/*!
* Process all incoming HCI events. Function cannot process anything else but events.
*/
@@ -319,10 +422,47 @@ void HciManager::_q_readNotify()
emit commandCompleted(event->opcode, status, additionalData);
}
break;
+ case LeMetaEvent:
+ handleLeMetaEvent(data);
+ break;
default:
break;
}
}
+void HciManager::handleLeMetaEvent(const quint8 *data)
+{
+ // Spec v4.2, Vol 2, part E, 7.7.65ff
+ switch (*data) {
+ case 0x1: {
+ const quint16 handle = bt_get_le16(data + 2);
+ emit connectionComplete(handle);
+ break;
+ }
+ case 0x3: {
+ // TODO: From little endian!
+ struct ConnectionUpdateData {
+ quint8 status;
+ quint16 handle;
+ quint16 interval;
+ quint16 latency;
+ quint16 timeout;
+ } __attribute((packed));
+ const auto * const updateData
+ = reinterpret_cast<const ConnectionUpdateData *>(data + 1);
+ if (updateData->status == 0) {
+ QLowEnergyConnectionParameters params;
+ const double interval = qFromLittleEndian(updateData->interval) * 1.25;
+ params.setIntervalRange(interval, interval);
+ params.setLatency(qFromLittleEndian(updateData->latency));
+ params.setSupervisionTimeout(qFromLittleEndian(updateData->timeout) * 10);
+ emit connectionUpdate(qFromLittleEndian(updateData->handle), params);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+}
QT_END_NAMESPACE
diff --git a/src/bluetooth/bluez/hcimanager_p.h b/src/bluetooth/bluez/hcimanager_p.h
index c8f2fe56..f1bd6d46 100644
--- a/src/bluetooth/bluez/hcimanager_p.h
+++ b/src/bluetooth/bluez/hcimanager_p.h
@@ -53,6 +53,8 @@
QT_BEGIN_NAMESPACE
+class QLowEnergyConnectionParameters;
+
class HciManager : public QObject
{
Q_OBJECT
@@ -60,6 +62,7 @@ public:
enum HciEvent {
EncryptChangeEvent = EVT_ENCRYPT_CHANGE,
CommandCompleteEvent = EVT_CMD_COMPLETE,
+ LeMetaEvent = 0x3e,
};
explicit HciManager(const QBluetoothAddress &deviceAdapter, QObject *parent = 0);
@@ -68,21 +71,30 @@ public:
bool isValid() const;
bool monitorEvent(HciManager::HciEvent event);
bool sendCommand(OpCodeGroupField ogf, OpCodeCommandField ocf, const QByteArray &parameters);
+
void stopEvents();
QBluetoothAddress addressForConnectionHandle(quint16 handle) const;
+ bool sendConnectionUpdateCommand(quint16 handle, const QLowEnergyConnectionParameters &params);
+ bool sendConnectionParameterUpdateRequest(quint16 handle,
+ const QLowEnergyConnectionParameters &params);
+
signals:
void encryptionChangedEvent(const QBluetoothAddress &address, bool wasSuccess);
void commandCompleted(quint16 opCode, quint8 status, const QByteArray &data);
+ void connectionComplete(quint16 handle);
+ void connectionUpdate(quint16 handle, const QLowEnergyConnectionParameters &parameters);
private slots:
void _q_readNotify();
private:
int hciForAddress(const QBluetoothAddress &deviceAdapter);
+ void handleLeMetaEvent(const quint8 *data);
int hciSocket;
int hciDev;
+ quint8 sigPacketIdentifier = 0;
QSocketNotifier *notifier;
QSet<HciManager::HciEvent> runningEvents;
};
diff --git a/src/bluetooth/qlowenergyconnectionparameters.cpp b/src/bluetooth/qlowenergyconnectionparameters.cpp
new file mode 100644
index 00000000..727ed141
--- /dev/null
+++ b/src/bluetooth/qlowenergyconnectionparameters.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 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.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergyconnectionparameters.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyConnectionParametersPrivate : public QSharedData
+{
+public:
+ QLowEnergyConnectionParametersPrivate()
+ : minInterval(7.5)
+ , maxInterval(4000)
+ , latency(0)
+ , timeout(32000)
+ {
+ }
+
+ double minInterval;
+ double maxInterval;
+ int latency;
+ int timeout;
+};
+
+/*!
+ \since 5.7
+ \class QLowEnergyConnectionParameters
+ \brief The QLowEnergyConnectionParameters class is used when requesting or reporting
+ an update of the parameters of a Bluetooth LE connection.
+
+ The connection parameters influence how often a master and a slave device synchronize
+ with each other. In general, a lower connection interval and latency means faster communication,
+ but also higher power consumption. How these criteria should be weighed against each other
+ is highly dependent on the concrete use case.
+ \inmodule QtBluetooth
+ \ingroup shared
+
+ \sa QLowEnergyController::requestConnectionUpdate
+ \sa QLowEnergyController::connectionUpdated
+*/
+
+
+/*!
+ Constructs a new object of this class. All values are initialized to valid defaults.
+ */
+QLowEnergyConnectionParameters::QLowEnergyConnectionParameters()
+ : d(new QLowEnergyConnectionParametersPrivate)
+{
+}
+
+/*! Constructs a new object of this class that is a copy of \a other. */
+QLowEnergyConnectionParameters::QLowEnergyConnectionParameters(const QLowEnergyConnectionParameters &other)
+ : d(other.d)
+{
+}
+
+/*! Destroys this object. */
+QLowEnergyConnectionParameters::~QLowEnergyConnectionParameters()
+{
+}
+
+/*! Makes this object a copy of \a other and returns the new value of this object. */
+QLowEnergyConnectionParameters &QLowEnergyConnectionParameters::operator=(const QLowEnergyConnectionParameters &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Sets the range in which the connection interval should be. The actual value will be decided by
+ the controller. Both \a minimum and \a maximum are given in milliseconds.
+ If \a maximum is smaller than \a minimum, it will be set to the value of \a minimum.
+ The smallest possible connection interval is 7.5 milliseconds, the largest one is
+ 4000 milliseconds.
+ \sa minimumInterval(), maximumInterval()
+ */
+void QLowEnergyConnectionParameters::setIntervalRange(double minimum, double maximum)
+{
+ d->minInterval = minimum;
+ d->maxInterval = qMax(minimum, maximum);
+}
+
+/*!
+ Returns the minimum connection interval in milliseconds. The default is 7.5.
+ \note If this object was emitted via \l QLowEnergyController::connectionUpdated(), then
+ this value is the same as \l maximumInterval() and refers to the actual
+ connection interval.
+ \sa setIntervalRange()
+ */
+double QLowEnergyConnectionParameters::minimumInterval() const
+{
+ return d->minInterval;
+}
+
+/*!
+ Returns the maximum connection interval in milliseconds. The default is 4000.
+ \note If this object was emitted via \l QLowEnergyController::connectionUpdated(), then
+ this value is the same as \l minimumInterval() and refers to the actual
+ connection interval.
+ \sa setIntervalRange()
+ */
+double QLowEnergyConnectionParameters::maximumInterval() const
+{
+ return d->maxInterval;
+}
+
+/*!
+ Sets the slave latency of the connection (that is, the number of connection events that a slave
+ device is allowed to ignore) to \a latency. The minimum value is 0, the maximum is 499.
+ \sa latency()
+ */
+void QLowEnergyConnectionParameters::setLatency(int latency)
+{
+ d->latency = latency;
+}
+
+/*!
+ Returns the slave latency of the connection.
+ \sa setLatency()
+*/
+int QLowEnergyConnectionParameters::latency() const
+{
+ return d->latency;
+}
+
+/*!
+ Sets the link supervision timeout to \a timeout milliseconds.
+ There are several constraints on this value: It must be in the range [100,32000] and it must be
+ larger than (1 + \l latency()) * 2 * \l maximumInterval().
+ \sa supervisionTimeout()
+ */
+void QLowEnergyConnectionParameters::setSupervisionTimeout(int timeout)
+{
+ d->timeout = timeout;
+}
+
+/*!
+ Returns the link supervision timeout of the connection in milliseconds.
+ \sa setSupervisionTimeout()
+*/
+int QLowEnergyConnectionParameters::supervisionTimeout() const
+{
+ return d->timeout;
+}
+
+/*!
+ \fn void QLowEnergyConnectionParameters::swap(QLowEnergyConnectionParameters &other)
+ Swaps this object with \a other.
+ */
+
+/*!
+ Returns \a true if \a p1 and \a p2 are equal with respect to their public state,
+ otherwise returns false.
+ */
+bool operator==(const QLowEnergyConnectionParameters &p1, const QLowEnergyConnectionParameters &p2)
+{
+ if (p1.d == p2.d)
+ return true;
+ return p1.minimumInterval() == p2.minimumInterval()
+ && p1.maximumInterval() == p2.maximumInterval()
+ && p1.latency() == p2.latency()
+ && p1.supervisionTimeout() == p2.supervisionTimeout();
+}
+
+/*!
+ \fn bool operator!=(const QLowEnergyConnectionParameters &p1,
+ const QLowEnergyConnectionParameters &p2)
+ Returns \a true if \a p1 and \a p2 are not equal with respect to their public state,
+ otherwise returns false.
+ */
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergyconnectionparameters.h b/src/bluetooth/qlowenergyconnectionparameters.h
new file mode 100644
index 00000000..a7022dd2
--- /dev/null
+++ b/src/bluetooth/qlowenergyconnectionparameters.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $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 The Qt Company. For licensing terms
+** and conditions see http://www.qt.io/terms-conditions. For further
+** information use the contact form at http://www.qt.io/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 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.
+**
+** As a special exception, The Qt Company gives you certain additional
+** rights. These rights are described in The Qt Company LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYCONNECTIONPARAMETERS_H
+#define QLOWENERGYCONNECTIONPARAMETERS_H
+
+#include <QtBluetooth/qbluetoothglobal.h>
+#include <QtCore/qmetatype.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyConnectionParametersPrivate;
+
+class Q_BLUETOOTH_EXPORT QLowEnergyConnectionParameters
+{
+ friend Q_BLUETOOTH_EXPORT bool operator==(const QLowEnergyConnectionParameters &p1,
+ const QLowEnergyConnectionParameters &p2);
+public:
+ QLowEnergyConnectionParameters();
+ QLowEnergyConnectionParameters(const QLowEnergyConnectionParameters &other);
+ ~QLowEnergyConnectionParameters();
+
+ QLowEnergyConnectionParameters &operator=(const QLowEnergyConnectionParameters &other);
+
+ void setIntervalRange(double minimum, double maximum);
+ double minimumInterval() const;
+ double maximumInterval() const;
+
+ void setLatency(int latency);
+ int latency() const;
+
+ void setSupervisionTimeout(int timeout);
+ int supervisionTimeout() const;
+
+ void swap(QLowEnergyConnectionParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+
+private:
+ QSharedDataPointer<QLowEnergyConnectionParametersPrivate> d;
+};
+
+Q_BLUETOOTH_EXPORT bool operator==(const QLowEnergyConnectionParameters &p1,
+ const QLowEnergyConnectionParameters &p2);
+inline bool operator!=(const QLowEnergyConnectionParameters &p1,
+ const QLowEnergyConnectionParameters &p2)
+{
+ return !(p1 == p2);
+}
+
+Q_DECLARE_SHARED(QLowEnergyConnectionParameters)
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QLowEnergyConnectionParameters)
+
+#endif // Include guard
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
index 8a6a2e1d..cf712e17 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -35,6 +35,7 @@
#include "qlowenergycontroller_p.h"
#include "qlowenergycharacteristicdata.h"
+#include "qlowenergyconnectionparameters.h"
#include "qlowenergydescriptordata.h"
#include "qlowenergyservicedata.h"
@@ -238,12 +239,25 @@ Q_DECLARE_LOGGING_CATEGORY(QT_BT)
\sa discoverServices(), error()
*/
+/*!
+ \fn void QLowEnergyController::connectionUpdated(const QLowEnergyConnectionParameters &newParameters)
+
+ This signal is emitted when the connection parameters change. This can happen as a result
+ of calling \l requestConnectionUpdate() or due to other reasons, for instance because
+ the other side of the connection requested new parameters. The new values can be retrieved
+ from \a newParameters.
+
+ \sa requestConnectionUpdate()
+*/
+
+
void registerQLowEnergyControllerMetaType()
{
static bool initDone = false;
if (!initDone) {
qRegisterMetaType<QLowEnergyController::ControllerState>();
qRegisterMetaType<QLowEnergyController::Error>();
+ qRegisterMetaType<QLowEnergyConnectionParameters>();
initDone = true;
}
}
@@ -882,6 +896,22 @@ QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData
}
/*!
+ Requests the controller to update the connection according to \a parameters.
+ If the request is successful, the \l connectionUpdated() signal will be emitted
+ with the actual new parameters.
+ See the \l QLowEnergyConnectionParameters class for more information on connection parameters.
+ \note Currently, this functionality is only implemented on Linux.
+ */
+void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters &parameters)
+{
+ if (state() != ConnectedState) {
+ qCWarning(QT_BT) << "Connection update request only possible in connected state";
+ return;
+ }
+ d_ptr->requestConnectionUpdate(parameters);
+}
+
+/*!
Returns the last occurred error or \l NoError.
*/
QLowEnergyController::Error QLowEnergyController::error() const
diff --git a/src/bluetooth/qlowenergycontroller.h b/src/bluetooth/qlowenergycontroller.h
index cec0a8cf..608f6855 100644
--- a/src/bluetooth/qlowenergycontroller.h
+++ b/src/bluetooth/qlowenergycontroller.h
@@ -44,6 +44,7 @@
QT_BEGIN_NAMESPACE
class QLowEnergyAdvertisingParameters;
+class QLowEnergyConnectionParameters;
class QLowEnergyControllerPrivate;
class QLowEnergyServiceData;
@@ -123,6 +124,8 @@ public:
QLowEnergyService *addService(const QLowEnergyServiceData &service, QObject *parent = 0);
+ void requestConnectionUpdate(const QLowEnergyConnectionParameters &parameters);
+
Error error() const;
QString errorString() const;
@@ -136,6 +139,7 @@ Q_SIGNALS:
void serviceDiscovered(const QBluetoothUuid &newService);
void discoveryFinished();
+ void connectionUpdated(const QLowEnergyConnectionParameters &parameters);
private:
explicit QLowEnergyController(QObject *parent = 0); // For the peripheral role.
diff --git a/src/bluetooth/qlowenergycontroller_android.cpp b/src/bluetooth/qlowenergycontroller_android.cpp
index 3e3ea830..b435f89d 100644
--- a/src/bluetooth/qlowenergycontroller_android.cpp
+++ b/src/bluetooth/qlowenergycontroller_android.cpp
@@ -604,6 +604,12 @@ void QLowEnergyControllerPrivate::stopAdvertising()
qCWarning(QT_BT_ANDROID) << "LE advertising not implemented for Android";
}
+void QLowEnergyControllerPrivate::requestConnectionUpdate(const QLowEnergyConnectionParameters &params)
+{
+ Q_UNUSED(params);
+ qCWarning(QT_BT_ANDROID) << "Connection update not implemented for Android";
+}
+
void QLowEnergyControllerPrivate::addToGenericAttributeList(const QLowEnergyServiceData &service,
QLowEnergyHandle startHandle)
{
diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp
index a69bd2c4..35291c97 100644
--- a/src/bluetooth/qlowenergycontroller_bluez.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluez.cpp
@@ -264,6 +264,17 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
hciManager->monitorEvent(HciManager::EncryptChangeEvent);
connect(hciManager, SIGNAL(encryptionChangedEvent(QBluetoothAddress,bool)),
this, SLOT(encryptionChangedEvent(QBluetoothAddress,bool)));
+ hciManager->monitorEvent(HciManager::LeMetaEvent);
+ connect(hciManager, &HciManager::connectionComplete, [this](quint16 handle) {
+ connectionHandle = handle;
+ qCDebug(QT_BT_BLUEZ) << "received connection complete event, handle:" << handle;
+ });
+ connect(hciManager, &HciManager::connectionUpdate,
+ [this](quint16 handle, const QLowEnergyConnectionParameters &params) {
+ if (handle == connectionHandle)
+ emit q_ptr->connectionUpdated(params);
+ }
+ );
}
QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
@@ -359,6 +370,18 @@ void QLowEnergyControllerPrivate::stopAdvertising()
advertiser->stopAdvertising();
}
+void QLowEnergyControllerPrivate::requestConnectionUpdate(const QLowEnergyConnectionParameters &params)
+{
+ // The spec says that the connection update command can be used by both slave and master
+ // devices, but BlueZ allows it only for master devices. So for slave devices, we have to use a
+ // connection parameter update request, which we need to wrap in an ACL command, as BlueZ
+ // does not allow user-space sockets for the signaling channel.
+ if (role == QLowEnergyController::CentralRole)
+ hciManager->sendConnectionUpdateCommand(connectionHandle, params);
+ else
+ hciManager->sendConnectionParameterUpdateRequest(connectionHandle, params);
+}
+
void QLowEnergyControllerPrivate::connectToDevice()
{
if (remoteDevice.isNull()) {
@@ -481,6 +504,7 @@ void QLowEnergyControllerPrivate::resetController()
encryptionChangePending = false;
receivedMtuExchangeRequest = false;
securityLevelValue = -1;
+ connectionHandle = 0;
}
void QLowEnergyControllerPrivate::l2cpReadyRead()
@@ -2623,6 +2647,8 @@ void QLowEnergyControllerPrivate::handleConnectionRequest()
}
remoteDevice = QBluetoothAddress(convertAddress(clientAddr.l2_bdaddr.b));
qCDebug(QT_BT_BLUEZ) << "GATT connection from device" << remoteDevice;
+ if (connectionHandle == 0)
+ qCWarning(QT_BT_BLUEZ) << "Received client connection, but no connection complete event";
closeServerSocket();
l2cpSocket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol, this);
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index 7ca0d9ac..f730444a 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -1173,6 +1173,12 @@ QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData
return nullptr;
}
+void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters &params)
+{
+ Q_UNUSED(params);
+ qCWarning(QT_BT_OSX) << "Connection update not implemented for OS X";
+}
+
QT_END_NAMESPACE
#include "moc_qlowenergycontroller_osx_p.cpp"
diff --git a/src/bluetooth/qlowenergycontroller_p.cpp b/src/bluetooth/qlowenergycontroller_p.cpp
index c04d0b3c..fba8e9c1 100644
--- a/src/bluetooth/qlowenergycontroller_p.cpp
+++ b/src/bluetooth/qlowenergycontroller_p.cpp
@@ -116,6 +116,10 @@ void QLowEnergyControllerPrivate::stopAdvertising()
{
}
+void QLowEnergyControllerPrivate::requestConnectionUpdate(const QLowEnergyConnectionParameters & /* params */)
+{
+}
+
void QLowEnergyControllerPrivate::addToGenericAttributeList(const QLowEnergyServiceData &/* service */,
QLowEnergyHandle /* startHandle */)
{
diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h
index f3b28f5c..e0b2a2d7 100644
--- a/src/bluetooth/qlowenergycontroller_p.h
+++ b/src/bluetooth/qlowenergycontroller_p.h
@@ -122,6 +122,8 @@ public:
const QLowEnergyAdvertisingData &scanResponseData);
void stopAdvertising();
+ void requestConnectionUpdate(const QLowEnergyConnectionParameters &params);
+
// misc helpers
QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(
QLowEnergyHandle handle);
@@ -192,6 +194,7 @@ public:
private:
#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+ quint16 connectionHandle = 0;
QBluetoothSocket *l2cpSocket;
struct Request {
quint8 command;
diff --git a/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp b/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp
index e2c930bf..087b0284 100644
--- a/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp
+++ b/tests/auto/qlowenergycontroller-gattserver/server/qlowenergycontroller-gattserver.cpp
@@ -33,6 +33,7 @@
#include <QtBluetooth/qlowenergyadvertisingdata.h>
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
+#include <QtBluetooth/qlowenergyconnectionparameters.h>
#include <QtBluetooth/qlowenergycontroller.h>
#include <QtBluetooth/qlowenergycharacteristicdata.h>
#include <QtBluetooth/qlowenergydescriptordata.h>
@@ -212,6 +213,11 @@ int main(int argc, char *argv[])
Q_ASSERT(notifiableChar.isValid());
customService->writeCharacteristic(notifiableChar, "notified");
Q_ASSERT(notifiableChar.value() == "notified");
+ QLowEnergyConnectionParameters connParams;
+ connParams.setIntervalRange(30, 62.5);
+ connParams.setLatency(5);
+ connParams.setSupervisionTimeout(5500);
+ leController->requestConnectionUpdate(connParams);
};
QObject::connect(customService.data(), &QLowEnergyService::descriptorWritten,
descriptorWriteHandler);
diff --git a/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp b/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp
index 6c45eed7..e2534a14 100644
--- a/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp
+++ b/tests/auto/qlowenergycontroller-gattserver/test/tst_qlowenergycontroller-gattserver.cpp
@@ -37,6 +37,7 @@
#include <QtBluetooth/qbluetoothlocaldevice.h>
#include <QtBluetooth/qlowenergyadvertisingdata.h>
#include <QtBluetooth/qlowenergyadvertisingparameters.h>
+#include <QtBluetooth/qlowenergyconnectionparameters.h>
#include <QtBluetooth/qlowenergycontroller.h>
#include <QtBluetooth/qlowenergycharacteristicdata.h>
#include <QtBluetooth/qlowenergydescriptordata.h>
@@ -60,6 +61,7 @@ private slots:
// Static, local stuff goes here.
void advertisingParameters();
void advertisingData();
+ void connectionParameters();
void controllerType();
void serviceData();
@@ -148,6 +150,28 @@ void TestQLowEnergyControllerGattServer::advertisingData()
QVERIFY(data != QLowEnergyAdvertisingData());
}
+void TestQLowEnergyControllerGattServer::connectionParameters()
+{
+ QLowEnergyConnectionParameters connParams;
+ QCOMPARE(connParams, QLowEnergyConnectionParameters());
+ connParams.setIntervalRange(8, 9);
+ QCOMPARE(connParams.minimumInterval(), double(8));
+ QCOMPARE(connParams.maximumInterval(), double(9));
+ connParams.setIntervalRange(9, 8);
+ QCOMPARE(connParams.minimumInterval(), double(9));
+ QCOMPARE(connParams.maximumInterval(), double(9));
+ connParams.setLatency(50);
+ QCOMPARE(connParams.latency(), 50);
+ connParams.setSupervisionTimeout(1000);
+ QCOMPARE(connParams.supervisionTimeout(), 1000);
+ const QLowEnergyConnectionParameters cp2 = connParams;
+ QCOMPARE(cp2, connParams);
+ QLowEnergyConnectionParameters cp3;
+ QVERIFY(cp3 != connParams);
+ cp3 = connParams;
+ QCOMPARE(cp3, connParams);
+}
+
void TestQLowEnergyControllerGattServer::advertisedData()
{
if (m_serverAddress.isNull())
@@ -325,6 +349,9 @@ void TestQLowEnergyControllerGattServer::serverCommunication()
QCOMPARE(customChar3.value().constData(), "indicated");
QCOMPARE(customChar4.value().constData(), "notified");
+ spy.reset(new QSignalSpy(m_leController.data(), &QLowEnergyController::connectionUpdated));
+ QVERIFY(spy->wait(5000));
+
const bool isBonded = QBluetoothLocalDevice().pairingStatus(m_serverAddress)
!= QBluetoothLocalDevice::Unpaired;
m_leController->disconnectFromDevice();