summaryrefslogtreecommitdiffstats
path: root/src/bluetooth
diff options
context:
space:
mode:
authorChristian Kandeler <christian.kandeler@theqtcompany.com>2015-10-14 16:54:47 +0200
committerAlex Blasche <alexander.blasche@theqtcompany.com>2015-11-17 15:40:02 +0000
commita5f362af452555b5aaa4585be82053029e4b25c0 (patch)
treeeae7edea0537c8fe55226628fc3d5618741cf04f /src/bluetooth
parenteb59027d32c7904a129b16c786df1dc2097ab9c9 (diff)
Bluetooth: Introduce API for LE advertising.
And provide an implementation for BlueZ. Change-Id: I302aee7c43b77016d9e1e7a0d5bcbf00096abf76 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/bluetooth')
-rw-r--r--src/bluetooth/bluetooth.pro8
-rw-r--r--src/bluetooth/bluez/bluez_data_p.h32
-rw-r--r--src/bluetooth/bluez/hcimanager.cpp46
-rw-r--r--src/bluetooth/bluez/hcimanager_p.h4
-rw-r--r--src/bluetooth/qleadvertiser_bluez.cpp443
-rw-r--r--src/bluetooth/qleadvertiser_p.h137
-rw-r--r--src/bluetooth/qlowenergyadvertisingdata.cpp280
-rw-r--r--src/bluetooth/qlowenergyadvertisingdata.h99
-rw-r--r--src/bluetooth/qlowenergyadvertisingparameters.cpp259
-rw-r--r--src/bluetooth/qlowenergyadvertisingparameters.h111
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp176
-rw-r--r--src/bluetooth/qlowenergycontroller.h27
-rw-r--r--src/bluetooth/qlowenergycontroller_android.cpp15
-rw-r--r--src/bluetooth/qlowenergycontroller_bluez.cpp31
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm45
-rw-r--r--src/bluetooth/qlowenergycontroller_osx_p.h2
-rw-r--r--src/bluetooth/qlowenergycontroller_p.cpp10
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h9
18 files changed, 1713 insertions, 21 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index e37ad7aa..99067c74 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -27,6 +27,8 @@ PUBLIC_HEADERS += \
qlowenergycharacteristic.h \
qlowenergydescriptor.h \
qbluetoothtransferreply.h \
+ qlowenergyadvertisingdata.h \
+ qlowenergyadvertisingparameters.h \
qlowenergycontroller.h
PRIVATE_HEADERS += \
@@ -43,7 +45,8 @@ PRIVATE_HEADERS += \
qprivatelinearbuffer_p.h \
qbluetoothlocaldevice_p.h \
qlowenergycontroller_p.h \
- qlowenergyserviceprivate_p.h
+ qlowenergyserviceprivate_p.h \
+ qleadvertiser_p.h \
SOURCES += \
qbluetoothaddress.cpp\
@@ -60,6 +63,8 @@ SOURCES += \
qbluetoothtransfermanager.cpp \
qbluetoothtransferrequest.cpp \
qbluetoothtransferreply.cpp \
+ qlowenergyadvertisingdata.cpp \
+ qlowenergyadvertisingparameters.cpp \
qlowenergyservice.cpp \
qlowenergycharacteristic.cpp \
qlowenergydescriptor.cpp \
@@ -88,6 +93,7 @@ config_bluez:qtHaveModule(dbus) {
# old versions of Bluez do not have the required BTLE symbols
config_bluez_le {
SOURCES += \
+ qleadvertiser_bluez.cpp \
qlowenergycontroller_bluez.cpp
} else {
message("Bluez version is too old to support Bluetooth Low Energy.")
diff --git a/src/bluetooth/bluez/bluez_data_p.h b/src/bluetooth/bluez/bluez_data_p.h
index 5d6a90ef..456a9374 100644
--- a/src/bluetooth/bluez/bluez_data_p.h
+++ b/src/bluetooth/bluez/bluez_data_p.h
@@ -217,12 +217,14 @@ template<> inline void putBtData(quint128 src, void *dst)
#define HCI_FILTER 2
// HCI packet types
+#define HCI_COMMAND_PKT 0x01
#define HCI_EVENT_PKT 0x04
#define HCI_VENDOR_PKT 0xff
#define HCI_FLT_TYPE_BITS 31
#define HCI_FLT_EVENT_BITS 63
+
struct sockaddr_hci {
sa_family_t hci_family;
unsigned short hci_dev;
@@ -347,6 +349,36 @@ typedef struct {
} __attribute__ ((packed)) evt_encrypt_change;
#define EVT_ENCRYPT_CHANGE_SIZE 4
+#define EVT_CMD_COMPLETE 0x0E
+struct evt_cmd_complete {
+ quint8 ncmd;
+ quint16 opcode;
+} __attribute__ ((packed));
+
+struct hci_command_hdr {
+ quint16 opcode; /* OCF & OGF */
+ quint8 plen;
+} __attribute__ ((packed));
+
+enum OpCodeGroupField {
+ OgfLinkControl = 0x8,
+};
+
+enum OpCodeCommandField {
+ OcfLeSetAdvParams = 0x6,
+ OcfLeReadTxPowerLevel = 0x7,
+ OcfLeSetAdvData = 0x8,
+ OcfLeSetScanResponseData = 0x9,
+ OcfLeSetAdvEnable = 0xa,
+ OcfLeClearWhiteList = 0x10,
+ OcfLeAddToWhiteList = 0x11,
+};
+
+/* Command opcode pack/unpack */
+#define opCodePack(ogf, ocf) (quint16(((ocf) & 0x03ff)|((ogf) << 10)))
+#define ogfFromOpCode(op) ((op) >> 10)
+#define ocfFromOpCode(op) ((op) & 0x03ff)
+
QT_END_NAMESPACE
#endif // BLUEZ_DATA_P_H
diff --git a/src/bluetooth/bluez/hcimanager.cpp b/src/bluetooth/bluez/hcimanager.cpp
index 30511ae5..388f3e0c 100644
--- a/src/bluetooth/bluez/hcimanager.cpp
+++ b/src/bluetooth/bluez/hcimanager.cpp
@@ -36,12 +36,14 @@
#include "qbluetoothsocket_p.h"
-#include <QtCore/QLoggingCategory>
+#include <QtCore/qloggingcategory.h>
+#include <cstring>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
+#include <sys/uio.h>
#include <unistd.h>
#define HCIGETCONNLIST _IOR('H', 212, int)
@@ -174,6 +176,36 @@ bool HciManager::monitorEvent(HciManager::HciEvent event)
return true;
}
+bool HciManager::sendCommand(OpCodeGroupField ogf, OpCodeCommandField ocf, const QByteArray &parameters)
+{
+ qCDebug(QT_BT_BLUEZ) << "sending command; ogf:" << ogf << "ocf:" << ocf;
+ quint8 packetType = HCI_COMMAND_PKT;
+ hci_command_hdr command = {
+ opCodePack(ogf, ocf),
+ static_cast<uint8_t>(parameters.count())
+ };
+ static_assert(sizeof command == 3, "unexpected struct size");
+ struct iovec iv[3];
+ iv[0].iov_base = &packetType;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = &command;
+ iv[1].iov_len = sizeof command;
+ int ivn = 2;
+ if (!parameters.isEmpty()) {
+ iv[2].iov_base = const_cast<char *>(parameters.constData()); // const_cast is safe, since iov_base will not get modified.
+ iv[2].iov_len = parameters.count();
+ ++ivn;
+ }
+ while (writev(hciSocket, iv, ivn) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ qCDebug(QT_BT_BLUEZ()) << "hci command failure:" << strerror(errno);
+ return false;
+ }
+ qCDebug(QT_BT_BLUEZ) << "command sent successfully";
+ return true;
+}
+
/*
* Unsubscribe from all events
*/
@@ -275,6 +307,18 @@ void HciManager::_q_readNotify()
emit encryptionChangedEvent(remoteDevice, event->status == 0);
}
break;
+ case EVT_CMD_COMPLETE: {
+ auto * const event = reinterpret_cast<const evt_cmd_complete *>(data);
+ static_assert(sizeof *event == 3, "unexpected struct size");
+
+ // There is always a status byte right after the generic structure.
+ Q_ASSERT(size > static_cast<int>(sizeof *event));
+ const quint8 status = data[sizeof *event];
+ const auto additionalData = QByteArray(reinterpret_cast<const char *>(data)
+ + sizeof *event + 1, size - sizeof *event - 1);
+ emit commandCompleted(event->opcode, status, additionalData);
+ }
+ break;
default:
break;
}
diff --git a/src/bluetooth/bluez/hcimanager_p.h b/src/bluetooth/bluez/hcimanager_p.h
index 9dd2ceee..c8f2fe56 100644
--- a/src/bluetooth/bluez/hcimanager_p.h
+++ b/src/bluetooth/bluez/hcimanager_p.h
@@ -59,6 +59,7 @@ class HciManager : public QObject
public:
enum HciEvent {
EncryptChangeEvent = EVT_ENCRYPT_CHANGE,
+ CommandCompleteEvent = EVT_CMD_COMPLETE,
};
explicit HciManager(const QBluetoothAddress &deviceAdapter, QObject *parent = 0);
@@ -66,12 +67,13 @@ 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;
-
signals:
void encryptionChangedEvent(const QBluetoothAddress &address, bool wasSuccess);
+ void commandCompleted(quint16 opCode, quint8 status, const QByteArray &data);
private slots:
void _q_readNotify();
diff --git a/src/bluetooth/qleadvertiser_bluez.cpp b/src/bluetooth/qleadvertiser_bluez.cpp
new file mode 100644
index 00000000..4c231dca
--- /dev/null
+++ b/src/bluetooth/qleadvertiser_bluez.cpp
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** 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 "qleadvertiser_p.h"
+
+#include "bluez/bluez_data_p.h"
+#include "bluez/hcimanager_p.h"
+#include "qbluetoothsocket_p.h"
+
+#include <QtCore/qloggingcategory.h>
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
+
+struct AdvParams {
+ quint16 minInterval;
+ quint16 maxInterval;
+ quint8 type;
+ quint8 ownAddrType;
+ quint8 directAddrType;
+ bdaddr_t directAddr;
+ quint8 channelMap;
+ quint8 filterPolicy;
+} __attribute__ ((packed));
+
+struct AdvData {
+ quint8 length;
+ quint8 data[31];
+};
+
+struct WhiteListParams {
+ quint8 addrType;
+ bdaddr_t addr;
+};
+
+
+template<typename T> QByteArray byteArrayFromStruct(const T &data, int maxSize = -1)
+{
+ return QByteArray(reinterpret_cast<const char *>(&data), maxSize != -1 ? maxSize : sizeof data);
+}
+
+QLeAdvertiserBluez::QLeAdvertiserBluez(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData,
+ HciManager &hciManager, QObject *parent)
+ : QLeAdvertiser(params, advertisingData, scanResponseData, parent), m_hciManager(hciManager)
+{
+ connect(&m_hciManager, &HciManager::commandCompleted, this,
+ &QLeAdvertiserBluez::handleCommandCompleted);
+}
+
+QLeAdvertiserBluez::~QLeAdvertiserBluez()
+{
+ disconnect(&m_hciManager, &HciManager::commandCompleted, this,
+ &QLeAdvertiserBluez::handleCommandCompleted);
+ doStopAdvertising();
+}
+
+void QLeAdvertiserBluez::doStartAdvertising()
+{
+ if (!m_hciManager.monitorEvent(HciManager::CommandCompleteEvent)) {
+ handleError();
+ return;
+ }
+
+ m_disableCommandFinished = false;
+ m_sendPowerLevel = advertisingData().includePowerLevel()
+ || scanResponseData().includePowerLevel();
+ if (m_sendPowerLevel)
+ queueReadTxPowerLevelCommand();
+ else
+ queueAdvertisingCommands();
+ sendNextCommand();
+}
+
+void QLeAdvertiserBluez::doStopAdvertising()
+{
+ toggleAdvertising(false);
+}
+
+void QLeAdvertiserBluez::queueCommand(OpCodeCommandField ocf, const QByteArray &data)
+{
+ m_pendingCommands << Command(ocf, data);
+}
+
+void QLeAdvertiserBluez::sendNextCommand()
+{
+ if (m_pendingCommands.isEmpty()) {
+ // TODO: Unmonitor event.
+ return;
+ }
+ const Command &c = m_pendingCommands.first();
+ if (!m_hciManager.sendCommand(OgfLinkControl, c.ocf, c.data)) {
+ handleError();
+ return;
+ }
+}
+
+void QLeAdvertiserBluez::queueAdvertisingCommands()
+{
+ toggleAdvertising(false); // Stop advertising first, in case it's currently active.
+ setWhiteList();
+ setAdvertisingParams();
+ setAdvertisingData();
+ setScanResponseData();
+ toggleAdvertising(true);
+}
+
+void QLeAdvertiserBluez::queueReadTxPowerLevelCommand()
+{
+ // Spec v4.2, Vol 2, Part E, 7.8.6
+ queueCommand(OcfLeReadTxPowerLevel, QByteArray());
+}
+
+void QLeAdvertiserBluez::toggleAdvertising(bool enable)
+{
+ // Spec v4.2, Vol 2, Part E, 7.8.9
+ queueCommand(OcfLeSetAdvEnable, QByteArray(1, enable));
+}
+
+void QLeAdvertiserBluez::setAdvertisingParams()
+{
+ // Spec v4.2, Vol 2, Part E, 7.8.5
+ AdvParams params;
+ static_assert(sizeof params == 15, "unexpected struct size");
+ setAdvertisingInterval(params);
+ params.type = parameters().mode();
+ params.filterPolicy = parameters().filterPolicy();
+ if (params.filterPolicy != QLowEnergyAdvertisingParameters::IgnoreWhiteList
+ && advertisingData().discoverability() == QLowEnergyAdvertisingData::DiscoverabilityLimited) {
+ qCWarning(QT_BT_BLUEZ) << "limited discoverability is incompatible with "
+ "using a white list; disabling filtering";
+ params.filterPolicy = QLowEnergyAdvertisingParameters::IgnoreWhiteList;
+ }
+ params.ownAddrType = QLowEnergyController::PublicAddress; // TODO: Make configurable.
+
+ // TODO: For ADV_DIRECT_IND.
+ // params.directAddrType = xxx;
+ // params.direct_bdaddr = xxx;
+
+ params.channelMap = 0x7; // All channels.
+
+ const QByteArray paramsData = byteArrayFromStruct(params);
+ qCDebug(QT_BT_BLUEZ) << "advertising parameters:" << paramsData.toHex();
+ queueCommand(OcfLeSetAdvParams, paramsData);
+}
+
+static quint16 forceIntoRange(quint16 val, quint16 min, quint16 max)
+{
+ return qMin(qMax(val, min), max);
+}
+
+void QLeAdvertiserBluez::setAdvertisingInterval(AdvParams &params)
+{
+ const double multiplier = 0.625;
+ const quint16 minVal = parameters().minimumInterval() / multiplier;
+ const quint16 maxVal = parameters().maximumInterval() / multiplier;
+ Q_ASSERT(minVal <= maxVal);
+ const quint16 specMinimum =
+ parameters().mode() == QLowEnergyAdvertisingParameters::AdvScanInd
+ || parameters().mode() == QLowEnergyAdvertisingParameters::AdvNonConnInd ? 0xa0 : 0x20;
+ const quint16 specMaximum = 0x4000;
+ params.minInterval = forceIntoRange(minVal, specMinimum, specMaximum);
+ params.maxInterval = forceIntoRange(maxVal, specMinimum, specMaximum);
+ Q_ASSERT(params.minInterval <= params.maxInterval);
+}
+
+void QLeAdvertiserBluez::setPowerLevel(AdvData &advData)
+{
+ if (m_sendPowerLevel) {
+ advData.data[advData.length++] = 2;
+ advData.data[advData.length++]= 0xa;
+ advData.data[advData.length++] = m_powerLevel;
+ }
+}
+
+void QLeAdvertiserBluez::setFlags(AdvData &advData)
+{
+ // TODO: Discoverability flags are incompatible with ADV_DIRECT_IND
+ quint8 flags = 0;
+ if (advertisingData().discoverability() == QLowEnergyAdvertisingData::DiscoverabilityLimited)
+ flags |= 0x1;
+ else if (advertisingData().discoverability() == QLowEnergyAdvertisingData::DiscoverabilityLimited)
+ flags |= 0x2;
+ if (flags) {
+ advData.data[advData.length++] = 2;
+ advData.data[advData.length++] = 0x1;
+ advData.data[advData.length++] = flags;
+ }
+}
+
+template<typename T> static quint8 servicesType(bool dataComplete);
+template<> quint8 servicesType<quint16>(bool dataComplete)
+{
+ return dataComplete ? 0x3 : 0x2;
+}
+template<> quint8 servicesType<quint32>(bool dataComplete)
+{
+ return dataComplete ? 0x5 : 0x4;
+}
+template<> quint8 servicesType<quint128>(bool dataComplete)
+{
+ return dataComplete ? 0x7 : 0x6;
+}
+
+template<typename T> static void addServicesData(AdvData &data, const QVector<T> &services)
+{
+ if (services.isEmpty())
+ return;
+ const int spaceAvailable = sizeof data.data - data.length;
+ const int maxServices = qMin<int>((spaceAvailable - 2) / sizeof(T), services.count());
+ if (maxServices == 0) {
+ qCWarning(QT_BT_BLUEZ) << "services data does not fit into advertising data packet";
+ return;
+ }
+ const bool dataComplete = maxServices == services.count();
+ if (!dataComplete) {
+ qCWarning(QT_BT_BLUEZ) << "only" << maxServices << "out of" << services.count()
+ << "services fit into the advertising data";
+ }
+ data.data[data.length++] = 1 + maxServices * sizeof(T);
+ data.data[data.length++] = servicesType<T>(dataComplete);
+ for (int i = 0; i < maxServices; ++i) {
+ putBtData(services.at(i), data.data + data.length);
+ data.length += sizeof(T);
+ }
+}
+
+void QLeAdvertiserBluez::setServicesData(const QLowEnergyAdvertisingData &src, AdvData &dest)
+{
+ QVector<quint16> services16;
+ QVector<quint32> services32;
+ QVector<quint128> services128;
+ foreach (const QBluetoothUuid &service, src.services()) {
+ bool ok;
+ const quint16 service16 = service.toUInt16(&ok);
+ if (ok) {
+ services16 << service16;
+ continue;
+ }
+ const quint32 service32 = service.toUInt32(&ok);
+ if (ok) {
+ services32 << service32;
+ continue;
+ }
+ services128 << service.toUInt128();
+ }
+ addServicesData(dest, services16);
+ addServicesData(dest, services32);
+ addServicesData(dest, services128);
+}
+
+void QLeAdvertiserBluez::setManufacturerData(const QLowEnergyAdvertisingData &src, AdvData &dest)
+{
+ if (src.manufacturerId() == QLowEnergyAdvertisingData::invalidManufacturerId())
+ return;
+ if (dest.length >= sizeof dest.data - 1 - 1 - 2 - src.manufacturerData().count()) {
+ qCWarning(QT_BT_BLUEZ) << "manufacturer data does not fit into advertising data packet";
+ return;
+ }
+
+ dest.data[dest.length++] = src.manufacturerData().count() + 1 + 2;
+ dest.data[dest.length++] = 0xff;
+ putBtData(src.manufacturerId(), dest.data + dest.length);
+ dest.length += sizeof(quint16);
+ std::memcpy(dest.data + dest.length, src.manufacturerData(), src.manufacturerData().count());
+ dest.length += src.manufacturerData().count();
+}
+
+void QLeAdvertiserBluez::setLocalNameData(const QLowEnergyAdvertisingData &src, AdvData &dest)
+{
+ if (src.localName().isEmpty())
+ return;
+ if (dest.length >= sizeof dest.data - 3) {
+ qCWarning(QT_BT_BLUEZ) << "local name does not fit into advertising data";
+ return;
+ }
+
+ const QByteArray localNameUtf8 = src.localName().toUtf8();
+ const int fullSize = localNameUtf8.count() + 1 + 1;
+ const int size = qMin<int>(fullSize, sizeof dest.data - dest.length);
+ const bool isComplete = size == fullSize;
+ dest.data[dest.length++] = size - 1;
+ const int dataType = isComplete ? 0x9 : 0x8;
+ dest.data[dest.length++] = dataType;
+ std::memcpy(dest.data + dest.length, localNameUtf8, size - 2);
+ dest.length += size - 2;
+}
+
+void QLeAdvertiserBluez::setData(bool isScanResponseData)
+{
+ // Spec v4.2, Vol 3, Part C, 11 and Supplement, Part 1
+ AdvData theData;
+ static_assert(sizeof theData == 32, "unexpected struct size");
+ theData.length = 0;
+
+ const QLowEnergyAdvertisingData &sourceData = isScanResponseData
+ ? scanResponseData() : advertisingData();
+
+ if (!sourceData.rawData().isEmpty()) {
+ theData.length = qMin<int>(sizeof theData.data, sourceData.rawData().count());
+ std::memcpy(theData.data, sourceData.rawData().constData(), theData.length);
+ } else {
+ if (sourceData.includePowerLevel())
+ setPowerLevel(theData);
+ if (!isScanResponseData)
+ setFlags(theData);
+
+ // Insert new constant-length data here.
+
+ setLocalNameData(sourceData, theData);
+ setServicesData(sourceData, theData);
+ setManufacturerData(sourceData, theData);
+ }
+
+ const QByteArray dataToSend = byteArrayFromStruct(theData, 1 + theData.length);
+ if (!isScanResponseData) {
+ qCDebug(QT_BT_BLUEZ) << "advertising data:" << dataToSend.toHex();
+ queueCommand(OcfLeSetAdvData, dataToSend);
+ } else if ((parameters().mode() == QLowEnergyAdvertisingParameters::AdvScanInd
+ || parameters().mode() == QLowEnergyAdvertisingParameters::AdvInd)
+ && theData.length > 0) {
+ qCDebug(QT_BT_BLUEZ) << "scan response data:" << dataToSend.toHex();
+ queueCommand(OcfLeSetScanResponseData, dataToSend);
+ }
+}
+
+void QLeAdvertiserBluez::setAdvertisingData()
+{
+ // Spec v4.2, Vol 2, Part E, 7.8.7
+ setData(false);
+}
+
+void QLeAdvertiserBluez::setScanResponseData()
+{
+ // Spec v4.2, Vol 2, Part E, 7.8.8
+ setData(true);
+}
+
+void QLeAdvertiserBluez::setWhiteList()
+{
+ // Spec v4.2, Vol 2, Part E, 7.8.15-16
+ if (parameters().filterPolicy() == QLowEnergyAdvertisingParameters::IgnoreWhiteList)
+ return;
+ queueCommand(OcfLeClearWhiteList, QByteArray());
+ foreach (const auto &addressInfo, parameters().whiteList()) {
+ WhiteListParams commandParam;
+ static_assert(sizeof commandParam == 7, "unexpected struct size");
+ commandParam.addrType = addressInfo.type;
+ convertAddress(addressInfo.address.toUInt64(), commandParam.addr.b);
+ queueCommand(OcfLeAddToWhiteList, byteArrayFromStruct(commandParam));
+ }
+}
+
+void QLeAdvertiserBluez::handleCommandCompleted(quint16 opCode, quint8 status,
+ const QByteArray &data)
+{
+ if (m_pendingCommands.isEmpty())
+ return;
+ const quint16 ocf = ocfFromOpCode(opCode);
+ if (m_pendingCommands.first().ocf != ocf)
+ return; // Not one of our commands.
+ m_pendingCommands.takeFirst();
+ if (status != 0) {
+ qCDebug(QT_BT_BLUEZ) << "command" << ocf << "failed with status" << status;
+ if (ocf == OcfLeSetAdvEnable && !m_disableCommandFinished && status == 0xc) {
+ qCDebug(QT_BT_BLUEZ) << "initial advertising disable failed, ignoring";
+ m_disableCommandFinished = true;
+ sendNextCommand();
+ return;
+ }
+ if (ocf == OcfLeReadTxPowerLevel) {
+ qCDebug(QT_BT_BLUEZ) << "reading power level failed, leaving it out of the "
+ "advertising data";
+ m_sendPowerLevel = false;
+ } else {
+ handleError();
+ return;
+ }
+ } else {
+ qCDebug(QT_BT_BLUEZ) << "command" << ocf << "executed successfully";
+ }
+
+ switch (ocf) {
+ case OcfLeReadTxPowerLevel:
+ if (m_sendPowerLevel) {
+ m_powerLevel = data.at(0);
+ qCDebug(QT_BT_BLUEZ) << "TX power level is" << m_powerLevel;
+ }
+ queueAdvertisingCommands();
+ break;
+ case OcfLeSetAdvEnable:
+ if (!m_disableCommandFinished)
+ m_disableCommandFinished = true;
+ break;
+ default:
+ break;
+ }
+
+ sendNextCommand();
+}
+
+void QLeAdvertiserBluez::handleError()
+{
+ m_pendingCommands.clear();
+ // TODO: Unmonitor event
+ emit errorOccurred();
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qleadvertiser_p.h b/src/bluetooth/qleadvertiser_p.h
new file mode 100644
index 00000000..bdcfcf1c
--- /dev/null
+++ b/src/bluetooth/qleadvertiser_p.h
@@ -0,0 +1,137 @@
+/****************************************************************************
+**
+** 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 QLEADVERTISER_P_H
+#define QLEADVERTISER_P_H
+
+#include "qlowenergyadvertisingdata.h"
+#include "qlowenergyadvertisingparameters.h"
+
+#ifdef QT_BLUEZ_BLUETOOTH
+#include "bluez/bluez_data_p.h"
+#endif
+
+#include <QtCore/qobject.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QLeAdvertiser : public QObject
+{
+ Q_OBJECT
+public:
+ void startAdvertising() { doStartAdvertising(); }
+ void stopAdvertising() { doStopAdvertising(); }
+
+signals:
+ void errorOccurred();
+
+protected:
+ QLeAdvertiser(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advData,
+ const QLowEnergyAdvertisingData &responseData, QObject *parent)
+ : QObject(parent), m_params(params), m_advData(advData), m_responseData(responseData) {}
+ virtual ~QLeAdvertiser() { }
+
+ const QLowEnergyAdvertisingParameters &parameters() const { return m_params; }
+ const QLowEnergyAdvertisingData &advertisingData() const { return m_advData; }
+ const QLowEnergyAdvertisingData &scanResponseData() const { return m_responseData; }
+
+private:
+ virtual void doStartAdvertising() = 0;
+ virtual void doStopAdvertising() = 0;
+
+ const QLowEnergyAdvertisingParameters m_params;
+ const QLowEnergyAdvertisingData m_advData;
+ const QLowEnergyAdvertisingData m_responseData;
+};
+
+
+#ifdef QT_BLUEZ_BLUETOOTH
+struct AdvData;
+struct AdvParams;
+class HciManager;
+
+class QLeAdvertiserBluez : public QLeAdvertiser
+{
+public:
+ QLeAdvertiserBluez(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData, HciManager &hciManager,
+ QObject *parent = nullptr);
+ ~QLeAdvertiserBluez();
+
+private:
+ void doStartAdvertising() override;
+ void doStopAdvertising() override;
+
+ void setPowerLevel(AdvData &advData);
+ void setFlags(AdvData &advData);
+ void setServicesData(const QLowEnergyAdvertisingData &src, AdvData &dest);
+ void setManufacturerData(const QLowEnergyAdvertisingData &src, AdvData &dest);
+ void setLocalNameData(const QLowEnergyAdvertisingData &src, AdvData &dest);
+
+ void queueCommand(OpCodeCommandField ocf, const QByteArray &advertisingData);
+ void sendNextCommand();
+ void queueAdvertisingCommands();
+ void queueReadTxPowerLevelCommand();
+ void toggleAdvertising(bool enable);
+ void setAdvertisingParams();
+ void setAdvertisingInterval(AdvParams &params);
+ void setData(bool isScanResponseData);
+ void setAdvertisingData();
+ void setScanResponseData();
+ void setWhiteList();
+
+ void handleCommandCompleted(quint16 opCode, quint8 status, const QByteArray &advertisingData);
+ void handleError();
+
+ HciManager &m_hciManager;
+
+ struct Command {
+ Command() {}
+ Command(OpCodeCommandField ocf, const QByteArray &data) : ocf(ocf), data(data) { }
+ OpCodeCommandField ocf;
+ QByteArray data;
+ };
+ QVector<Command> m_pendingCommands;
+
+ quint8 m_powerLevel;
+ bool m_sendPowerLevel;
+ bool m_disableCommandFinished;
+};
+#endif // QT_BLUEZ_BLUETOOTH
+
+QT_END_NAMESPACE
+
+#endif // Include guard.
diff --git a/src/bluetooth/qlowenergyadvertisingdata.cpp b/src/bluetooth/qlowenergyadvertisingdata.cpp
new file mode 100644
index 00000000..e68a1b35
--- /dev/null
+++ b/src/bluetooth/qlowenergyadvertisingdata.cpp
@@ -0,0 +1,280 @@
+/****************************************************************************
+**
+** 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 "qlowenergyadvertisingdata.h"
+
+#include <cstring>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyAdvertisingDataPrivate : public QSharedData
+{
+public:
+ QLowEnergyAdvertisingDataPrivate()
+ : manufacturerId(QLowEnergyAdvertisingData::invalidManufacturerId())
+ , discoverability(QLowEnergyAdvertisingData::DiscoverabilityNone)
+ , includePowerLevel(false)
+ {
+ }
+
+ QString localName;
+ QByteArray manufacturerData;
+ QByteArray rawData;
+ QList<QBluetoothUuid> services;
+ quint16 manufacturerId;
+ QLowEnergyAdvertisingData::Discoverability discoverability;
+ bool includePowerLevel;
+};
+
+/*!
+ \since 5.7
+ \class QLowEnergyAdvertisingData
+ \brief The QLowEnergyAdvertisingData class represents the data to be broadcast during
+ Bluetooth Low Energy advertising.
+ \inmodule QtBluetooth
+ \ingroup shared
+
+ This data can include the device name, GATT services offered by the device, and so on.
+ The data set via this class will be used when advertising is started by calling
+ \l QLowEnergyController::startAdvertising(). Objects of this class can represent an
+ Advertising Data packet or a Scan Response packet.
+ \note The actual data packets sent over the advertising channel cannot contain more than 31
+ bytes. If the variable-length data set via this class exceeds that limit, it will
+ be left out of the packet or truncated, depending on the type.
+
+ \sa QLowEnergyAdvertisingParameters
+ \sa QLowEnergyController::startAdvertising()
+*/
+
+/*!
+ \enum QLowEnergyAdvertisingData::Discoverability
+
+ The discoverability of the advertising device as defined by the Generic Access Profile.
+
+ \value DiscoverabilityNone
+ The advertising device does not wish to be discoverable by scanning devices.
+ \value DiscoverabilityLimited
+ The advertising device wishes to be discoverable with a high priority. Note that this mode
+ is not compatible with using a white list. The value of
+ \l QLowEnergyAdvertisingParameters::filterPolicy() is always assumed to be
+ \l QLowEnergyAdvertisingParameters::IgnoreWhiteList when limited discoverability
+ is used.
+ \value DiscoverabilityGeneral
+ The advertising device wishes to be discoverable by scanning devices.
+ */
+
+/*!
+ Creates a new object of this class. All values are initialized to their defaults
+ according to the Bluetooth Low Energy specification.
+ */
+QLowEnergyAdvertisingData::QLowEnergyAdvertisingData() : d(new QLowEnergyAdvertisingDataPrivate)
+{
+}
+
+/*! Constructs a new object of this class that is a copy of \a other. */
+QLowEnergyAdvertisingData::QLowEnergyAdvertisingData(const QLowEnergyAdvertisingData &other)
+ : d(other.d)
+{
+}
+
+/*! Destroys this object. */
+QLowEnergyAdvertisingData::~QLowEnergyAdvertisingData()
+{
+}
+
+/*! Makes this object a copy of \a other and returns the new value of this object. */
+QLowEnergyAdvertisingData &QLowEnergyAdvertisingData::operator=(const QLowEnergyAdvertisingData &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Specifies that \a name should be broadcast as the name of the device. If the full name does not
+ fit into the advertising data packet, an abbreviated name is sent, as described by the
+ Bluetooth Low Energy specification.
+ */
+void QLowEnergyAdvertisingData::setLocalName(const QString &name)
+{
+ d->localName = name;
+}
+
+/*!
+ Returns the name of the local device that is to be advertised.
+ */
+QString QLowEnergyAdvertisingData::localName() const
+{
+ return d->localName;
+}
+
+/*!
+ Sets the manufacturer id and data. The \a id parameter is a company identifier as assigned
+ by the Bluetooth SIG. The \a data parameter is an arbitrary value.
+ */
+void QLowEnergyAdvertisingData::setManufacturerData(quint16 id, const QByteArray &data)
+{
+ d->manufacturerId = id;
+ d->manufacturerData = data;
+}
+
+/*!
+ Returns the manufacturer id.
+ The default is \l QLowEnergyAdvertisingData::invalidManufacturerId(), which means
+ the data will not be advertised.
+ */
+quint16 QLowEnergyAdvertisingData::manufacturerId() const
+{
+ return d->manufacturerId;
+}
+
+/*!
+ Returns the manufacturer data. The default is an empty byte array.
+ */
+QByteArray QLowEnergyAdvertisingData::manufacturerData() const
+{
+ return d->manufacturerData;
+}
+
+/*!
+ Specifies whether to include the device's transmit power level in the advertising data. If
+ \a doInclude is \c true, the data will be included, otherwise it will not.
+ */
+void QLowEnergyAdvertisingData::setIncludePowerLevel(bool doInclude)
+{
+ d->includePowerLevel = doInclude;
+}
+
+/*!
+ Returns whether to include the device's transmit power level in the advertising data.
+ The default is \c false.
+ */
+bool QLowEnergyAdvertisingData::includePowerLevel() const
+{
+ return d->includePowerLevel;
+}
+
+/*!
+ Sets the discoverability type of the advertising device to \a mode.
+ \note Discoverability information can only appear in an actual advertising data packet. If
+ this object acts as scan response data, a call to this function will have no effect
+ on the scan response sent.
+ */
+void QLowEnergyAdvertisingData::setDiscoverability(QLowEnergyAdvertisingData::Discoverability mode)
+{
+ d->discoverability = mode;
+}
+
+/*!
+ Returns the discoverability mode of the advertising device.
+ The default is \l DiscoverabilityNone.
+ */
+QLowEnergyAdvertisingData::Discoverability QLowEnergyAdvertisingData::discoverability() const
+{
+ return d->discoverability;
+}
+
+/*!
+ Specifies that the service UUIDs in \a services should be advertised.
+ If the entire list does not fit into the packet, an incomplete list is sent as specified
+ by the Bluetooth Low Energy specification.
+ */
+void QLowEnergyAdvertisingData::setServices(const QList<QBluetoothUuid> &services)
+{
+ d->services = services;
+}
+
+/*!
+ Returns the list of service UUIDs to be advertised.
+ By default, this list is empty.
+ */
+QList<QBluetoothUuid> QLowEnergyAdvertisingData::services() const
+{
+ return d->services;
+}
+
+/*!
+ Sets the data to be advertised to \a data. If the value is not an empty byte array, it will
+ be sent as-is as the advertising data and all other data in this object will be ignored.
+ This can be used to send non-standard data.
+ \note If \a data is longer than 31 bytes, it will be truncated. It is the caller's responsibility
+ to ensure that \a data is well-formed.
+ */
+void QLowEnergyAdvertisingData::setRawData(const QByteArray &data)
+{
+ d->rawData = data;
+}
+
+/*!
+ Returns the user-supplied raw data to be advertised. The default is an empty byte array.
+ */
+QByteArray QLowEnergyAdvertisingData::rawData() const
+{
+ return d->rawData;
+}
+
+/*!
+ \fn void QLowEnergyAdvertisingData::swap(QLowEnergyAdvertisingData &other)
+ Swaps this object with \a other.
+ */
+
+/*!
+ Returns \c true if \a data1 and \a data2 are equal with respect to their public state,
+ otherwise returns \c false.
+ */
+bool operator==(const QLowEnergyAdvertisingData &data1, const QLowEnergyAdvertisingData &data2)
+{
+ if (data1.d == data2.d)
+ return true;
+ return data1.discoverability() == data2.discoverability()
+ && data1.includePowerLevel() == data2.includePowerLevel()
+ && data1.localName() == data2.localName()
+ && data1.manufacturerData() == data2.manufacturerData()
+ && data1.manufacturerId() == data2.manufacturerId()
+ && data1.services() == data2.services()
+ && data1.rawData() == data2.rawData();
+}
+
+/*!
+ \fn bool operator!=(const QLowEnergyAdvertisingData &data1,
+ const QLowEnergyAdvertisingData &data2)
+ Returns \c true if \a data1 and \a data2 are not equal with respect to their public state,
+ otherwise returns \c false.
+ */
+
+/*!
+ \fn static quint16 QLowEnergyAdvertisingData::invalidManufacturerId();
+ Returns an invalid manufacturer id. If this value is set as the manufacturer id
+ (which it is by default), no manufacturer data will be present in the advertising data.
+ */
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergyadvertisingdata.h b/src/bluetooth/qlowenergyadvertisingdata.h
new file mode 100644
index 00000000..314df3f0
--- /dev/null
+++ b/src/bluetooth/qlowenergyadvertisingdata.h
@@ -0,0 +1,99 @@
+/****************************************************************************
+**
+** 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 QLOWENERGYADVERTISINGDATA_H
+#define QLOWENERGYADVERTISINGDATA_H
+
+#include <QtBluetooth/qbluetoothglobal.h>
+#include <QtBluetooth/qbluetoothuuid.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyAdvertisingDataPrivate;
+
+class Q_BLUETOOTH_EXPORT QLowEnergyAdvertisingData
+{
+ friend Q_BLUETOOTH_EXPORT bool operator==(const QLowEnergyAdvertisingData &data1,
+ const QLowEnergyAdvertisingData &data2);
+public:
+ QLowEnergyAdvertisingData();
+ QLowEnergyAdvertisingData(const QLowEnergyAdvertisingData &other);
+ ~QLowEnergyAdvertisingData();
+
+ QLowEnergyAdvertisingData &operator=(const QLowEnergyAdvertisingData &other);
+
+ void setLocalName(const QString &name);
+ QString localName() const;
+
+ static quint16 invalidManufacturerId() { return 0xffff; }
+ void setManufacturerData(quint16 id, const QByteArray &data);
+ quint16 manufacturerId() const;
+ QByteArray manufacturerData() const;
+
+ void setIncludePowerLevel(bool doInclude);
+ bool includePowerLevel() const;
+
+ enum Discoverability {
+ DiscoverabilityNone, DiscoverabilityLimited, DiscoverabilityGeneral
+ };
+ void setDiscoverability(Discoverability mode);
+ Discoverability discoverability() const;
+
+ void setServices(const QList<QBluetoothUuid> &services);
+ QList<QBluetoothUuid> services() const;
+
+ // TODO: BR/EDR capability flag?
+
+ void setRawData(const QByteArray &data);
+ QByteArray rawData() const;
+
+ void swap(QLowEnergyAdvertisingData &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+
+private:
+ QSharedDataPointer<QLowEnergyAdvertisingDataPrivate> d;
+};
+
+Q_BLUETOOTH_EXPORT bool operator==(const QLowEnergyAdvertisingData &data1,
+ const QLowEnergyAdvertisingData &data2);
+inline bool operator!=(const QLowEnergyAdvertisingData &data1,
+ const QLowEnergyAdvertisingData &data2)
+{
+ return !(data1 == data2);
+}
+
+Q_DECLARE_SHARED(QLowEnergyAdvertisingData)
+
+QT_END_NAMESPACE
+
+#endif // Include guard
diff --git a/src/bluetooth/qlowenergyadvertisingparameters.cpp b/src/bluetooth/qlowenergyadvertisingparameters.cpp
new file mode 100644
index 00000000..bf1e7083
--- /dev/null
+++ b/src/bluetooth/qlowenergyadvertisingparameters.cpp
@@ -0,0 +1,259 @@
+/****************************************************************************
+**
+** 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 "qlowenergyadvertisingparameters.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyAdvertisingParametersPrivate : public QSharedData
+{
+public:
+ QLowEnergyAdvertisingParametersPrivate()
+ : filterPolicy(QLowEnergyAdvertisingParameters::IgnoreWhiteList)
+ , mode(QLowEnergyAdvertisingParameters::AdvInd)
+ , minInterval(1280)
+ , maxInterval(1280)
+ {
+ }
+
+ QList<QLowEnergyAdvertisingParameters::AddressInfo> whiteList;
+ QLowEnergyAdvertisingParameters::FilterPolicy filterPolicy;
+ QLowEnergyAdvertisingParameters::Mode mode;
+ int minInterval;
+ int maxInterval;
+};
+
+/*!
+ \since 5.7
+ \class QLowEnergyAdvertisingParameters
+ \brief The QLowEnergyAdvertisingParameters class represents the parameters used for
+ Bluetooth Low Energy advertising.
+ \inmodule QtBluetooth
+ \ingroup shared
+
+ When running the advertising procedure, a number of parameters can be configured, such as
+ how fast to advertise or which clients, if any, can connect to the advertising device.
+ These parameters are set via this class, and their values will be used when advertising
+ is started by calling \l QLowEnergyController::startAdvertising().
+
+ \sa QLowEnergyAdvertisingData
+ \sa QLowEnergyController::startAdvertising()
+*/
+
+/*!
+ \enum QLowEnergyAdvertisingParameters::Mode
+
+ Specifies in which way to advertise.
+ \value AdvInd
+ For non-directed, connectable advertising. Advertising is not directed to
+ one specific device and a device seeing the advertisement can connect to the
+ advertising device or send scan requests.
+ \value AdvScanInd
+ For non-directed, scannable advertising. Advertising is not directed to
+ one specific device and a device seeing the advertisement can send a scan
+ request to the advertising device, but cannot connect to it.
+ \value AdvNonConnInd
+ For non-directed, non-connectable advertising. Advertising is not directed to
+ one specific device. A device seeing the advertisement cannot connect to the
+ advertising device, nor can it send a scan request. This mode thus implies
+ pure broadcasting.
+*/
+
+/*!
+ \enum QLowEnergyAdvertisingParameters::FilterPolicy
+
+ Specifies the semantics of the white list.
+ \value IgnoreWhiteList
+ The value of the white list is ignored, that is, no filtering takes place for
+ either scan or connection requests when using undirected advertising.
+ \value UseWhiteListForScanning
+ The white list is used when handling scan requests, but is ignored for connection
+ requests.
+ \value UseWhiteListForConnecting
+ The white list is used when handling connection requests, but is ignored for scan
+ requests.
+ \value UseWhiteListForScanningAndConnecting
+ The white list is used for both connection and scan requests.
+
+ \sa QLowEnergyAdvertisingParameters::whiteList()
+*/
+
+/*!
+ \struct QLowEnergyAdvertisingParameters::AddressInfo
+
+ Objects of this type form the elements of a white list.
+ \sa QLowEnergyAdvertisingParameters::whiteList()
+*/
+
+/*!
+ \variable QLowEnergyAdvertisingParameters::AddressInfo::address
+ The Bluetooth address of a remote address.
+*/
+
+/*!
+ \variable QLowEnergyAdvertisingParameters::AddressInfo::type
+ The type of the address (public or private).
+*/
+
+
+/*!
+ Constructs a new object of this class. All values are initialized to their defaults
+ according to the Bluetooth Low Energy specification.
+ */
+QLowEnergyAdvertisingParameters::QLowEnergyAdvertisingParameters()
+ : d(new QLowEnergyAdvertisingParametersPrivate)
+{
+}
+
+/*! Constructs a new object of this class that is a copy of \a other. */
+QLowEnergyAdvertisingParameters::QLowEnergyAdvertisingParameters(const QLowEnergyAdvertisingParameters &other)
+ : d(other.d)
+{
+}
+
+/*! Destroys this object. */
+QLowEnergyAdvertisingParameters::~QLowEnergyAdvertisingParameters()
+{
+}
+
+/*! Makes this object a copy of \a other and returns the new value of this object. */
+QLowEnergyAdvertisingParameters &QLowEnergyAdvertisingParameters::operator=(const QLowEnergyAdvertisingParameters &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*! Sets the advertising mode to \a mode. */
+void QLowEnergyAdvertisingParameters::setMode(QLowEnergyAdvertisingParameters::Mode mode)
+{
+ d->mode = mode;
+}
+
+/*!
+ Returns the advertising mode. The default is \l QLowEnergyAdvertisingParameters::AdvInd.
+ */
+QLowEnergyAdvertisingParameters::Mode QLowEnergyAdvertisingParameters::mode() const
+{
+ return d->mode;
+}
+
+/*!
+ Sets the white list that is potentially used for filtering scan and connection requests.
+ The \a whiteList parameter is the list of addresses to use for filtering, and \a policy
+ specifies how exactly to use \a whiteList.
+ */
+void QLowEnergyAdvertisingParameters::setWhiteList(const QList<AddressInfo> &whiteList,
+ FilterPolicy policy)
+{
+ d->whiteList = whiteList;
+ d->filterPolicy = policy;
+}
+
+/*!
+ Returns the white list used for filtering scan and connection requests.
+ By default, this list is empty.
+ */
+QList<QLowEnergyAdvertisingParameters::AddressInfo> QLowEnergyAdvertisingParameters::whiteList() const
+{
+ return d->whiteList;
+}
+
+/*!
+ Returns the filter policy that determines how the white list is used. The default
+ is \l QLowEnergyAdvertisingParameters::IgnoreWhiteList.
+ */
+QLowEnergyAdvertisingParameters::FilterPolicy QLowEnergyAdvertisingParameters::filterPolicy() const
+{
+ return d->filterPolicy;
+}
+
+/*!
+ Sets the advertising interval. This is a range that gives the controller an upper and a lower
+ bound for how often to send the advertising data. 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.
+ \note There are limits for the minimum and maximum interval; the exact values depend on
+ the mode. If they are exceeded, the lowest or highest possible value will be used,
+ respectively.
+ */
+void QLowEnergyAdvertisingParameters::setInterval(quint16 minimum, quint16 maximum)
+{
+ d->minInterval = minimum;
+ d->maxInterval = qMax(minimum, maximum);
+}
+
+/*!
+ Returns the minimum advertising interval in milliseconds. The default is 1280.
+ */
+int QLowEnergyAdvertisingParameters::minimumInterval() const
+{
+ return d->minInterval;
+}
+
+/*!
+ Returns the maximum advertising interval in milliseconds. The default is 1280.
+ */
+int QLowEnergyAdvertisingParameters::maximumInterval() const
+{
+ return d->maxInterval;
+}
+
+/*!
+ \fn void QLowEnergyAdvertisingParameters::swap(QLowEnergyAdvertisingParameters &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 QLowEnergyAdvertisingParameters &p1,
+ const QLowEnergyAdvertisingParameters &p2)
+{
+ if (p1.d == p2.d)
+ return true;
+ return p1.filterPolicy() == p2.filterPolicy()
+ && p1.minimumInterval() == p2.minimumInterval()
+ && p1.maximumInterval() == p2.maximumInterval()
+ && p1.mode() == p2.mode()
+ && p1.whiteList() == p2.whiteList();
+}
+
+/*!
+ \fn bool operator!=(const QLowEnergyAdvertisingParameters &p1,
+ const QLowEnergyAdvertisingParameters &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/qlowenergyadvertisingparameters.h b/src/bluetooth/qlowenergyadvertisingparameters.h
new file mode 100644
index 00000000..8d98a10b
--- /dev/null
+++ b/src/bluetooth/qlowenergyadvertisingparameters.h
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** 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 QLOWENERGYADVERTISINGPARAMETERS_H
+#define QLOWENERGYADVERTISINGPARAMETERS_H
+
+#include <QtBluetooth/qbluetoothglobal.h>
+#include <QtBluetooth/qbluetoothaddress.h>
+#include <QtBluetooth/qlowenergycontroller.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyAdvertisingParametersPrivate;
+
+class Q_BLUETOOTH_EXPORT QLowEnergyAdvertisingParameters
+{
+ friend Q_BLUETOOTH_EXPORT bool operator==(const QLowEnergyAdvertisingParameters &p1,
+ const QLowEnergyAdvertisingParameters &p2);
+public:
+ QLowEnergyAdvertisingParameters();
+ QLowEnergyAdvertisingParameters(const QLowEnergyAdvertisingParameters &other);
+ ~QLowEnergyAdvertisingParameters();
+
+ QLowEnergyAdvertisingParameters &operator=(const QLowEnergyAdvertisingParameters &other);
+
+ enum Mode { AdvInd = 0x0, AdvScanInd = 0x2, AdvNonConnInd = 0x3 };
+ void setMode(Mode mode);
+ Mode mode() const;
+
+ struct AddressInfo {
+ AddressInfo(const QBluetoothAddress &addr, QLowEnergyController::RemoteAddressType t)
+ : address(addr), type(t) {}
+ AddressInfo() {}
+
+ QBluetoothAddress address;
+ QLowEnergyController::RemoteAddressType type;
+ };
+ enum FilterPolicy {
+ IgnoreWhiteList = 0x00,
+ UseWhiteListForScanning = 0x01,
+ UseWhiteListForConnecting = 0x02,
+ UseWhiteListForScanningAndConnecting = 0x03,
+ };
+ void setWhiteList(const QList<AddressInfo> &whiteList, FilterPolicy policy);
+ QList<AddressInfo> whiteList() const;
+ FilterPolicy filterPolicy() const;
+
+ void setInterval(quint16 minimum, quint16 maximum);
+ int minimumInterval() const;
+ int maximumInterval() const;
+
+ // TODO: own address type
+ // TODO: For ADV_DIRECT_IND: peer address + peer address type
+
+ void swap(QLowEnergyAdvertisingParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+
+private:
+ QSharedDataPointer<QLowEnergyAdvertisingParametersPrivate> d;
+};
+
+inline bool operator==(const QLowEnergyAdvertisingParameters::AddressInfo &ai1,
+ const QLowEnergyAdvertisingParameters::AddressInfo &ai2)
+{
+ return ai1.address == ai2.address && ai1.type == ai2.type;
+}
+
+Q_BLUETOOTH_EXPORT bool operator==(const QLowEnergyAdvertisingParameters &p1,
+ const QLowEnergyAdvertisingParameters &p2);
+inline bool operator!=(const QLowEnergyAdvertisingParameters &p1,
+ const QLowEnergyAdvertisingParameters &p2)
+{
+ return !(p1 == p2);
+}
+
+Q_DECLARE_SHARED(QLowEnergyAdvertisingParameters)
+
+QT_END_NAMESPACE
+
+#endif // Include guard
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
index 62e8f7e3..9a9327a5 100644
--- a/src/bluetooth/qlowenergycontroller.cpp
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -35,11 +35,14 @@
#include "qlowenergycontroller_p.h"
#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtCore/QLoggingCategory>
#include <algorithm>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(QT_BT)
+
/*!
\class QLowEnergyController
\inmodule QtBluetooth
@@ -49,9 +52,7 @@ QT_BEGIN_NAMESPACE
\since 5.4
QLowEnergyController acts as the entry point for Bluetooth Low Energy
- development. Each QLowEnergyController instance acts as placeholder
- towards a remote Low Energy device enabling connection control,
- service discovery and state tracking.
+ development.
Bluetooth Low Energy defines two types of devices; the peripheral and
the central. Each role performs a different task. The peripheral device
@@ -62,12 +63,12 @@ QT_BEGIN_NAMESPACE
the sensor is the peripheral device and the mobile phone acts as the
central device.
- At the moment Qt only supports the central role and therefore the remote
- device can only be a device acting as a peripheral. This implies that the local
- device acts within the boundaries of the central role as per the Bluetooth 4.0
- specification.
+ A controller in the central role is created via the \l createCentral() factory method.
+ Such an object essentially acts as a placeholder towards a remote Low Energy peripheral
+ device, enabling features such as service discovery and state tracking.
- The first step is to establish a connection via \l connectToDevice().
+ After having created a controller object in the central role, the first step is to establish
+ a connection via \l connectToDevice().
Once the connection has been established, the controller's \l state()
changes to \l QLowEnergyController::ConnectedState and the \l connected()
signal is emitted. It is important to mention that some platforms such as
@@ -93,7 +94,20 @@ QT_BEGIN_NAMESPACE
connection becomes invalid as soon as the controller disconnects from the
remote Bluetooth Low Energy device.
+ A controller in the peripheral role is created via the \l createPeripheral() factory method.
+ Such an object acts as a peripheral device itself, enabling features such as advertising
+ services and allowing clients to get notified about changes to characteristic values.
+
+ \omit
+ After having created a controller object in the peripheral role, the first step is to
+ populate the set of GATT services offered to client devices [not yet implemented].
+ Afterwards, one would call \l startAdvertising() to let the device broadcast some data
+ and, depending on the type of advertising being done, also listen for incoming connections
+ from GATT clients.
+ \endomit
+
\sa QLowEnergyService, QLowEnergyCharacteristic, QLowEnergyDescriptor
+ \sa QLowEnergyAdvertisingParameters, QLowEnergyAdvertisingData
*/
/*!
@@ -113,6 +127,8 @@ QT_BEGIN_NAMESPACE
there is no local Bluetooth device.
\value ConnectionError The attempt to connect to the remote device failed.
This value was introduced by Qt 5.5.
+ \value AdvertisingError The attempt to start advertising failed.
+ This value was introduced by Qt 5.7.
*/
/*!
@@ -128,6 +144,7 @@ QT_BEGIN_NAMESPACE
\value DiscoveredState The controller has discovered all services offered by the
remote device.
\value ClosingState The controller is about to be disconnected from the remote device.
+ \value AdvertisingState The controller is currently advertising data.
*/
/*!
@@ -135,26 +152,47 @@ QT_BEGIN_NAMESPACE
Indicates what type of Bluetooth address the remote device uses.
- \value PublicAddress The peripheral uses a public Bluetooth address.
+ \value PublicAddress The remote device uses a public Bluetooth address.
\value RandomAddress A random address is a Bluetooth Low Energy security feature.
Peripherals using such addresses may frequently change their
Bluetooth address. This information is needed when trying to
connect to a peripheral.
*/
+/*!
+ \enum QLowEnergyController::Role
+
+ Indicates the role of the controller object.
+
+ \value CentralRole
+ The controller acts as a client interacting with a remote device which is in the peripheral
+ role. The controller can initiate connections, discover services and
+ read and write characteristics.
+ \value PeripheralRole
+ The controller can be used to advertise services and handle incoming
+ connections and client requests, acting as a GATT server. A remote device connected to
+ the controller is in the central role.
+
+ \sa QLowEnergyController::createCentral()
+ \sa QLowEnergyController::createPeripheral()
+ \since 5.7
+ \note The peripheral role is currently only supported on Linux.
+ */
+
/*!
\fn void QLowEnergyController::connected()
This signal is emitted when the controller successfully connects to the remote
- Low Energy device.
+ Low Energy device (if the controller is in the \l CentralRole) or if a remote Low Energy
+ device connected to the controller (if the controller is in the \l PeripheralRole).
*/
/*!
\fn void QLowEnergyController::disconnected()
This signal is emitted when the controller disconnects from the remote
- Low Energy device.
+ Low Energy device or vice versa.
*/
/*!
@@ -181,6 +219,8 @@ QT_BEGIN_NAMESPACE
This signal is emitted each time a new service is discovered. The
\a newService parameter contains the UUID of the found service.
+ This signal can only be emitted if the controller is in the \c CentralRole.
+
\sa discoverServices(), discoveryFinished()
*/
@@ -191,6 +231,8 @@ QT_BEGIN_NAMESPACE
The signal is not emitted if the discovery process finishes with
an error.
+ This signal can only be emitted if the controller is in the \l CentralRole.
+
\sa discoverServices(), error()
*/
@@ -225,11 +267,14 @@ void QLowEnergyControllerPrivate::setError(
case QLowEnergyController::ConnectionError:
errorString = QLowEnergyController::tr("Error occurred trying to connect to remote device.");
break;
- case QLowEnergyController::NoError:
- return;
+ case QLowEnergyController::AdvertisingError:
+ errorString = QLowEnergyController::tr("Error occurred trying to start advertising");
+ break;
case QLowEnergyController::UnknownError:
errorString = QLowEnergyController::tr("Unknown Error");
break;
+ case QLowEnergyController::NoError:
+ return;
}
emit q->error(newError);
@@ -409,6 +454,7 @@ QLowEnergyController::QLowEnergyController(
{
Q_D(QLowEnergyController);
d->q_ptr = this;
+ d->role = CentralRole;
d->remoteDevice = remoteDevice;
d->localAdapter = QBluetoothLocalDevice().address();
d->addressType = QLowEnergyController::PublicAddress;
@@ -425,6 +471,7 @@ QLowEnergyController::QLowEnergyController(
the connection management.
\since 5.5
+ \obsolete
*/
QLowEnergyController::QLowEnergyController(
const QBluetoothDeviceInfo &remoteDeviceInfo,
@@ -433,6 +480,7 @@ QLowEnergyController::QLowEnergyController(
{
Q_D(QLowEnergyController);
d->q_ptr = this;
+ d->role = CentralRole;
d->remoteDevice = remoteDeviceInfo.address();
d->localAdapter = QBluetoothLocalDevice().address();
d->addressType = QLowEnergyController::PublicAddress;
@@ -462,11 +510,47 @@ QLowEnergyController::QLowEnergyController(
{
Q_D(QLowEnergyController);
d->q_ptr = this;
+ d->role = CentralRole;
d->remoteDevice = remoteDevice;
d->localAdapter = localDevice;
}
/*!
+ Returns a new object of this class that is in the \l CentralRole.
+ The \a remoteDevice refers to the device that a connection will be established to later.
+ *
+ The controller uses the local default Bluetooth adapter for the connection management.
+ \sa QLowEnergyController::CentralRole
+ */
+QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice,
+ QObject *parent)
+{
+ return new QLowEnergyController(remoteDevice, parent);
+}
+
+
+/*!
+ Returns a new object of this class that is in the \l PeripheralRole.
+ Typically, the next step is to call \l startAdvertising() on the returned object.
+ *
+ The controller uses the local default Bluetooth adapter for the connection management.
+ \sa QLowEnergyController::PeripheralRole
+ */
+QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent)
+{
+ return new QLowEnergyController(parent);
+}
+
+QLowEnergyController::QLowEnergyController(QObject *parent)
+ : QObject(parent), d_ptr(new QLowEnergyControllerPrivate())
+{
+ Q_D(QLowEnergyController);
+ d->q_ptr = this;
+ d->role = PeripheralRole;
+ d->localAdapter = QBluetoothLocalDevice().address();
+}
+
+/*!
Destroys the QLowEnergyController instance.
*/
QLowEnergyController::~QLowEnergyController()
@@ -492,6 +576,11 @@ QBluetoothAddress QLowEnergyController::localAddress() const
/*!
Returns the address of the remote Bluetooth Low Energy device.
+
+ For a controller in the \l CentralRole, this value will always be the one passed in when
+ the controller object was created. For a controller in the \l PeripheralRole, this value
+ is the address of the currently connected client device. In particular, this address will
+ be invalid if the controller is not currently in the \l ConnectedState.
*/
QBluetoothAddress QLowEnergyController::remoteAddress() const
{
@@ -499,7 +588,8 @@ QBluetoothAddress QLowEnergyController::remoteAddress() const
}
/*!
- Returns the name of the remote Bluetooth Low Energy device.
+ Returns the name of the remote Bluetooth Low Energy device, if the controller is in the
+ \l CentralRole. Otherwise the result is unspecified.
\since 5.5
*/
@@ -611,6 +701,10 @@ void QLowEnergyController::discoverServices()
{
Q_D(QLowEnergyController);
+ if (d->role != PeripheralRole) {
+ qCWarning(QT_BT) << "Cannot discover services in peripheral role";
+ return;
+ }
if (d->state != QLowEnergyController::ConnectedState)
return;
@@ -619,7 +713,8 @@ void QLowEnergyController::discoverServices()
}
/*!
- Returns the list of services offered by the remote device.
+ Returns the list of services offered by the remote device, if the controller is in
+ the \l CentralRole. Otherwise, the result is unspecified.
The list contains all primary and secondary services.
@@ -672,6 +767,49 @@ QLowEnergyService *QLowEnergyController::createServiceObject(
}
/*!
+ Starts advertising the data given in \a advertisingData and \a scanResponseData, using
+ the parameters set in \a parameters. The controller has to be in the \l PeripheralRole.
+ \omit
+ If \a parameters indicates that the advertisement should be connectable, then this call
+ also starts listening for incoming client connections.
+ \endomit
+
+ Providing \a scanResponseData is not required, as it is not applicable for certain
+ configurations of \c parameters.
+
+ If this object is currently not in the \l UnconnectedState, nothing happens.
+ \note Advertising will stop automatically once a client connects to the local device.
+ */
+void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters &parameters,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData)
+{
+ Q_D(QLowEnergyController);
+ if (role() != PeripheralRole) {
+ qCWarning(QT_BT) << "Cannot start advertising in central role" << state();
+ return;
+ }
+ if (state() != UnconnectedState) {
+ qCWarning(QT_BT) << "Cannot start advertising in state" << state();
+ return;
+ }
+ d->startAdvertising(parameters, advertisingData, scanResponseData);
+}
+
+/*!
+ Stops advertising, if this object is currently in the advertising state.
+ */
+void QLowEnergyController::stopAdvertising()
+{
+ Q_D(QLowEnergyController);
+ if (state() != AdvertisingState) {
+ qCDebug(QT_BT) << "stopAdvertising called in state" << state();
+ return;
+ }
+ d->stopAdvertising();
+}
+
+/*!
Returns the last occurred error or \l NoError.
*/
QLowEnergyController::Error QLowEnergyController::error() const
@@ -688,4 +826,12 @@ QString QLowEnergyController::errorString() const
return d_ptr->errorString;
}
+/*!
+ Returns the role that this controller object is in.
+ */
+QLowEnergyController::Role QLowEnergyController::role() const
+{
+ return d_ptr->role;
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller.h b/src/bluetooth/qlowenergycontroller.h
index be729dda..66928da8 100644
--- a/src/bluetooth/qlowenergycontroller.h
+++ b/src/bluetooth/qlowenergycontroller.h
@@ -38,11 +38,14 @@
#include <QtBluetooth/QBluetoothAddress>
#include <QtBluetooth/QBluetoothDeviceInfo>
#include <QtBluetooth/QBluetoothUuid>
+#include <QtBluetooth/QLowEnergyAdvertisingData>
#include <QtBluetooth/QLowEnergyService>
QT_BEGIN_NAMESPACE
+class QLowEnergyAdvertisingParameters;
class QLowEnergyControllerPrivate;
+
class Q_BLUETOOTH_EXPORT QLowEnergyController : public QObject
{
Q_OBJECT
@@ -53,7 +56,8 @@ public:
UnknownRemoteDeviceError,
NetworkError,
InvalidBluetoothAdapterError,
- ConnectionError
+ ConnectionError,
+ AdvertisingError,
};
Q_ENUM(Error)
@@ -63,7 +67,8 @@ public:
ConnectedState,
DiscoveringState,
DiscoveredState,
- ClosingState
+ ClosingState,
+ AdvertisingState,
};
Q_ENUM(ControllerState)
@@ -73,6 +78,9 @@ public:
};
Q_ENUM(RemoteAddressType)
+ enum Role { CentralRole, PeripheralRole };
+ Q_ENUM(Role)
+
explicit QLowEnergyController(const QBluetoothAddress &remoteDevice,
QObject *parent = 0); // TODO Qt 6 remove ctor
explicit QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice,
@@ -80,6 +88,11 @@ public:
explicit QLowEnergyController(const QBluetoothAddress &remoteDevice,
const QBluetoothAddress &localDevice,
QObject *parent = 0); // TODO Qt 6 remove ctor
+
+ static QLowEnergyController *createCentral(const QBluetoothDeviceInfo &remoteDevice,
+ QObject *parent = 0);
+ static QLowEnergyController *createPeripheral(QObject *parent = 0);
+
~QLowEnergyController();
QBluetoothAddress localAddress() const;
@@ -100,9 +113,16 @@ public:
QLowEnergyService *createServiceObject(
const QBluetoothUuid &service, QObject *parent = 0);
+ void startAdvertising(const QLowEnergyAdvertisingParameters &parameters,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData = QLowEnergyAdvertisingData());
+ void stopAdvertising();
+
Error error() const;
QString errorString() const;
+ Role role() const;
+
Q_SIGNALS:
void connected();
void disconnected();
@@ -113,6 +133,8 @@ Q_SIGNALS:
void discoveryFinished();
private:
+ explicit QLowEnergyController(QObject *parent = 0); // For the peripheral role.
+
Q_DECLARE_PRIVATE(QLowEnergyController)
QLowEnergyControllerPrivate *d_ptr;
};
@@ -122,5 +144,6 @@ QT_END_NAMESPACE
Q_DECLARE_METATYPE(QLowEnergyController::Error)
Q_DECLARE_METATYPE(QLowEnergyController::ControllerState)
Q_DECLARE_METATYPE(QLowEnergyController::RemoteAddressType)
+Q_DECLARE_METATYPE(QLowEnergyController::Role)
#endif // QLOWENERGYCONTROLLER_H
diff --git a/src/bluetooth/qlowenergycontroller_android.cpp b/src/bluetooth/qlowenergycontroller_android.cpp
index b04ddedd..437eb9a4 100644
--- a/src/bluetooth/qlowenergycontroller_android.cpp
+++ b/src/bluetooth/qlowenergycontroller_android.cpp
@@ -580,4 +580,19 @@ void QLowEnergyControllerPrivate::serviceError(
service->setError(errorCode);
}
+void QLowEnergyControllerPrivate::startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData)
+{
+ Q_UNUSED(params);
+ Q_UNUSED(advertisingData);
+ Q_UNUSED(scanResponseData);
+ qCWarning(QT_BT_ANDROID) << "LE advertising not implemented for Android";
+}
+
+void QLowEnergyControllerPrivate::stopAdvertising()
+{
+ qCWarning(QT_BT_ANDROID) << "LE advertising not implemented for Android";
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp
index 18d0f5a1..45d392d8 100644
--- a/src/bluetooth/qlowenergycontroller_bluez.cpp
+++ b/src/bluetooth/qlowenergycontroller_bluez.cpp
@@ -34,6 +34,7 @@
#include "qlowenergycontroller_p.h"
#include "qbluetoothsocket_p.h"
+#include "qleadvertiser_p.h"
#include "bluez/bluez_data_p.h"
#include "bluez/hcimanager_p.h"
@@ -203,7 +204,8 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
mtuSize(ATT_DEFAULT_LE_MTU),
securityLevelValue(-1),
encryptionChangePending(false),
- hciManager(0)
+ hciManager(0),
+ advertiser(0)
{
qRegisterMetaType<QList<QLowEnergyHandle> >();
@@ -219,6 +221,26 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
{
}
+void QLowEnergyControllerPrivate::startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData)
+{
+ qCDebug(QT_BT_BLUEZ) << "Starting to advertise";
+ if (!advertiser) {
+ advertiser = new QLeAdvertiserBluez(params, advertisingData, scanResponseData, *hciManager,
+ this);
+ connect(advertiser, &QLeAdvertiser::errorOccurred, this,
+ &QLowEnergyControllerPrivate::handleAdvertisingError);
+ }
+ setState(QLowEnergyController::AdvertisingState);
+ advertiser->startAdvertising();
+}
+
+void QLowEnergyControllerPrivate::stopAdvertising()
+{
+ setState(QLowEnergyController::UnconnectedState);
+ advertiser->stopAdvertising();
+}
void QLowEnergyControllerPrivate::connectToDevice()
{
@@ -1743,4 +1765,11 @@ bool QLowEnergyControllerPrivate::increaseEncryptLevelfRequired(quint8 errorCode
return false;
}
+void QLowEnergyControllerPrivate::handleAdvertisingError()
+{
+ qCWarning(QT_BT_BLUEZ) << "received advertising error";
+ setError(QLowEnergyController::AdvertisingError);
+ setState(QLowEnergyController::UnconnectedState);
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index d0d65248..dca245a8 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -817,6 +817,7 @@ QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddres
{
OSX_D_PTR;
+ osx_d_ptr->role = CentralRole;
osx_d_ptr->remoteAddress = remoteAddress;
osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
@@ -831,6 +832,7 @@ QLowEnergyController::QLowEnergyController(const QBluetoothDeviceInfo &remoteDev
{
OSX_D_PTR;
+ osx_d_ptr->role = CentralRole;
osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
// That's the only "real" ctor - with Core Bluetooth we need a _valid_ deviceUuid
// from 'remoteDevice'.
@@ -844,6 +846,7 @@ QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddres
{
OSX_D_PTR;
+ osx_d_ptr->role = CentralRole;
osx_d_ptr->remoteAddress = remoteAddress;
osx_d_ptr->localAddress = localAddress;
@@ -851,12 +854,39 @@ QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddres
"addresses is not supported!";
}
+QLowEnergyController::QLowEnergyController(QObject *parent)
+ : QObject(parent), d_ptr(new QLowEnergyControllerPrivateOSX(this))
+{
+ OSX_D_PTR;
+
+ osx_d_ptr->role = PeripheralRole;
+ osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
+}
+
+QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice,
+ QObject *parent)
+{
+ return new QLowEnergyController(remoteDevice, parent);
+}
+
+QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent)
+{
+ return new QLowEnergyController(parent);
+}
+
QLowEnergyController::~QLowEnergyController()
{
// Deleting a peripheral will also disconnect.
delete d_ptr;
}
+QLowEnergyController::Role QLowEnergyController::role() const
+{
+ OSX_D_PTR;
+
+ return osx_d_ptr->role;
+}
+
QBluetoothAddress QLowEnergyController::localAddress() const
{
OSX_D_PTR;
@@ -993,4 +1023,19 @@ QString QLowEnergyController::errorString() const
return osx_d_ptr->errorString;
}
+void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData)
+{
+ Q_UNUSED(params);
+ Q_UNUSED(advertisingData);
+ Q_UNUSED(scanResponseData);
+ qCWarning(QT_BT_OSX) << "LE advertising not implemented for OS X";
+}
+
+void QLowEnergyController::stopAdvertising()
+{
+ qCWarning(QT_BT_OSX) << "LE advertising not implemented for OS X";
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_osx_p.h
index 1c1cb1cd..6f6619db 100644
--- a/src/bluetooth/qlowenergycontroller_osx_p.h
+++ b/src/bluetooth/qlowenergycontroller_osx_p.h
@@ -156,6 +156,8 @@ private:
QBluetoothAddress localAddress;
QBluetoothAddress remoteAddress;
+ QLowEnergyController::Role role;
+
QLowEnergyController::ControllerState controllerState;
QLowEnergyController::RemoteAddressType addressType;
diff --git a/src/bluetooth/qlowenergycontroller_p.cpp b/src/bluetooth/qlowenergycontroller_p.cpp
index b3c718b5..4ee1e50f 100644
--- a/src/bluetooth/qlowenergycontroller_p.cpp
+++ b/src/bluetooth/qlowenergycontroller_p.cpp
@@ -104,4 +104,14 @@ void QLowEnergyControllerPrivate::writeDescriptor(
}
+void QLowEnergyControllerPrivate::startAdvertising(const QLowEnergyAdvertisingParameters &/* params */,
+ const QLowEnergyAdvertisingData &/* advertisingData */,
+ const QLowEnergyAdvertisingData &/* scanResponseData */)
+{
+}
+
+void QLowEnergyControllerPrivate::stopAdvertising()
+{
+}
+
QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h
index f587d3f9..722de692 100644
--- a/src/bluetooth/qlowenergycontroller_p.h
+++ b/src/bluetooth/qlowenergycontroller_p.h
@@ -85,6 +85,7 @@ class LowEnergyNotificationHub;
#endif
typedef QMap<QBluetoothUuid, QSharedPointer<QLowEnergyServicePrivate> > ServiceDataMap;
+class QLeAdvertiser;
class QLowEnergyControllerPrivate : public QObject
{
@@ -107,6 +108,11 @@ public:
void discoverServiceDetails(const QBluetoothUuid &service);
+ void startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData);
+ void stopAdvertising();
+
// misc helpers
QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(
QLowEnergyHandle handle);
@@ -142,6 +148,7 @@ public:
QBluetoothAddress remoteDevice;
QBluetoothAddress localAdapter;
+ QLowEnergyController::Role role;
QString remoteName;
@@ -172,6 +179,7 @@ private:
bool encryptionChangePending;
HciManager *hciManager;
+ QLeAdvertiser *advertiser;
void sendCommand(const QByteArray &packet);
void sendNextPendingRequest();
@@ -204,6 +212,7 @@ private:
void resetController();
+ void handleAdvertisingError();
private slots:
void l2cpConnected();
void l2cpDisconnected();