summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Blasche <alexander.blasche@digia.com>2014-08-14 09:57:07 +0200
committerAlex Blasche <alexander.blasche@digia.com>2014-08-14 09:58:35 +0200
commit67d8044bcc330d9aa139ed3026b6b0603a7a990a (patch)
treeeba72249447d6e2a3f28ed5b2cbfb351a0bc2053
parent687c885b8625374433f4ccc8b6442ea72ea62d46 (diff)
parent05dc94529b1a8f7c65c8aebbf8081d1d1f3aa96a (diff)
Merge remote-tracking branch 'gerrit/btle' into 5.4
-rw-r--r--.gitignore9
-rw-r--r--config.tests/bluez_le/bluez_le.pro8
-rw-r--r--config.tests/bluez_le/main.cpp52
-rw-r--r--examples/bluetooth/bluetooth.pro6
-rw-r--r--examples/bluetooth/btchat/chatserver.cpp2
-rw-r--r--examples/bluetooth/btfiletransfer/btfiletransfer.pro2
-rw-r--r--examples/bluetooth/btscanner/service.cpp12
-rw-r--r--examples/bluetooth/btscanner/service.h3
-rw-r--r--examples/bluetooth/heartlistener/assets/Button.qml77
-rw-r--r--examples/bluetooth/heartlistener/assets/Point.qml51
-rw-r--r--examples/bluetooth/heartlistener/assets/blue_heart.pngbin0 -> 7283 bytes
-rw-r--r--examples/bluetooth/heartlistener/assets/blue_heart_small.pngbin0 -> 250 bytes
-rw-r--r--examples/bluetooth/heartlistener/assets/busy_dark.pngbin0 -> 1130 bytes
-rw-r--r--examples/bluetooth/heartlistener/assets/dialog.qml61
-rw-r--r--examples/bluetooth/heartlistener/assets/draw.js74
-rw-r--r--examples/bluetooth/heartlistener/assets/home.qml186
-rw-r--r--examples/bluetooth/heartlistener/assets/main.qml84
-rw-r--r--examples/bluetooth/heartlistener/assets/monitor.qml154
-rw-r--r--examples/bluetooth/heartlistener/assets/results.qml291
-rw-r--r--examples/bluetooth/heartlistener/assets/star.pngbin0 -> 262 bytes
-rw-r--r--examples/bluetooth/heartlistener/deviceinfo.cpp58
-rw-r--r--examples/bluetooth/heartlistener/deviceinfo.h69
-rw-r--r--examples/bluetooth/heartlistener/doc/images/hearratemonitor.pngbin0 -> 46437 bytes
-rw-r--r--examples/bluetooth/heartlistener/doc/images/hearrateresults.pngbin0 -> 38983 bytes
-rw-r--r--examples/bluetooth/heartlistener/doc/images/hearrateresults1.pngbin0 -> 42472 bytes
-rw-r--r--examples/bluetooth/heartlistener/doc/images/heartrateintro.pngbin0 -> 21538 bytes
-rw-r--r--examples/bluetooth/heartlistener/doc/src/heartlistener.qdoc82
-rw-r--r--examples/bluetooth/heartlistener/heartlistener.pro20
-rw-r--r--examples/bluetooth/heartlistener/heartrate.cpp424
-rw-r--r--examples/bluetooth/heartlistener/heartrate.h149
-rw-r--r--examples/bluetooth/heartlistener/main.cpp61
-rw-r--r--examples/bluetooth/heartlistener/resources.qrc16
-rw-r--r--examples/bluetooth/lowenergyscanner/assets/Characteristics.qml147
-rw-r--r--examples/bluetooth/lowenergyscanner/assets/Dialog.qml61
-rw-r--r--examples/bluetooth/lowenergyscanner/assets/Header.qml61
-rw-r--r--examples/bluetooth/lowenergyscanner/assets/Label.qml53
-rw-r--r--examples/bluetooth/lowenergyscanner/assets/Menu.qml90
-rw-r--r--examples/bluetooth/lowenergyscanner/assets/Services.qml136
-rw-r--r--examples/bluetooth/lowenergyscanner/assets/main.qml130
-rw-r--r--examples/bluetooth/lowenergyscanner/characteristicinfo.cpp145
-rw-r--r--examples/bluetooth/lowenergyscanner/characteristicinfo.h75
-rw-r--r--examples/bluetooth/lowenergyscanner/device.cpp290
-rw-r--r--examples/bluetooth/lowenergyscanner/device.h122
-rw-r--r--examples/bluetooth/lowenergyscanner/deviceinfo.cpp72
-rw-r--r--examples/bluetooth/lowenergyscanner/deviceinfo.h71
-rw-r--r--examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example.pngbin0 -> 31581 bytes
-rw-r--r--examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example1.pngbin0 -> 44107 bytes
-rw-r--r--examples/bluetooth/lowenergyscanner/doc/src/lowenergyscanner.qdoc40
-rw-r--r--examples/bluetooth/lowenergyscanner/lowenergyscanner.pro25
-rw-r--r--examples/bluetooth/lowenergyscanner/main.cpp62
-rw-r--r--examples/bluetooth/lowenergyscanner/resources.qrc11
-rw-r--r--examples/bluetooth/lowenergyscanner/serviceinfo.cpp102
-rw-r--r--examples/bluetooth/lowenergyscanner/serviceinfo.h67
-rw-r--r--qtconnectivity.pro1
-rw-r--r--src/bluetooth/bluetooth.pro49
-rw-r--r--src/bluetooth/bluez/bluez_data_p.h86
-rw-r--r--src/bluetooth/doc/src/bluetooth-index.qdoc2
-rw-r--r--src/bluetooth/doc/src/bluetooth-overview.qdoc68
-rw-r--r--src/bluetooth/doc/src/examples.qdoc7
-rw-r--r--src/bluetooth/gatoattclient.cpp617
-rw-r--r--src/bluetooth/gatoattclient.h143
-rw-r--r--src/bluetooth/gatoperipheral.cpp874
-rw-r--r--src/bluetooth/gatoperipheral.h119
-rw-r--r--src/bluetooth/gatoperipheral_p.h116
-rw-r--r--src/bluetooth/qbluetooth.cpp7
-rw-r--r--src/bluetooth/qbluetooth.h2
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent.h1
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp104
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_p.h5
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_qnx.cpp23
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo.cpp42
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo.h10
-rw-r--r--src/bluetooth/qbluetoothdeviceinfo_p.h1
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.cpp7
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent.h2
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp125
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_p.h14
-rw-r--r--src/bluetooth/qbluetoothservicediscoveryagent_qnx.cpp96
-rw-r--r--src/bluetooth/qbluetoothsocket.h1
-rw-r--r--src/bluetooth/qbluetoothsocket_bluez.cpp18
-rw-r--r--src/bluetooth/qbluetoothsocket_p.h5
-rw-r--r--src/bluetooth/qbluetoothuuid.cpp546
-rw-r--r--src/bluetooth/qbluetoothuuid.h125
-rw-r--r--src/bluetooth/qlowenergycharacteristic.cpp325
-rw-r--r--src/bluetooth/qlowenergycharacteristic.h111
-rw-r--r--src/bluetooth/qlowenergycontroller.cpp360
-rw-r--r--src/bluetooth/qlowenergycontroller.h112
-rw-r--r--src/bluetooth/qlowenergycontroller_bluez.cpp1192
-rw-r--r--src/bluetooth/qlowenergycontroller_p.cpp94
-rw-r--r--src/bluetooth/qlowenergycontroller_p.h166
-rw-r--r--src/bluetooth/qlowenergydescriptor.cpp293
-rw-r--r--src/bluetooth/qlowenergydescriptor.h93
-rw-r--r--src/bluetooth/qlowenergyservice.cpp322
-rw-r--r--src/bluetooth/qlowenergyservice.h121
-rw-r--r--src/bluetooth/qlowenergyserviceinfo.cpp180
-rw-r--r--src/bluetooth/qlowenergyserviceinfo.h81
-rw-r--r--src/bluetooth/qlowenergyserviceinfo_p.h58
-rw-r--r--src/bluetooth/qlowenergyserviceprivate.cpp80
-rw-r--r--src/bluetooth/qlowenergyserviceprivate_p.h113
-rw-r--r--src/nfc/qnx/qnxnfcmanager.cpp1
-rw-r--r--sync.profile3
-rw-r--r--tests/auto/auto.pro6
-rw-r--r--tests/auto/qbluetoothdeviceinfo/qbluetoothdeviceinfo.pro3
-rw-r--r--tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp87
-rw-r--r--tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro3
-rw-r--r--tests/auto/qbluetoothservicediscoveryagent/tst_qbluetoothservicediscoveryagent.cpp33
-rw-r--r--tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro9
-rw-r--r--tests/auto/qlowenergycharacteristic/tst_qlowenergycharacteristic.cpp358
-rw-r--r--tests/auto/qlowenergycontroller/qlowenergycontroller.pro6
-rw-r--r--tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp1807
-rw-r--r--tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro8
-rw-r--r--tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp326
-rw-r--r--tests/auto/qlowenergyserviceinfo/qlowenergyserviceinfo.pro9
-rw-r--r--tests/auto/qlowenergyserviceinfo/tst_qlowenergyserviceinfo.cpp251
-rw-r--r--tests/bttestui/btlocaldevice.cpp16
-rw-r--r--tests/bttestui/btlocaldevice.h1
116 files changed, 13387 insertions, 168 deletions
diff --git a/.gitignore b/.gitignore
index 691e20fa..344041e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,10 +24,13 @@ tmp
imports/*
include
config.tests/bluez/bluez
+config.tests/bluez_le/bluez_le
examples/bluetooth/btchat/btchat
examples/bluetooth/btfiletransfer/btfiletransfer
examples/bluetooth/btscanner/btscanner
-examples/bluetooth/bttennis/bttennis
+examples/bluetooth/heartlistener/heartlistener
+examples/bluetooth/lowenergyscanner/lowenergyscanner
+examples/bluetooth/pingpong/pingpong
examples/bluetooth/picturetransfer/qml_picturetransfer
examples/bluetooth/scanner/qml_scanner
examples/nfc/annotatedurl/annotatedurl
@@ -49,6 +52,10 @@ tests/auto/qbluetoothsocket/tst_qbluetoothsocket
tests/auto/qbluetoothtransfermanager/tst_qbluetoothtransfermanager
tests/auto/qbluetoothtransferrequest/tst_qbluetoothtransferrequest
tests/auto/qbluetoothuuid/tst_qbluetoothuuid
+tests/auto/qlowenergycharacteristic/tst_qlowenergycharacteristic
+tests/auto/qlowenergycontroller/tst_qlowenergycontroller
+tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor
+tests/auto/qlowenergyserviceinfo/tst_qlowenergyserviceinfo
tests/auto/qndefmessage/tst_qndefmessage
tests/auto/qndefnfcsmartposterrecord/tst_qndefnfcsmartposterrecord
tests/auto/qndefrecord/tst_qndefrecord
diff --git a/config.tests/bluez_le/bluez_le.pro b/config.tests/bluez_le/bluez_le.pro
new file mode 100644
index 00000000..27b19a45
--- /dev/null
+++ b/config.tests/bluez_le/bluez_le.pro
@@ -0,0 +1,8 @@
+TEMPLATE = app
+
+CONFIG += link_pkgconfig
+PKGCONFIG += bluez
+
+TARGET = bluez_le
+
+SOURCES += main.cpp
diff --git a/config.tests/bluez_le/main.cpp b/config.tests/bluez_le/main.cpp
new file mode 100644
index 00000000..ae23f6a6
--- /dev/null
+++ b/config.tests/bluez_le/main.cpp
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtConnectivity module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+
+int main()
+{
+ sockaddr_l2 addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ addr.l2_cid = 4;
+ addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
+}
diff --git a/examples/bluetooth/bluetooth.pro b/examples/bluetooth/bluetooth.pro
index a0379f6c..4576956f 100644
--- a/examples/bluetooth/bluetooth.pro
+++ b/examples/bluetooth/bluetooth.pro
@@ -5,4 +5,8 @@ qtHaveModule(widgets) {
btfiletransfer
}
-qtHaveModule(quick): SUBDIRS += scanner picturetransfer pingpong
+qtHaveModule(quick): SUBDIRS += scanner \
+ picturetransfer \
+ pingpong \
+ lowenergyscanner \
+ heartlistener
diff --git a/examples/bluetooth/btchat/chatserver.cpp b/examples/bluetooth/btchat/chatserver.cpp
index 0b29b87b..6da4b471 100644
--- a/examples/bluetooth/btchat/chatserver.cpp
+++ b/examples/bluetooth/btchat/chatserver.cpp
@@ -83,7 +83,9 @@ void ChatServer::startServer(const QBluetoothAddress& localAdapter)
classId);
classId.prepend(QVariant::fromValue(QBluetoothUuid(serviceUuid)));
+
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
+ serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,classId);
//! [Class Uuuid must contain at least 1 entry]
diff --git a/examples/bluetooth/btfiletransfer/btfiletransfer.pro b/examples/bluetooth/btfiletransfer/btfiletransfer.pro
index 00b16415..e5d47302 100644
--- a/examples/bluetooth/btfiletransfer/btfiletransfer.pro
+++ b/examples/bluetooth/btfiletransfer/btfiletransfer.pro
@@ -26,5 +26,5 @@ OTHER_FILES += \
RESOURCES += \
btfiletransfer.qrc
-target.path = $$[QT_INSTALL_EXAMPLES]/bluetooth/scanner
+target.path = $$[QT_INSTALL_EXAMPLES]/bluetooth/btfiletransfer
INSTALLS += target
diff --git a/examples/bluetooth/btscanner/service.cpp b/examples/bluetooth/btscanner/service.cpp
index d9172d69..d24837eb 100644
--- a/examples/bluetooth/btscanner/service.cpp
+++ b/examples/bluetooth/btscanner/service.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -44,6 +45,8 @@
#include <qbluetoothservicediscoveryagent.h>
#include <qbluetoothserviceinfo.h>
#include <qbluetoothlocaldevice.h>
+#include <qlowenergyserviceinfo.h>
+#include <qbluetoothuuid.h>
ServiceDiscoveryDialog::ServiceDiscoveryDialog(const QString &name,
@@ -74,6 +77,8 @@ ServiceDiscoveryDialog::ServiceDiscoveryDialog(const QString &name,
connect(discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)),
this, SLOT(addService(QBluetoothServiceInfo)));
connect(discoveryAgent, SIGNAL(finished()), ui->status, SLOT(hide()));
+ connect(discoveryAgent, SIGNAL(serviceDiscovered(const QLowEnergyServiceInfo&)),
+ this, SLOT(addLowEnergyService(const QLowEnergyServiceInfo&)));
discoveryAgent->start();
}
@@ -98,3 +103,10 @@ void ServiceDiscoveryDialog::addService(const QBluetoothServiceInfo &info)
ui->list->addItem(line);
}
+void ServiceDiscoveryDialog::addLowEnergyService(const QLowEnergyServiceInfo &gatt)
+{
+ QString line = gatt.serviceName();
+
+ ui->list->addItem(line);
+}
+
diff --git a/examples/bluetooth/btscanner/service.h b/examples/bluetooth/btscanner/service.h
index 293bc7a9..2089eb87 100644
--- a/examples/bluetooth/btscanner/service.h
+++ b/examples/bluetooth/btscanner/service.h
@@ -49,6 +49,8 @@
QT_FORWARD_DECLARE_CLASS(QBluetoothAddress)
QT_FORWARD_DECLARE_CLASS(QBluetoothServiceInfo)
QT_FORWARD_DECLARE_CLASS(QBluetoothServiceDiscoveryAgent)
+QT_FORWARD_DECLARE_CLASS (QLowEnergyServiceInfo)
+QT_FORWARD_DECLARE_CLASS (QLowEnergyCharacteristicInfo)
QT_USE_NAMESPACE
@@ -62,6 +64,7 @@ public:
public slots:
void addService(const QBluetoothServiceInfo&);
+ void addLowEnergyService(const QLowEnergyServiceInfo&);
private:
QBluetoothServiceDiscoveryAgent *discoveryAgent;
diff --git a/examples/bluetooth/heartlistener/assets/Button.qml b/examples/bluetooth/heartlistener/assets/Button.qml
new file mode 100644
index 00000000..40e98875
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/Button.qml
@@ -0,0 +1,77 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ id:button
+ //color: "#3870BA"
+
+ property real buttonWidth: 300
+ property real buttonHeight: 80
+ property string text: "Button"
+
+ signal buttonClick()
+ width: click.pressed ? (buttonWidth - 15) : buttonWidth
+ height: click.pressed ? (buttonHeight - 15) :buttonHeight
+
+ color: click.pressed ? "#3265A7" : "#3870BA"
+
+ border.color: "#F0EBED"
+ border.width: 5
+ radius: 10
+
+ Text {
+ id: label
+ font.pixelSize: 30; font.bold: true
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ anchors.fill: parent
+ elide: Text.ElideMiddle
+ color: "#F0EBED"
+ text: button.text
+ }
+
+ MouseArea {
+ id: click
+ anchors.fill: parent
+ onClicked: buttonClick()
+ }
+}
diff --git a/examples/bluetooth/heartlistener/assets/Point.qml b/examples/bluetooth/heartlistener/assets/Point.qml
new file mode 100644
index 00000000..2ed94a4d
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/Point.qml
@@ -0,0 +1,51 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ id: point
+
+ Image {
+ width: 10; height: 7
+ smooth: true
+ source: "blue_heart_small.png"
+ }
+}
diff --git a/examples/bluetooth/heartlistener/assets/blue_heart.png b/examples/bluetooth/heartlistener/assets/blue_heart.png
new file mode 100644
index 00000000..997ee699
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/blue_heart.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/assets/blue_heart_small.png b/examples/bluetooth/heartlistener/assets/blue_heart_small.png
new file mode 100644
index 00000000..7bd1f981
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/blue_heart_small.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/assets/busy_dark.png b/examples/bluetooth/heartlistener/assets/busy_dark.png
new file mode 100644
index 00000000..3a105953
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/busy_dark.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/assets/dialog.qml b/examples/bluetooth/heartlistener/assets/dialog.qml
new file mode 100644
index 00000000..45b64cf3
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/dialog.qml
@@ -0,0 +1,61 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ opacity: 0.8
+ color: "#E0DEDF"
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ property int hr: heartRate.hr
+ Text {
+ text: heartRate.message
+ color: "#3870BA"
+
+ }
+
+ onHrChanged: {
+ if (heartRate.hr > 0) {
+ root.destroy()
+ }
+ }
+}
diff --git a/examples/bluetooth/heartlistener/assets/draw.js b/examples/bluetooth/heartlistener/assets/draw.js
new file mode 100644
index 00000000..a5de5474
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/draw.js
@@ -0,0 +1,74 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+var component;
+var size = 0;
+var counter = 0;
+var difference = 0;
+
+function start() {
+ size = heartRate.measurementsSize();
+ difference = (plot.width-topbar.width)/size;
+ console.log(size +" "+ plot.width);
+ for (var i = 0; i< size; i++) {
+ var value = heartRate.measurements(i);
+
+ drawIt(value);
+ counter++;
+ }
+}
+
+function drawIt(value) {
+ if (component == null)
+ component = Qt.createComponent("Point.qml");
+ if (component.status == Component.Ready) {
+ var dynamicObject = component.createObject(plot);
+ if (dynamicObject == null) {
+ console.log("error creating block");
+ console.log(component.errorString());
+ return false;
+ }
+ dynamicObject.x = 10+(counter*difference);
+ console.log(plot.height)
+ dynamicObject.y = plot.height -value;
+
+ }
+}
+
diff --git a/examples/bluetooth/heartlistener/assets/home.qml b/examples/bluetooth/heartlistener/assets/home.qml
new file mode 100644
index 00000000..2c5e9485
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/home.qml
@@ -0,0 +1,186 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ id: screen
+ color: "#F0EBED"
+ property string message: heartRate.message
+ onMessageChanged: {
+ if (heartRate.message != "Scanning for devices..." && heartRate.message != "Low Energy device found. Scanning for more...") {
+ background.visible = false;
+ demoMode.visible = true;
+ }
+ else {
+ demoMode.visible = false;
+ background.visible = true;
+ }
+ }
+
+ Rectangle {
+ id:select
+ width: parent.width
+ anchors.top: parent.top
+ height: 80
+ color: "#F0EBED"
+ border.color: "#3870BA"
+ border.width: 2
+ radius: 10
+
+ Text {
+ id: selectText
+ color: "#3870BA"
+ font.pixelSize: 34
+ anchors.centerIn: parent
+ text: "Select Device"
+ }
+ }
+
+ Rectangle {
+ id: spinner
+ width: parent.width
+ anchors.top: select.bottom
+ anchors.bottom: demoMode.top
+ visible: false
+ color: "#F0EBED"
+ z: 100
+
+ Rectangle {
+ id: inside
+ anchors.centerIn: parent
+ Image {
+ id: background
+
+ width:100
+ height:100
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ source: "busy_dark.png"
+ fillMode: Image.PreserveAspectFit
+ NumberAnimation on rotation { duration: 3000; from:0; to: 360; loops: Animation.Infinite}
+ }
+
+ Text {
+ id: infotext
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: background.bottom
+ text: heartRate.message
+ color: "#8F8F8F"
+ }
+ }
+ }
+
+ Component.onCompleted: {
+ heartRate.deviceSearch();
+ spinner.visible=true;
+ }
+
+ ListView {
+ id: theListView
+ width: parent.width
+ onModelChanged: spinner.visible=false
+ anchors.top: select.bottom
+ anchors.bottom: demoMode.top
+ model: heartRate.name
+
+ delegate: Rectangle {
+ id: box
+ height:140
+ width: parent.width
+ color: "#3870BA"
+ border.color: "#F0EBED"
+ border.width: 5
+ radius: 15
+
+ MouseArea {
+ anchors.fill: parent
+ onPressed: { box.color= "#3265A7"; box.height=110}
+ onClicked: {
+ heartRate.connectToService(modelData.deviceAddress);
+ pageLoader.source="monitor.qml";
+ }
+ }
+
+ Text {
+ id: device
+ font.pixelSize: 30
+ text: modelData.deviceName
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#F0EBED"
+ }
+
+ Text {
+ id: deviceAddress
+ font.pixelSize: 30
+ text: modelData.deviceAddress
+ anchors.bottom: parent.bottom
+ anchors.bottomMargin: 5
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#F0EBED"
+ }
+ }
+ }
+
+ Button {
+ id:demoMode
+ buttonWidth: parent.width
+ buttonHeight: 0.1*parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: scanAgain.top
+ text: "Run Demo"
+ onButtonClick: {
+ heartRate.startDemo();
+ pageLoader.source="monitor.qml";
+ }
+ }
+
+ Button {
+ id:scanAgain
+ buttonWidth: parent.width
+ buttonHeight: 0.1*parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ text: "Menu"
+ onButtonClick: pageLoader.source="main.qml"
+ }
+}
diff --git a/examples/bluetooth/heartlistener/assets/main.qml b/examples/bluetooth/heartlistener/assets/main.qml
new file mode 100644
index 00000000..e892eb47
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/main.qml
@@ -0,0 +1,84 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Item {
+ width: 400
+ height: 600
+ id: begin
+
+ Rectangle {
+ color: "#F0EBED"
+ anchors.fill: parent
+ Rectangle {
+ id: about
+ width: 0.75*parent.width
+ height: 0.1*parent.height
+ anchors.top: parent.top
+ anchors.topMargin: 20
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#F0EBED"
+ border.color: "#3870BA"
+ border.width: 2
+ radius: 10
+ Text {
+ id: aboutinfo
+ anchors.centerIn: parent
+ color: "#3870BA"
+ text: "Welcome to the Heart Listener Application"
+ }
+ }
+
+ Button {
+ id:call
+ buttonWidth: 0.75*parent.width
+ buttonHeight: 0.15*parent.height
+ anchors.centerIn: parent
+ text: "Scan for Devices"
+ onButtonClick: pageLoader.source="home.qml"
+ }
+ }
+
+ Loader {
+ id: pageLoader
+ anchors.fill: parent
+ }
+}
diff --git a/examples/bluetooth/heartlistener/assets/monitor.qml b/examples/bluetooth/heartlistener/assets/monitor.qml
new file mode 100644
index 00000000..2fcbef3d
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/monitor.qml
@@ -0,0 +1,154 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import QtQuick.Particles 2.0
+
+Rectangle {
+ id: screenMonitor
+ color: "#F0EBED"
+
+ Button {
+ id:menu
+ buttonWidth: parent.width
+ buttonHeight: 0.1 * parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+ text: "Menu"
+ onButtonClick: {
+ heartRate.disconnectService();
+ pageLoader.source="home.qml";
+ }
+ }
+
+ Text {
+ id: hrValue
+ font.pointSize: 24; font.bold: true
+ anchors.top:menu.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 25
+
+ color: "#3870BA"
+ text: heartRate.hr
+ onTextChanged: {
+ if (heartRate.hr > 0 && updatei != null && heartRate.numDevices() > 0) {
+ updatei.destroy()
+ }
+ }
+ }
+
+ Rectangle {
+ id: updatei
+ width: parent.width
+ height: 80
+ anchors.bottom: stop.top
+
+ color: "#F0EBED"
+ border.color: "#3870BA"
+ border.width: 2
+
+ Text {
+ id: logi
+ text: heartRate.message
+ anchors.centerIn: updatei
+ color: "#3870BA"
+ }
+ }
+
+ Image {
+ id: background
+ width: 300
+ height: width
+ anchors.centerIn: parent
+ source: "blue_heart.png"
+ fillMode: Image.PreserveAspectFit
+ NumberAnimation on width {
+ running: heartRate.hr > 0;
+ duration: heartRate.hr/60*250;
+ from:300; to: 350;
+ loops: Animation.Infinite;
+ }
+
+ ParticleSystem {
+ id: systwo
+ anchors.fill: parent
+
+ ImageParticle {
+ system: systwo
+ id: cptwo
+ source: "star.png"
+ colorVariation: 0.4
+ color: "#000000FF"
+ }
+
+ Emitter {
+ //burst on click
+ id: burstytwo
+ system: systwo
+ enabled: true
+ x: 160
+ y: 150
+ emitRate: heartRate.hr*100
+ maximumEmitted: 4000
+ acceleration: AngleDirection {angleVariation: 360; magnitude: 360; }
+ size: 4
+ endSize: 8
+ sizeVariation: 4
+ }
+
+
+ }
+
+ }
+
+ Button {
+ id:stop
+ buttonWidth: parent.width
+ buttonHeight: 0.1*parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ text: "Stop Monitoring"
+ onButtonClick: {
+ burstytwo.enabled = false;
+ heartRate.disconnectService();
+ pageLoader.source = "results.qml";
+ }
+ }
+}
diff --git a/examples/bluetooth/heartlistener/assets/results.qml b/examples/bluetooth/heartlistener/assets/results.qml
new file mode 100644
index 00000000..bdc7147d
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/results.qml
@@ -0,0 +1,291 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import "draw.js" as DrawGraph
+
+Rectangle {
+ id: results
+ color: "#F0EBED"
+
+ Component.onCompleted: heartRate.obtainResults()
+
+ function getTime() {
+ var t = heartRate.time;
+ var min = Math.floor(t/60);
+ var sec = t%60;
+ var r = min + " min " + sec + " sec ";
+ return r;
+ }
+
+ function drawGraph() {
+ var b = plot.height/200;
+ var ctx = canvas1.getContext('2d');
+ ctx.beginPath()
+ ctx.moveTo(10, plot.height- (b*60))
+ var size = heartRate.measurementsSize();
+ var difference = (plot.width-topbar.width)/size;
+
+ for (var i = 0; i< size; i++) {
+ var value = heartRate.measurements(i);
+ if (i == 0) {
+ ctx.moveTo(10+2, (plot.height- (value*b) + 2));
+ ctx.rect((10 + i*difference), (plot.height- (value*b)), 4, 4);
+
+ }
+ else {
+ ctx.lineTo((10+2 + i*difference), (plot.height- (value*b) + 2));
+ ctx.rect((10 + i*difference), (plot.height- (value*b)), 4, 4);
+ }
+
+ }
+ ctx.fillStyle = "#3870BA"
+ ctx.fill()
+ ctx.strokeStyle = "#3870BA"
+ ctx.stroke()
+ ctx.closePath()
+ }
+
+ Rectangle {
+ id: res
+ width: parent.width
+ anchors.top: parent.top
+ height: 80
+ color: "#F0EBED"
+ border.color: "#3870BA"
+ border.width: 2
+ radius: 10
+ Text {
+ id: restText
+ color: "#3870BA"
+ font.pixelSize: 34
+ anchors.centerIn: parent
+ text: "Results"
+ }
+ }
+
+ Text {
+ id: topbar
+ text: "200"
+ anchors.left: parent.left
+ anchors.top: res.bottom
+ anchors.rightMargin: 4
+ color: "#3870BA"
+ z: 50
+ }
+
+ Rectangle {
+ id: level
+ anchors.left: topbar.right
+
+ anchors.top: res.bottom
+ height: ((results.height -(res.height + menuLast.height + start.height ))/2)
+ width: 3
+ color: "#3870BA"
+ }
+
+ Text {
+ id: middlebar
+ anchors.verticalCenter: level.verticalCenter
+ anchors.left: parent.left
+ text: "100"
+ color: "#3870BA"
+ z: 50
+ }
+
+ Rectangle{
+ id: downlevel
+ anchors.bottom: cover.top
+ width: parent.width
+ anchors.left: level.right
+ height: 3
+ color: "#3870BA"
+ z: 50
+ }
+
+ Rectangle {
+ id: plot
+ anchors.left: level.right
+ anchors.leftMargin: 15
+ width: results.width
+ height: ((parent.height-(res.height+menuLast.height+start.height))/2)
+
+ anchors.top: res.bottom
+ color: "#F0EBED"
+ Canvas {
+ id: canvas1
+ anchors.fill: parent
+ z: 150
+ onPaint: drawGraph()
+ }
+ }
+
+ Rectangle {
+ id: cover
+ anchors.top: plot.bottom
+ anchors.bottom: menuLast.top
+ width: parent.width
+ height: ((parent.height-(res.height+menuLast.height+start.height))/2)
+ color: "#F0EBED"
+ radius: 10
+ border.color: "#3870BA"
+ border.width: 2
+
+ Flickable {
+ id: scroll
+ anchors.fill: parent
+ anchors.margins: 5
+ clip: true
+ contentWidth: parent.width
+ contentHeight: stresult.height
+
+ Rectangle {
+ id: stresult
+ width: parent.width
+ height: (results.height - (res.height + menuLast.height + start.height - 100))
+ color: "#F0EBED"
+ radius: 10
+
+ Text {
+ id: averageHR
+ font.pixelSize: 30;
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.top: parent.top
+
+ color: "#3870BA"
+ text: "Average Heart Rate"
+ }
+
+ Text {
+ id: averageHRt
+ font.pixelSize: 40; font.bold: true
+ anchors.top: averageHR.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 10
+ color: "#3870BA"
+ text: heartRate.average
+ }
+
+ Text {
+ id: time
+ font.pixelSize: 30;
+ anchors.top: averageHRt.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 10
+ color: "#3870BA"
+ text: "Seconds measured:"
+ }
+
+ Text {
+ id: timet
+ font.pixelSize: 40; font.bold: true
+ anchors.top: time.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 10
+ color: "#3870BA"
+ text: getTime()
+ }
+ Text {
+ id: maxi
+ font.pixelSize: 30;
+ anchors.top: timet.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 20
+ color: "#3870BA"
+ text: " Max || Min "
+ }
+
+ Text {
+ id: mini
+ font.pixelSize: 40; font.bold: true
+ anchors.top:maxi.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 10
+ color: "#3870BA"
+ text: " " + heartRate.maxHR + " || " + heartRate.minHR
+ }
+
+ Text {
+ id: calories
+ font.pixelSize: 30;
+ anchors.top: mini.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 10
+ color: "#3870BA"
+ text: " Calories "
+ }
+
+ Text {
+ id: caloriestext
+ font.pixelSize: 40; font.bold: true
+ anchors.top:calories.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.topMargin: 10
+ color: "#3870BA"
+ text: heartRate.calories.toFixed(3)
+ }
+ }
+ }
+ }
+
+ Button {
+ id:menuLast
+ buttonWidth: parent.width
+ buttonHeight: 0.1*parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: start.top
+ text: "Menu"
+ onButtonClick: { pageLoader.source="main.qml"}
+ }
+
+ Button {
+ id:start
+ buttonWidth: parent.width
+ buttonHeight: 0.1*parent.height
+ anchors.horizontalCenter: parent.horizontalCenter
+ anchors.bottom: parent.bottom
+ text: "Start Monitoring"
+ onButtonClick: {
+ heartRate.connectToService(heartRate.deviceAddress());
+ pageLoader.source="monitor.qml";
+ }
+ }
+}
diff --git a/examples/bluetooth/heartlistener/assets/star.png b/examples/bluetooth/heartlistener/assets/star.png
new file mode 100644
index 00000000..defbde53
--- /dev/null
+++ b/examples/bluetooth/heartlistener/assets/star.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/deviceinfo.cpp b/examples/bluetooth/heartlistener/deviceinfo.cpp
new file mode 100644
index 00000000..5be9fcfe
--- /dev/null
+++ b/examples/bluetooth/heartlistener/deviceinfo.cpp
@@ -0,0 +1,58 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "deviceinfo.h"
+
+DeviceInfo::DeviceInfo(const QBluetoothDeviceInfo &info):
+ QObject(), m_device(info)
+{
+}
+
+QBluetoothDeviceInfo DeviceInfo::getDevice() const
+{
+ return m_device;
+}
+
+void DeviceInfo::setDevice(const QBluetoothDeviceInfo &device)
+{
+ m_device = device;
+ emit deviceChanged();
+}
diff --git a/examples/bluetooth/heartlistener/deviceinfo.h b/examples/bluetooth/heartlistener/deviceinfo.h
new file mode 100644
index 00000000..a80f723b
--- /dev/null
+++ b/examples/bluetooth/heartlistener/deviceinfo.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEVICEINFO_H
+#define DEVICEINFO_H
+
+#include <QString>
+#include <QObject>
+#include <qbluetoothdeviceinfo.h>
+#include <qbluetoothaddress.h>
+
+class DeviceInfo: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString deviceName READ getName NOTIFY deviceChanged)
+ Q_PROPERTY(QString deviceAddress READ getAddress NOTIFY deviceChanged)
+public:
+ DeviceInfo(const QBluetoothDeviceInfo &device);
+ void setDevice(const QBluetoothDeviceInfo &device);
+ QString getName() const { return m_device.name(); }
+ QString getAddress() const { return m_device.address().toString(); }
+ QBluetoothDeviceInfo getDevice() const;
+
+signals:
+ void deviceChanged();
+
+private:
+ QBluetoothDeviceInfo m_device;
+};
+
+#endif // DEVICEINFO_H
diff --git a/examples/bluetooth/heartlistener/doc/images/hearratemonitor.png b/examples/bluetooth/heartlistener/doc/images/hearratemonitor.png
new file mode 100644
index 00000000..ed51ba86
--- /dev/null
+++ b/examples/bluetooth/heartlistener/doc/images/hearratemonitor.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/images/hearrateresults.png b/examples/bluetooth/heartlistener/doc/images/hearrateresults.png
new file mode 100644
index 00000000..2c961517
--- /dev/null
+++ b/examples/bluetooth/heartlistener/doc/images/hearrateresults.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/images/hearrateresults1.png b/examples/bluetooth/heartlistener/doc/images/hearrateresults1.png
new file mode 100644
index 00000000..7af63b57
--- /dev/null
+++ b/examples/bluetooth/heartlistener/doc/images/hearrateresults1.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/images/heartrateintro.png b/examples/bluetooth/heartlistener/doc/images/heartrateintro.png
new file mode 100644
index 00000000..fb0a7b34
--- /dev/null
+++ b/examples/bluetooth/heartlistener/doc/images/heartrateintro.png
Binary files differ
diff --git a/examples/bluetooth/heartlistener/doc/src/heartlistener.qdoc b/examples/bluetooth/heartlistener/doc/src/heartlistener.qdoc
new file mode 100644
index 00000000..fa63714a
--- /dev/null
+++ b/examples/bluetooth/heartlistener/doc/src/heartlistener.qdoc
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example heartlistener
+ \title Bluetooth Low Energy Heart Listener example
+
+ The Bluetooth Low Energy (BLE) Heart Listener Example shows how to develop a Bluetooth
+ Low Energy applications using the Qt Bluetooth API. The application covers
+ the scanning for BLE devices, connecting to the BLE Heart Rate service and
+ receiving updates from the BLE heart belt. The BLE heart belt is required
+ for this application to work.
+
+ \image heartrateintro.png
+
+ The best and the safest approach is to do a service discovery for BLE device first and
+ then pick the heart rate service. Before that it is necessary to connect signals from
+ the QLowEnergyController class, which is responsible for the communication with the
+ BLE device.
+
+ \snippet heartlistener/heartrate.cpp Connect signals
+
+ After service scan is done and heart rate service found, the heart rate measurement
+ characteristic needs to be found and enabled for the notifications (advertisements).
+
+ \snippet heartlistener/heartrate.cpp Connecting to service
+
+ The enableNotifications(m_heartRateCharacteristic) method is the one that
+ will enable advertisement from the BLE device. Every time, when new update gets from
+ the BLE device (in our case heart rate belt) QLowEnergyController will emit
+ valueChanged(QLowEnergyCharacteristicInfo) method, which will invoke
+ receiveMeasurement(const QLowEnergyCharacteristicInfo &) slot.
+
+ \snippet heartlistener/heartrate.cpp Reading value
+
+ Every BLE device has its own structure of data that is advertising. In the code above,
+ the approach for checking the value structure is presented. The heart rate service contains
+ that information in the first 8 bits of the advertised value. For instance, first bit tells
+ the format of the value (is it 8 or 16 bit value), fourth tells does the heart belt provide
+ energy spenditure information, etc.
+
+ \snippet heartlistener/heartrate.cpp Error handling
+
+ In case an error occurs, QLowEnergyController will emit error, which can be read with
+ errorString() method. An error for BLE service can occur in the process of connecting to
+ the service. For BLE characteristics, an error can occur when trying to subscribe for the
+ notifications.
+
+ In the end, it is required to disconnect from the service.
+
+ \snippet heartlistener/heartrate.cpp Disconnecting from service
+
+ \image hearratemonitor.png
+ \image hearrateresults.png
+ \image hearrateresults1.png
+
+*/
+
diff --git a/examples/bluetooth/heartlistener/heartlistener.pro b/examples/bluetooth/heartlistener/heartlistener.pro
new file mode 100644
index 00000000..7856b64e
--- /dev/null
+++ b/examples/bluetooth/heartlistener/heartlistener.pro
@@ -0,0 +1,20 @@
+TEMPLATE = app
+TARGET = heartlistener
+
+QT += quick bluetooth
+
+# Input
+HEADERS += deviceinfo.h \
+ heartrate.h
+SOURCES += deviceinfo.cpp \
+ heartrate.cpp \
+ main.cpp
+
+OTHER_FILES += assets/*.qml \
+ assets/*.js
+
+RESOURCES += \
+ resources.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/bluetooth/heartlistener
+INSTALLS += target
diff --git a/examples/bluetooth/heartlistener/heartrate.cpp b/examples/bluetooth/heartlistener/heartrate.cpp
new file mode 100644
index 00000000..ac61a076
--- /dev/null
+++ b/examples/bluetooth/heartlistener/heartrate.cpp
@@ -0,0 +1,424 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "heartrate.h"
+
+HeartRate::HeartRate():
+ m_currentDevice(QBluetoothDeviceInfo()), foundHeartRateService(false),
+ m_max(0), m_min(0), calories(0), m_control(0), timer(0),
+ m_service(0)
+{
+ m_deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
+
+ connect(m_deviceDiscoveryAgent, SIGNAL(deviceDiscovered(const QBluetoothDeviceInfo&)),
+ this, SLOT(addDevice(const QBluetoothDeviceInfo&)));
+ connect(m_deviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
+ this, SLOT(deviceScanError(QBluetoothDeviceDiscoveryAgent::Error)));
+ connect(m_deviceDiscoveryAgent, SIGNAL(finished()), this, SLOT(scanFinished()));
+
+ // initialize random seed for demo mode
+ qsrand(QTime::currentTime().msec());
+}
+
+HeartRate::~HeartRate()
+{
+ qDeleteAll(m_devices);
+ m_devices.clear();
+}
+
+void HeartRate::deviceSearch()
+{
+ qDeleteAll(m_devices);
+ m_devices.clear();
+ m_deviceDiscoveryAgent->start();
+ setMessage("Scanning for devices...");
+}
+
+void HeartRate::addDevice(const QBluetoothDeviceInfo &device)
+{
+ if (device.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
+ qWarning() << "Discovered LE Device name: " << device.name() << " Address: "
+ << device.address().toString();
+
+ DeviceInfo *dev = new DeviceInfo(device);
+ m_devices.append(dev);
+ setMessage("Low Energy device found. Scanning for more...");
+ }
+}
+
+void HeartRate::scanFinished()
+{
+ if (m_devices.size() == 0)
+ setMessage("No Low Energy devices found");
+ Q_EMIT nameChanged();
+}
+
+void HeartRate::deviceScanError(QBluetoothDeviceDiscoveryAgent::Error error)
+{
+ if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError)
+ setMessage("The Bluetooth adaptor is powered off, power it on before doing discovery.");
+ else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError)
+ setMessage("Writing or reading from the device resulted in an error.");
+ else
+ setMessage("An unknown error has occurred.");
+}
+
+
+void HeartRate::setMessage(QString message)
+{
+ m_info = message;
+ Q_EMIT messageChanged();
+}
+
+QString HeartRate::message() const
+{
+ return m_info;
+}
+
+QVariant HeartRate::name()
+{
+ return QVariant::fromValue(m_devices);
+}
+
+void HeartRate::connectToService(const QString &address)
+{
+ m_measurements.clear();
+
+ bool deviceFound = false;
+ for (int i = 0; i < m_devices.size(); i++) {
+ if (((DeviceInfo*)m_devices.at(i))->getAddress() == address ) {
+ m_currentDevice.setDevice(((DeviceInfo*)m_devices.at(i))->getDevice());
+ setMessage("Connecting to device...");
+ deviceFound = true;
+ break;
+ }
+ }
+ // we are running demo mode
+ if (!deviceFound) {
+ startDemo();
+ return;
+ }
+
+ if (m_control) {
+ m_control->disconnectFromDevice();
+ delete m_control;
+ m_control = 0;
+
+ }
+ //! [Connect signals]
+ m_control = new QLowEnergyController(m_currentDevice.getDevice().address(),
+ this);
+ connect(m_control, SIGNAL(serviceDiscovered(QBluetoothUuid)),
+ this, SLOT(serviceDiscovered(QBluetoothUuid)));
+ connect(m_control, SIGNAL(discoveryFinished()),
+ this, SLOT(serviceScanDone()));
+ connect(m_control, SIGNAL(error(QLowEnergyController::Error)),
+ this, SLOT(controllerError(QLowEnergyController::Error)));
+ connect(m_control, SIGNAL(connected()),
+ this, SLOT(serviceConnected()));
+
+ m_control->connectToDevice();
+ //! [Connect signals]
+}
+
+// TODO fix the qdoc tag below
+//! [Connecting to service]
+
+void HeartRate::serviceConnected()
+{
+ m_control->discoverServices();
+}
+
+void HeartRate::deviceDisconnected()
+{
+ setMessage("Heart Rate service disconnected");
+ qWarning() << "Remote device disconnected";
+}
+
+
+void HeartRate::serviceDiscovered(const QBluetoothUuid &gatt)
+{
+ if (gatt == QBluetoothUuid(QBluetoothUuid::HeartRate)) {
+ setMessage("Heart Rate service discovered. Waiting for service scan to be done...");
+ foundHeartRateService = true;
+ }
+}
+
+void HeartRate::serviceScanDone()
+{
+ delete m_service;
+ m_service = 0;
+
+ if (foundHeartRateService) {
+ setMessage("Connecting to service...");
+ m_service = m_control->createServiceObject(
+ QBluetoothUuid(QBluetoothUuid::HeartRate), this);
+ }
+
+ if (!m_service) {
+ setMessage("Heart Rate Service not found.");
+ return;
+ }
+
+ connect(m_service, SIGNAL(stateChanged(QLowEnergyService::ServiceState)),
+ this, SLOT(serviceStateChanged(QLowEnergyService::ServiceState)));
+ connect(m_service, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),
+ this, SLOT(updateHeartRateValue(QLowEnergyCharacteristic,QByteArray)));
+ connect(m_service, SIGNAL(descriptorChanged(QLowEnergyDescriptor,QByteArray)),
+ this, SLOT(confirmedDescriptorWrite(QLowEnergyDescriptor,QByteArray)));
+
+ m_service->discoverDetails();
+}
+
+//! [Connecting to service]
+
+void HeartRate::disconnectService()
+{
+ foundHeartRateService = false;
+ m_stop = QDateTime::currentDateTime();
+
+ if (m_devices.isEmpty()) {
+ if (timer)
+ timer->stop();
+ return;
+ }
+
+ //disable notifications
+ if (m_notificationDesc.isValid() && m_service) {
+ m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0000"));
+ } else {
+ m_control->disconnectFromDevice();
+ delete m_service;
+ m_service = 0;
+ }
+}
+
+//! [Error handling]
+void HeartRate::controllerError(QLowEnergyController::Error error)
+{
+ setMessage("Cannot connect to remote device.");
+ qWarning() << "Controller Error:" << error;
+}
+//! [Error handling]
+
+
+void HeartRate::serviceStateChanged(QLowEnergyService::ServiceState s)
+{
+ switch (s) {
+ case QLowEnergyService::ServiceDiscovered:
+ {
+ const QLowEnergyCharacteristic hrChar = m_service->characteristic(
+ QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement));
+ if (!hrChar.isValid()) {
+ setMessage("HR Data not found.");
+ break;
+ }
+
+ const QLowEnergyDescriptor m_notificationDesc = hrChar.descriptor(
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ if (m_notificationDesc.isValid()) {
+ m_service->writeDescriptor(m_notificationDesc, QByteArray::fromHex("0100"));
+ setMessage("Measuring");
+ m_start = QDateTime::currentDateTime();
+ }
+
+ break;
+ }
+ default:
+ //nothing for now
+ break;
+ }
+}
+
+void HeartRate::serviceError(QLowEnergyService::ServiceError e)
+{
+ switch (e) {
+ case QLowEnergyService::DescriptorWriteError:
+ setMessage("Cannot obtain HR notifications");
+ break;
+ default:
+ qWarning() << "HR service error:" << e;
+ }
+}
+
+void HeartRate::updateHeartRateValue(const QLowEnergyCharacteristic &c,
+ const QByteArray &value)
+{
+ // ignore any other characteristic change -> shouldn't really happen though
+ if (c.uuid() != QBluetoothUuid(QBluetoothUuid::HeartRateMeasurement))
+ return;
+
+ //! [Reading value]
+ const char *data = value.constData();
+ quint8 flags = data[0];
+
+ //Heart Rate
+ if (flags & 0x1) { // HR 16 bit? otherwise 8 bit
+ quint16 *heartRate = (quint16 *) &data[1];
+ //qDebug() << "16 bit HR value:" << *heartRate;
+ m_measurements.append(*heartRate);
+ } else {
+ quint8 *heartRate = (quint8 *) &data[1];
+ m_measurements.append(*heartRate);
+ //qDebug() << "8 bit HR value:" << *heartRate;
+ }
+
+ //Energy Expended
+ if (flags & 0x8) {
+ int index = (flags & 0x1) ? 5 : 3;
+ quint16 *energy = (quint16 *) &data[index];
+ qDebug() << "Used Energy:" << *energy;
+ }
+ //! [Reading value]
+
+ Q_EMIT hrChanged();
+}
+
+void HeartRate::confirmedDescriptorWrite(const QLowEnergyDescriptor &d,
+ const QByteArray &value)
+{
+ if (d.isValid() && d == m_notificationDesc && value == QByteArray("0000")) {
+ //disabled notifications -> assume disconnect intent
+ m_control->disconnectFromDevice();
+ delete m_service;
+ m_service = 0;
+ }
+}
+
+int HeartRate::hR() const
+{
+ if (m_measurements.isEmpty())
+ return 0;
+ return m_measurements.last();
+}
+
+void HeartRate::obtainResults()
+{
+ Q_EMIT timeChanged();
+ Q_EMIT averageChanged();
+ Q_EMIT caloriesChanged();
+}
+
+int HeartRate::time()
+{
+ return m_start.secsTo(m_stop);
+}
+
+int HeartRate::maxHR() const
+{
+ return m_max;
+}
+
+int HeartRate::minHR() const
+{
+ return m_min;
+}
+
+float HeartRate::average()
+{
+ if (m_measurements.size() == 0) {
+ return 0;
+ } else {
+ m_max = 0;
+ m_min = 1000;
+ int sum = 0;
+ for (int i = 0; i < m_measurements.size(); i++) {
+ sum += (int) m_measurements.value(i);
+ if (((int)m_measurements.value(i)) > m_max)
+ m_max = (int)m_measurements.value(i);
+ if (((int)m_measurements.value(i)) < m_min)
+ m_min = (int)m_measurements.value(i);
+ }
+ return sum/m_measurements.size();
+ }
+}
+
+int HeartRate::measurements(int index) const
+{
+ if (index > m_measurements.size())
+ return 0;
+ else
+ return (int)m_measurements.value(index);
+}
+
+int HeartRate::measurementsSize() const
+{
+ return m_measurements.size();
+}
+
+QString HeartRate::deviceAddress() const
+{
+ return m_currentDevice.getAddress();
+}
+
+float HeartRate::caloriesCalculation()
+{
+ calories = ((-55.0969 + (0.6309 * average()) + (0.1988 * 94) + (0.2017 * 24)) / 4.184) * 60 * time()/3600 ;
+ return calories;
+}
+
+int HeartRate::numDevices() const
+{
+ return m_devices.size();
+}
+
+void HeartRate::startDemo()
+{
+ m_start = QDateTime::currentDateTime();
+ if (!timer) {
+ timer = new QTimer(this);
+ connect(timer, SIGNAL(timeout()), this, SLOT(receiveDemo()));
+ }
+ timer->start(1000);
+ setMessage("This is Demo mode");
+}
+
+void HeartRate::receiveDemo()
+{
+ m_measurements.append(randomPulse());
+ Q_EMIT hrChanged();
+}
+
+int HeartRate::randomPulse() const
+{
+ // random number between 50 and 70
+ return qrand() % (70 - 50) + 50;
+}
diff --git a/examples/bluetooth/heartlistener/heartrate.h b/examples/bluetooth/heartlistener/heartrate.h
new file mode 100644
index 00000000..65e92af8
--- /dev/null
+++ b/examples/bluetooth/heartlistener/heartrate.h
@@ -0,0 +1,149 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HEARTRATE_H
+#define HEARTRATE_H
+
+#include "deviceinfo.h"
+
+#include <QString>
+#include <QDebug>
+#include <QDateTime>
+#include <QVector>
+#include <QTimer>
+#include <QBluetoothDeviceDiscoveryAgent>
+#include <QBluetoothDeviceInfo>
+#include <QLowEnergyController>
+#include <QLowEnergyService>
+
+
+QT_USE_NAMESPACE
+class HeartRate: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant name READ name NOTIFY nameChanged)
+ Q_PROPERTY(QString message READ message NOTIFY messageChanged)
+ Q_PROPERTY(int hr READ hR NOTIFY hrChanged)
+ Q_PROPERTY(int maxHR READ maxHR NOTIFY averageChanged)
+ Q_PROPERTY(int minHR READ minHR NOTIFY averageChanged)
+ Q_PROPERTY(float average READ average NOTIFY averageChanged)
+ Q_PROPERTY(int time READ time NOTIFY timeChanged)
+ Q_PROPERTY(float calories READ caloriesCalculation NOTIFY caloriesChanged)
+
+public:
+ HeartRate();
+ ~HeartRate();
+ void setMessage(QString message);
+ QString message() const;
+ QVariant name();
+ int hR() const;
+ int time();
+ float average();
+ int maxHR() const;
+ int minHR() const;
+ float caloriesCalculation();
+
+public slots:
+ void deviceSearch();
+ void connectToService(const QString &address);
+ void disconnectService();
+ void startDemo();
+
+ void obtainResults();
+ int measurements(int index) const;
+ int measurementsSize() const;
+ QString deviceAddress() const;
+ int numDevices() const;
+
+private slots:
+ //QBluetothDeviceDiscoveryAgent
+ void addDevice(const QBluetoothDeviceInfo&);
+ void scanFinished();
+ void deviceScanError(QBluetoothDeviceDiscoveryAgent::Error);
+
+ //QLowEnergyController
+ void serviceDiscovered(const QBluetoothUuid &);
+ void serviceScanDone();
+ void controllerError(QLowEnergyController::Error);
+ void serviceConnected();
+ void deviceDisconnected();
+
+
+ //QLowEnergyService
+ void serviceStateChanged(QLowEnergyService::ServiceState s);
+ void updateHeartRateValue(const QLowEnergyCharacteristic &c,
+ const QByteArray &value);
+ void confirmedDescriptorWrite(const QLowEnergyDescriptor &d,
+ const QByteArray &value);
+ void serviceError(QLowEnergyService::ServiceError e);
+
+ //DemoMode
+ void receiveDemo();
+
+Q_SIGNALS:
+ void messageChanged();
+ void nameChanged();
+ void hrChanged();
+ void averageChanged();
+ void timeChanged();
+ void caloriesChanged();
+
+private:
+ int randomPulse() const;
+
+ DeviceInfo m_currentDevice;
+ QBluetoothDeviceDiscoveryAgent *m_deviceDiscoveryAgent;
+ QLowEnergyDescriptor m_notificationDesc;
+ QList<QObject*> m_devices;
+ QString m_info;
+ bool foundHeartRateService;
+ QVector<quint16> m_measurements;
+ QDateTime m_start;
+ QDateTime m_stop;
+ int m_max;
+ int m_min;
+ float calories;
+ QLowEnergyController *m_control;
+ QTimer *timer; // for demo application
+ QLowEnergyService *m_service;
+};
+
+#endif // HEARTRATE_H
diff --git a/examples/bluetooth/heartlistener/main.cpp b/examples/bluetooth/heartlistener/main.cpp
new file mode 100644
index 00000000..e50b87f1
--- /dev/null
+++ b/examples/bluetooth/heartlistener/main.cpp
@@ -0,0 +1,61 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the examples of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QLoggingCategory>
+#include <QQmlContext>
+#include <QGuiApplication>
+#include <QQuickView>
+#include "heartrate.h"
+
+int main(int argc, char *argv[])
+{
+ //QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QGuiApplication app(argc, argv);
+
+ HeartRate heartRate;
+ QQuickView *view = new QQuickView;
+ view->rootContext()->setContextProperty("heartRate", &heartRate);
+ view->setSource(QUrl("qrc:/assets/main.qml"));
+ view->setResizeMode(QQuickView::SizeRootObjectToView);
+ view->show();
+ return app.exec();
+
+}
diff --git a/examples/bluetooth/heartlistener/resources.qrc b/examples/bluetooth/heartlistener/resources.qrc
new file mode 100644
index 00000000..ac6f9c83
--- /dev/null
+++ b/examples/bluetooth/heartlistener/resources.qrc
@@ -0,0 +1,16 @@
+<RCC>
+ <qresource prefix="/">
+ <file>assets/blue_heart.png</file>
+ <file>assets/busy_dark.png</file>
+ <file>assets/Button.qml</file>
+ <file>assets/dialog.qml</file>
+ <file>assets/draw.js</file>
+ <file>assets/home.qml</file>
+ <file>assets/main.qml</file>
+ <file>assets/monitor.qml</file>
+ <file>assets/Point.qml</file>
+ <file>assets/results.qml</file>
+ <file>assets/star.png</file>
+ <file>assets/blue_heart_small.png</file>
+ </qresource>
+</RCC>
diff --git a/examples/bluetooth/lowenergyscanner/assets/Characteristics.qml b/examples/bluetooth/lowenergyscanner/assets/Characteristics.qml
new file mode 100644
index 00000000..4a7db471
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/assets/Characteristics.qml
@@ -0,0 +1,147 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ width: 300
+ height: 600
+
+ Header {
+ id: header
+ anchors.top: parent.top
+ headerText: "Characteristics list"
+ }
+
+ Dialog {
+ id: info
+ anchors.centerIn: parent
+ visible: true
+ dialogText: "Scanning for characteristics...";
+ }
+
+ Connections {
+ target: device
+ onCharacteristicsUpdated: {
+ menu.menuText = "Back"
+ if (characteristicview.count === 0)
+ info.dialogText = "No characteristic found"
+ else
+ info.visible = false;
+ }
+
+ onDisconnected: {
+ pageLoader.source = "main.qml"
+ }
+ }
+
+ ListView {
+ id: characteristicview
+ width: parent.width
+ clip: true
+
+ anchors.top: header.bottom
+ anchors.bottom: menu.top
+ model: device.characteristicList
+
+ delegate: Rectangle {
+ id: characteristicbox
+ height:300
+ width: parent.width
+ color: "lightsteelblue"
+ border.width: 2
+ border.color: "black"
+ radius: 5
+
+ Label {
+ id: characteristicName
+ textContent: modelData.characteristicName
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicUuid
+ font.pointSize: characteristicName.font.pointSize*0.7
+ textContent: modelData.characteristicUuid
+ anchors.top: characteristicName.bottom
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicValue
+ font.pointSize: characteristicName.font.pointSize*0.7
+ textContent: ("Value: " + modelData.characteristicValue)
+ anchors.bottom: characteristicHandle.top
+ horizontalAlignment: Text.AlignHCenter
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicHandle
+ font.pointSize: characteristicName.font.pointSize*0.7
+ textContent: ("Handlers: " + modelData.characteristicHandle)
+ anchors.bottom: characteristicPermission.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: characteristicPermission
+ font.pointSize: characteristicName.font.pointSize*0.7
+ textContent: modelData.characteristicPermission
+ anchors.bottom: parent.bottom
+ anchors.topMargin: 5
+ anchors.bottomMargin: 5
+ }
+ }
+ }
+
+ Menu {
+ id: menu
+ anchors.bottom: parent.bottom
+ menuWidth: parent.width
+ menuText: "Scanning"
+ menuHeight: (parent.height/6)
+ onButtonClick: {
+ pageLoader.source = "Services.qml"
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/assets/Dialog.qml b/examples/bluetooth/lowenergyscanner/assets/Dialog.qml
new file mode 100644
index 00000000..be5388b5
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/assets/Dialog.qml
@@ -0,0 +1,61 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ width: parent.width/2
+ height: 62
+ z: 50
+ property string dialogText: ""
+ border.width: 1
+ border.color: "#363636"
+ radius: 10
+
+ Text {
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ anchors.fill: parent
+ elide: Text.ElideMiddle
+ text: dialogText
+ color: "#363636"
+ wrapMode: Text.Wrap
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/assets/Header.qml b/examples/bluetooth/lowenergyscanner/assets/Header.qml
new file mode 100644
index 00000000..654fda34
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/assets/Header.qml
@@ -0,0 +1,61 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ width: parent.width
+ height: 70
+ border.width: 1
+ border.color: "#363636"
+ radius: 5
+ property string headerText: ""
+
+ Text {
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ anchors.fill: parent
+ text: headerText
+ font.bold: true
+ font.pixelSize: 30
+ elide: Text.ElideMiddle
+ color: "#363636"
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/assets/Label.qml b/examples/bluetooth/lowenergyscanner/assets/Label.qml
new file mode 100644
index 00000000..1dced831
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/assets/Label.qml
@@ -0,0 +1,53 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Text {
+ property string textContent: ""
+ font.pixelSize: 30
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "#363636"
+ horizontalAlignment: Text.AlignHCenter
+ elide: Text.ElideMiddle
+ width: parent.width
+ wrapMode: Text.Wrap
+ text: textContent
+}
diff --git a/examples/bluetooth/lowenergyscanner/assets/Menu.qml b/examples/bluetooth/lowenergyscanner/assets/Menu.qml
new file mode 100644
index 00000000..d1eaebb2
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/assets/Menu.qml
@@ -0,0 +1,90 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+
+ property real menuWidth: 100
+ property real menuHeight: 50
+ property string menuText: "Search"
+ signal buttonClick()
+
+ height: menuHeight
+ width: menuWidth
+
+ Rectangle {
+ id: search
+ width: parent.width
+ height: parent.height
+ anchors.centerIn: parent
+ color: "#363636"
+ border.width: 1
+ border.color: "#E3E3E3"
+ radius: 5
+ Text {
+ id: searchText
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ anchors.fill: parent
+ text: menuText
+ elide: Text.ElideMiddle
+ color: "#E3E3E3"
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onPressed: {
+ search.width = search.width - 7
+ search.height = search.height - 5
+ }
+
+ onReleased: {
+ search.width = search.width + 7
+ search.height = search.height + 5
+ }
+
+ onClicked: {
+ buttonClick()
+ }
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/assets/Services.qml b/examples/bluetooth/lowenergyscanner/assets/Services.qml
new file mode 100644
index 00000000..e4e802d2
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/assets/Services.qml
@@ -0,0 +1,136 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ width: 300
+ height: 600
+
+ Header {
+ id: header
+ anchors.top: parent.top
+ headerText: "Services list"
+ }
+
+ Dialog {
+ id: info
+ anchors.centerIn: parent
+ visible: true
+ dialogText: "Scanning for services...";
+ }
+
+ Connections {
+ target: device
+ onServicesUpdated: {
+ if (servicesview.count === 0)
+ info.dialogText = "No services found"
+ else
+ info.visible = false;
+ }
+
+ onDisconnected: {
+ pageLoader.source = "main.qml"
+ }
+ }
+
+ ListView {
+ id: servicesview
+ width: parent.width
+ anchors.top: header.bottom
+ anchors.bottom: menu.top
+ model: device.servicesList
+ clip: true
+
+ delegate: Rectangle {
+ id: servicebox
+ height:100
+ color: "lightsteelblue"
+ border.width: 2
+ border.color: "black"
+ radius: 5
+ width: parent.width
+ Component.onCompleted: {
+ info.visible = false
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ device.connectToService(modelData.serviceUuid);
+ pageLoader.source = "Characteristics.qml";
+ }
+ }
+
+ Label {
+ id: serviceName
+ textContent: modelData.serviceName
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ textContent: modelData.serviceType
+ font.pointSize: serviceName.font.pointSize * 0.5
+ anchors.top: serviceName.bottom
+ }
+
+ Label {
+ id: serviceUuid
+ font.pointSize: serviceName.font.pointSize * 0.5
+ textContent: modelData.serviceUuid
+ anchors.bottom: servicebox.bottom
+ anchors.bottomMargin: 5
+ }
+ }
+ }
+
+ Menu {
+ id: menu
+ anchors.bottom: parent.bottom
+ menuWidth: parent.width
+ menuText: "Back"
+ menuHeight: (parent.height/6)
+ onButtonClick: {
+ device.disconnectFromDevice()
+ }
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/assets/main.qml b/examples/bluetooth/lowenergyscanner/assets/main.qml
new file mode 100644
index 00000000..9f0395d5
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/assets/main.qml
@@ -0,0 +1,130 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+Rectangle {
+ id: back
+ width: 300
+ height: 600
+ property bool deviceState: device.state
+ onDeviceStateChanged: {
+ if (!device.state)
+ info.visible = false;
+ }
+
+ Header {
+ id: header
+ anchors.top: parent.top
+ headerText: "Start Discovery"
+ }
+
+ Dialog {
+ id: info
+ anchors.centerIn: parent
+ visible: false
+ }
+
+ ListView {
+ id: theListView
+ width: parent.width
+ clip: true
+
+ anchors.top: header.bottom
+ anchors.bottom: menu.top
+ model: device.devicesList
+
+ delegate: Rectangle {
+ id: box
+ height:100
+ width: parent.width
+ color: "lightsteelblue"
+ border.width: 2
+ border.color: "black"
+ radius: 5
+
+ Component.onCompleted: {
+ info.visible = false;
+ header.headerText = "Select a device";
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ device.scanServices(modelData.deviceAddress);
+ pageLoader.source = "Services.qml"
+ }
+ }
+
+ Label {
+ id: deviceName
+ textContent: modelData.deviceName
+ anchors.top: parent.top
+ anchors.topMargin: 5
+ }
+
+ Label {
+ id: deviceAddress
+ textContent: modelData.deviceAddress
+ font.pointSize: deviceName.font.pointSize*0.7
+ anchors.bottom: box.bottom
+ anchors.bottomMargin: 5
+ }
+ }
+ }
+
+ Menu {
+ id: menu
+ anchors.bottom: parent.bottom
+ menuWidth: parent.width
+ menuHeight: (parent.height/6)
+ menuText: device.update
+ onButtonClick: {
+ device.startDeviceDiscovery();
+ info.dialogText = "Searching...";
+ info.visible = true;}
+ }
+
+ Loader {
+ id: pageLoader
+ anchors.fill: parent
+ }
+}
diff --git a/examples/bluetooth/lowenergyscanner/characteristicinfo.cpp b/examples/bluetooth/lowenergyscanner/characteristicinfo.cpp
new file mode 100644
index 00000000..d7ae8438
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/characteristicinfo.cpp
@@ -0,0 +1,145 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "characteristicinfo.h"
+#include "qbluetoothuuid.h"
+#include <QByteArray>
+
+CharacteristicInfo::CharacteristicInfo()
+{
+}
+
+CharacteristicInfo::CharacteristicInfo(const QLowEnergyCharacteristic &characteristic):
+ m_characteristic(characteristic)
+{
+}
+
+void CharacteristicInfo::setCharacteristic(const QLowEnergyCharacteristic &characteristic)
+{
+ m_characteristic = characteristic;
+ emit characteristicChanged();
+}
+
+QString CharacteristicInfo::getName() const
+{
+ QString name = m_characteristic.name();
+ if (!name.isEmpty())
+ return name;
+
+ // find descriptor with CharacteristicUserDescription
+ foreach (const QLowEnergyDescriptor &descriptor, m_characteristic.descriptors()) {
+ if (descriptor.type() == QBluetoothUuid::CharacteristicUserDescription) {
+ name = descriptor.value();
+ break;
+ }
+ }
+
+ if (name.isEmpty())
+ name = "Unknown";
+
+ return name;
+}
+
+QString CharacteristicInfo::getUuid() const
+{
+ const QBluetoothUuid uuid = m_characteristic.uuid();
+ bool success = false;
+ quint16 result16 = uuid.toUInt16(&success);
+ if (success)
+ return QStringLiteral("0x") + QString::number(result16, 16);
+
+ quint32 result32 = uuid.toUInt32(&success);
+ if (success)
+ return QStringLiteral("0x") + QString::number(result32, 16);
+
+ return uuid.toString().remove(QLatin1Char('{')).remove(QLatin1Char('}'));
+}
+
+QString CharacteristicInfo::getValue() const
+{
+ // Show raw string first and hex value below
+ QByteArray a = m_characteristic.value();
+ QString result;
+ if (a.isEmpty()) {
+ result = QStringLiteral("<none>");
+ return result;
+ }
+
+ result = a;
+ result += QLatin1Char('\n');
+ result += a.toHex();
+
+ return result;
+}
+
+QString CharacteristicInfo::getHandle() const
+{
+ return QStringLiteral("0x") + QString::number(m_characteristic.handle(), 16);
+}
+
+QString CharacteristicInfo::getPermission() const
+{
+ QString properties = "( ";
+ int permission = m_characteristic.properties();
+ if (permission & QLowEnergyCharacteristic::Read)
+ properties += QStringLiteral(" Read");
+ if (permission & QLowEnergyCharacteristic::Write)
+ properties += QStringLiteral(" Write");
+ if (permission & QLowEnergyCharacteristic::Notify)
+ properties += QStringLiteral(" Notify");
+ if (permission & QLowEnergyCharacteristic::Indicate)
+ properties += QStringLiteral(" Indicate");
+ if (permission & QLowEnergyCharacteristic::ExtendedProperty)
+ properties += QStringLiteral(" ExtendedProperty");
+ if (permission & QLowEnergyCharacteristic::Broadcasting)
+ properties += QStringLiteral(" Broadcast");
+ if (permission & QLowEnergyCharacteristic::WriteNoResponse)
+ properties += QStringLiteral(" WriteNoResp");
+ if (permission & QLowEnergyCharacteristic::WriteSigned)
+ properties += QStringLiteral(" WriteSigned");
+ properties += " )";
+ return properties;
+}
+
+QLowEnergyCharacteristic CharacteristicInfo::getCharacteristic() const
+{
+ return m_characteristic;
+}
diff --git a/examples/bluetooth/lowenergyscanner/characteristicinfo.h b/examples/bluetooth/lowenergyscanner/characteristicinfo.h
new file mode 100644
index 00000000..d06c962e
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/characteristicinfo.h
@@ -0,0 +1,75 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef CHARACTERISTICINFO_H
+#define CHARACTERISTICINFO_H
+#include <QObject>
+#include <QString>
+#include <QtBluetooth/QLowEnergyCharacteristic>
+
+class CharacteristicInfo: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString characteristicName READ getName NOTIFY characteristicChanged)
+ Q_PROPERTY(QString characteristicUuid READ getUuid NOTIFY characteristicChanged)
+ Q_PROPERTY(QString characteristicValue READ getValue NOTIFY characteristicChanged)
+ Q_PROPERTY(QString characteristicHandle READ getHandle NOTIFY characteristicChanged)
+ Q_PROPERTY(QString characteristicPermission READ getPermission NOTIFY characteristicChanged)
+
+public:
+ CharacteristicInfo();
+ CharacteristicInfo(const QLowEnergyCharacteristic &characteristic);
+ void setCharacteristic(const QLowEnergyCharacteristic &characteristic);
+ QString getName() const;
+ QString getUuid() const;
+ QString getValue() const;
+ QString getHandle() const;
+ QString getPermission() const;
+ QLowEnergyCharacteristic getCharacteristic() const;
+
+Q_SIGNALS:
+ void characteristicChanged();
+
+private:
+ QLowEnergyCharacteristic m_characteristic;
+};
+
+#endif // CHARACTERISTICINFO_H
diff --git a/examples/bluetooth/lowenergyscanner/device.cpp b/examples/bluetooth/lowenergyscanner/device.cpp
new file mode 100644
index 00000000..3d82800f
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/device.cpp
@@ -0,0 +1,290 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "device.h"
+#include <qbluetoothaddress.h>
+#include <qbluetoothdevicediscoveryagent.h>
+#include <qbluetoothlocaldevice.h>
+#include <qbluetoothdeviceinfo.h>
+#include <qbluetoothservicediscoveryagent.h>
+#include <QDebug>
+#include <QList>
+#include <QTimer>
+
+Device::Device():
+ connected(false), controller(0), m_deviceScanState(false)
+{
+ discoveryAgent = new QBluetoothDeviceDiscoveryAgent();
+ connect(discoveryAgent, SIGNAL(deviceDiscovered(const QBluetoothDeviceInfo&)),
+ this, SLOT(addDevice(const QBluetoothDeviceInfo&)));
+ connect(discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
+ this, SLOT(deviceScanError(QBluetoothDeviceDiscoveryAgent::Error)));
+ connect(discoveryAgent, SIGNAL(finished()), this, SLOT(deviceScanFinished()));
+
+ setUpdate("Search");
+}
+
+Device::~Device()
+{
+ delete discoveryAgent;
+ delete controller;
+ qDeleteAll(devices);
+ qDeleteAll(m_services);
+ qDeleteAll(m_characteristics);
+ devices.clear();
+ m_services.clear();
+ m_characteristics.clear();
+}
+
+void Device::startDeviceDiscovery()
+{
+ qDeleteAll(devices);
+ devices.clear();
+ emit devicesUpdated();
+
+ setUpdate("Scanning for devices ...");
+ discoveryAgent->start();
+ m_deviceScanState = true;
+ Q_EMIT stateChanged();
+}
+
+void Device::addDevice(const QBluetoothDeviceInfo &info)
+{
+ if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration) {
+ DeviceInfo *d = new DeviceInfo(info);
+ devices.append(d);
+ setUpdate("Last device added: " + d->getName());
+ }
+}
+
+void Device::deviceScanFinished()
+{
+ emit devicesUpdated();
+ m_deviceScanState = false;
+ emit stateChanged();
+ if (devices.isEmpty())
+ setUpdate("No Low Energy devices found...");
+ else
+ setUpdate("Done! Scan Again!");
+}
+
+QVariant Device::getDevices()
+{
+ return QVariant::fromValue(devices);
+}
+
+QVariant Device::getServices()
+{
+ return QVariant::fromValue(m_services);
+}
+
+QVariant Device::getCharacteristics()
+{
+ return QVariant::fromValue(m_characteristics);
+}
+
+QString Device::getUpdate()
+{
+ return m_message;
+}
+
+void Device::scanServices(const QString &address)
+{
+ // We need the current device for service discovery.
+ for (int i = 0; i < devices.size(); i++) {
+ if (((DeviceInfo*)devices.at(i))->getAddress() == address )
+ currentDevice.setDevice(((DeviceInfo*)devices.at(i))->getDevice());
+ }
+
+ if (!currentDevice.getDevice().isValid()) {
+ qWarning() << "Not a valid device";
+ return;
+ }
+
+ qDeleteAll(m_characteristics);
+ m_characteristics.clear();
+ emit characteristicsUpdated();
+ qDeleteAll(m_services);
+ m_services.clear();
+ emit servicesUpdated();
+
+ setUpdate("Connecting to device...");
+
+ if (controller && controller->remoteAddress() != currentDevice.getDevice().address()) {
+ controller->disconnectFromDevice();
+ delete controller;
+ controller = 0;
+ }
+
+ if (!controller) {
+ // Connecting signals and slots for connecting to LE services.
+ controller = new QLowEnergyController(currentDevice.getDevice().address());
+ connect(controller, SIGNAL(connected()),
+ this, SLOT(deviceConnected()));
+ connect(controller, SIGNAL(error(QLowEnergyController::Error)),
+ this, SLOT(errorReceived(QLowEnergyController::Error)));
+ connect(controller, SIGNAL(disconnected()),
+ this, SLOT(deviceDisconnected()));
+ connect(controller, SIGNAL(serviceDiscovered(QBluetoothUuid)),
+ this, SLOT(addLowEnergyService(QBluetoothUuid)));
+ connect(controller, SIGNAL(discoveryFinished()),
+ this, SLOT(serviceScanDone()));
+ }
+
+ controller->connectToDevice();
+}
+
+void Device::addLowEnergyService(const QBluetoothUuid &serviceUuid)
+{
+ QLowEnergyService *service = controller->createServiceObject(serviceUuid);
+ if (!service) {
+ qWarning() << "Cannot create service for uuid";
+ return;
+ }
+ ServiceInfo *serv = new ServiceInfo(service);
+ m_services.append(serv);
+
+ emit servicesUpdated();
+}
+
+void Device::serviceScanDone()
+{
+ setUpdate("Service scan done!");
+}
+
+void Device::connectToService(const QString &uuid)
+{
+ QLowEnergyService *service = 0;
+ for (int i = 0; i < m_services.size(); i++) {
+ ServiceInfo *serviceInfo = (ServiceInfo*)m_services.at(i);
+ if (serviceInfo->getUuid() == uuid) {
+ service = serviceInfo->service();
+ break;
+ }
+ }
+
+ if (!service)
+ return;
+
+ qDeleteAll(m_characteristics);
+ m_characteristics.clear();
+ emit characteristicsUpdated();
+
+ if (service->state() == QLowEnergyService::DiscoveryRequired) {
+ connect(service, SIGNAL(stateChanged(QLowEnergyService::ServiceState)),
+ this, SLOT(serviceDetailsDiscovered(QLowEnergyService::ServiceState)));
+ service->discoverDetails();
+ return;
+ }
+
+ //discovery already done
+ const QList<QLowEnergyCharacteristic> chars = service->characteristics();
+ foreach (const QLowEnergyCharacteristic &ch, chars) {
+ CharacteristicInfo *cInfo = new CharacteristicInfo(ch);
+ m_characteristics.append(cInfo);
+ }
+
+ QTimer::singleShot(0, this, SIGNAL(characteristicsUpdated()));
+}
+
+void Device::deviceConnected()
+{
+ setUpdate("Discovering services!");
+ connected = true;
+ controller->discoverServices();
+}
+
+void Device::errorReceived(QLowEnergyController::Error /*error*/)
+{
+ qWarning() << "Error: " << controller->errorString();
+ setUpdate(controller->errorString());
+}
+
+void Device::setUpdate(QString message)
+{
+ m_message = message;
+ emit updateChanged();
+}
+
+void Device::disconnectFromDevice()
+{
+ if (connected)
+ controller->disconnectFromDevice();
+}
+
+void Device::deviceDisconnected()
+{
+ qWarning() << "Disconnect from device";
+ emit disconnected();
+}
+
+void Device::serviceDetailsDiscovered(QLowEnergyService::ServiceState newState)
+{
+ if (newState != QLowEnergyService::ServiceDiscovered)
+ return;
+
+ QLowEnergyService *service = qobject_cast<QLowEnergyService *>(sender());
+ if (!service)
+ return;
+
+ const QList<QLowEnergyCharacteristic> chars = service->characteristics();
+ foreach (const QLowEnergyCharacteristic &ch, chars) {
+ CharacteristicInfo *cInfo = new CharacteristicInfo(ch);
+ m_characteristics.append(cInfo);
+ }
+
+ emit characteristicsUpdated();
+}
+
+void Device::deviceScanError(QBluetoothDeviceDiscoveryAgent::Error error)
+{
+ if (error == QBluetoothDeviceDiscoveryAgent::PoweredOffError)
+ setUpdate("The Bluetooth adaptor is powered off, power it on before doing discovery.");
+ else if (error == QBluetoothDeviceDiscoveryAgent::InputOutputError)
+ setUpdate("Writing or reading from the device resulted in an error.");
+ else
+ setUpdate("An unknown error has occurred.");
+}
+
+bool Device::state()
+{
+ return m_deviceScanState;
+}
diff --git a/examples/bluetooth/lowenergyscanner/device.h b/examples/bluetooth/lowenergyscanner/device.h
new file mode 100644
index 00000000..f15c4c95
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/device.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEVICE_H
+#define DEVICE_H
+
+#include <qbluetoothglobal.h>
+#include <qbluetoothlocaldevice.h>
+#include <QObject>
+#include <QVariant>
+#include <QList>
+#include <QBluetoothServiceDiscoveryAgent>
+#include <QBluetoothDeviceDiscoveryAgent>
+#include <QLowEnergyController>
+#include <QBluetoothServiceInfo>
+#include "deviceinfo.h"
+#include "serviceinfo.h"
+#include "characteristicinfo.h"
+
+QT_FORWARD_DECLARE_CLASS (QBluetoothDeviceInfo)
+QT_FORWARD_DECLARE_CLASS (QLowEnergyServiceInfo)
+QT_FORWARD_DECLARE_CLASS (QBluetoothServiceInfo)
+
+class Device: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QVariant devicesList READ getDevices NOTIFY devicesUpdated)
+ Q_PROPERTY(QVariant servicesList READ getServices NOTIFY servicesUpdated)
+ Q_PROPERTY(QVariant characteristicList READ getCharacteristics NOTIFY characteristicsUpdated)
+ Q_PROPERTY(QString update READ getUpdate NOTIFY updateChanged)
+ Q_PROPERTY(bool state READ state NOTIFY stateChanged)
+public:
+ Device();
+ ~Device();
+ QVariant getDevices();
+ QVariant getServices();
+ QVariant getCharacteristics();
+ QString getUpdate();
+ bool state();
+
+public slots:
+ void startDeviceDiscovery();
+ void scanServices(const QString &address);
+
+ void connectToService(const QString &uuid);
+ void disconnectFromDevice();
+
+private slots:
+ // QBluetoothDeviceDiscoveryAgent related
+ void addDevice(const QBluetoothDeviceInfo&);
+ void deviceScanFinished();
+ void deviceScanError(QBluetoothDeviceDiscoveryAgent::Error);
+
+ // QLowEnergyController realted
+ void addLowEnergyService(const QBluetoothUuid &uuid);
+ void deviceConnected();
+ void errorReceived(QLowEnergyController::Error);
+ void serviceScanDone();
+ void deviceDisconnected();
+
+ // QLowEnergyService related
+ void serviceDetailsDiscovered(QLowEnergyService::ServiceState newState);
+
+Q_SIGNALS:
+ void devicesUpdated();
+ void servicesUpdated();
+ void characteristicsUpdated();
+ void updateChanged();
+ void stateChanged();
+ void disconnected();
+
+private:
+ void setUpdate(QString message);
+ QBluetoothDeviceDiscoveryAgent *discoveryAgent;
+ DeviceInfo currentDevice;
+ QList<QObject*> devices;
+ QList<QObject*> m_services;
+ QList<QObject*> m_characteristics;
+ QString m_message;
+ bool connected;
+ QLowEnergyController *controller;
+ bool m_deviceScanState;
+};
+
+#endif // DEVICE_H
diff --git a/examples/bluetooth/lowenergyscanner/deviceinfo.cpp b/examples/bluetooth/lowenergyscanner/deviceinfo.cpp
new file mode 100644
index 00000000..d6b7a734
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/deviceinfo.cpp
@@ -0,0 +1,72 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "deviceinfo.h"
+
+DeviceInfo::DeviceInfo()
+{
+}
+
+DeviceInfo::DeviceInfo(const QBluetoothDeviceInfo &d)
+{
+ device = d;
+}
+
+QString DeviceInfo::getAddress() const
+{
+ return device.address().toString();
+}
+
+QString DeviceInfo::getName() const
+{
+ return device.name();
+}
+
+QBluetoothDeviceInfo DeviceInfo::getDevice()
+{
+ return device;
+}
+
+void DeviceInfo::setDevice(const QBluetoothDeviceInfo &dev)
+{
+ device = QBluetoothDeviceInfo(dev);
+ Q_EMIT deviceChanged();
+}
diff --git a/examples/bluetooth/lowenergyscanner/deviceinfo.h b/examples/bluetooth/lowenergyscanner/deviceinfo.h
new file mode 100644
index 00000000..48f11cfe
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/deviceinfo.h
@@ -0,0 +1,71 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef DEVICEINFO_H
+#define DEVICEINFO_H
+
+#include <QObject>
+#include <qbluetoothdeviceinfo.h>
+#include <qbluetoothaddress.h>
+#include <QList>
+#include "deviceinfo.h"
+
+class DeviceInfo: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString deviceName READ getName NOTIFY deviceChanged)
+ Q_PROPERTY(QString deviceAddress READ getAddress NOTIFY deviceChanged)
+public:
+ DeviceInfo();
+ DeviceInfo(const QBluetoothDeviceInfo &d);
+ QString getAddress() const;
+ QString getName() const;
+ QBluetoothDeviceInfo getDevice();
+ void setDevice(const QBluetoothDeviceInfo &dev);
+
+Q_SIGNALS:
+ void deviceChanged();
+
+private:
+ QBluetoothDeviceInfo device;
+};
+
+#endif // DEVICEINFO_H
diff --git a/examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example.png b/examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example.png
new file mode 100644
index 00000000..bdc3a22e
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example.png
Binary files differ
diff --git a/examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example1.png b/examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example1.png
new file mode 100644
index 00000000..4854abf7
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/doc/images/lowenergyscanner-example1.png
Binary files differ
diff --git a/examples/bluetooth/lowenergyscanner/doc/src/lowenergyscanner.qdoc b/examples/bluetooth/lowenergyscanner/doc/src/lowenergyscanner.qdoc
new file mode 100644
index 00000000..fe7e007f
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/doc/src/lowenergyscanner.qdoc
@@ -0,0 +1,40 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: http://www.gnu.org/copyleft/fdl.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \example lowenergyscanner
+ \title Bluetooth Low Energy Scanner Example
+
+ The Bluetooth Low Energy Scanner Example shows how to develop Bluetooth
+ Low Energy applications using the Qt Bluetooth API. The application covers
+ scanning for Low Energy devices, scanning their services and reading
+ the service characteristics.
+
+ \image lowenergyscanner-example.png
+ \image lowenergyscanner-example1.png
+
+*/
diff --git a/examples/bluetooth/lowenergyscanner/lowenergyscanner.pro b/examples/bluetooth/lowenergyscanner/lowenergyscanner.pro
new file mode 100644
index 00000000..f3378b4c
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/lowenergyscanner.pro
@@ -0,0 +1,25 @@
+TARGET = lowenergyscanner
+INCLUDEPATH += .
+
+QT += quick bluetooth
+
+# Input
+SOURCES += main.cpp \
+ device.cpp \
+ deviceinfo.cpp \
+ serviceinfo.cpp \
+ characteristicinfo.cpp
+
+OTHER_FILES += assets/*.qml
+
+HEADERS += \
+ device.h \
+ deviceinfo.h \
+ serviceinfo.h \
+ characteristicinfo.h
+
+RESOURCES += \
+ resources.qrc
+
+target.path = $$[QT_INSTALL_EXAMPLES]/bluetooth/lowenergyscanner
+INSTALLS += target
diff --git a/examples/bluetooth/lowenergyscanner/main.cpp b/examples/bluetooth/lowenergyscanner/main.cpp
new file mode 100644
index 00000000..ce359545
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/main.cpp
@@ -0,0 +1,62 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QLoggingCategory>
+#include <QQmlContext>
+#include <QGuiApplication>
+#include <QQuickView>
+#include "device.h"
+
+
+int main(int argc, char *argv[])
+{
+ //QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ QGuiApplication app(argc, argv);
+
+ Device d;
+ QQuickView *view = new QQuickView;
+ view->rootContext()->setContextProperty("device", &d);
+
+ view->setSource(QUrl("qrc:/assets/main.qml"));
+ view->setResizeMode(QQuickView::SizeRootObjectToView);
+ view->show();
+ return app.exec();
+}
diff --git a/examples/bluetooth/lowenergyscanner/resources.qrc b/examples/bluetooth/lowenergyscanner/resources.qrc
new file mode 100644
index 00000000..49a518e8
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/resources.qrc
@@ -0,0 +1,11 @@
+<RCC>
+ <qresource prefix="/">
+ <file>assets/Characteristics.qml</file>
+ <file>assets/main.qml</file>
+ <file>assets/Menu.qml</file>
+ <file>assets/Services.qml</file>
+ <file>assets/Header.qml</file>
+ <file>assets/Dialog.qml</file>
+ <file>assets/Label.qml</file>
+ </qresource>
+</RCC>
diff --git a/examples/bluetooth/lowenergyscanner/serviceinfo.cpp b/examples/bluetooth/lowenergyscanner/serviceinfo.cpp
new file mode 100644
index 00000000..c05422ca
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/serviceinfo.cpp
@@ -0,0 +1,102 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "serviceinfo.h"
+
+ServiceInfo::ServiceInfo()
+{
+}
+
+ServiceInfo::ServiceInfo(QLowEnergyService *service):
+ m_service(service)
+{
+ m_service->setParent(this);
+}
+
+QLowEnergyService *ServiceInfo::service() const
+{
+ return m_service;
+}
+
+QString ServiceInfo::getName() const
+{
+ if (!m_service)
+ return QString();
+
+ return m_service->serviceName();
+}
+
+QString ServiceInfo::getType() const
+{
+ if (!m_service)
+ return QString();
+
+ QString result;
+ if (m_service->type() & QLowEnergyService::PrimaryService)
+ result += QStringLiteral("primary");
+ else
+ result += QStringLiteral("secondary");
+
+ if (m_service->type() & QLowEnergyService::IncludedService)
+ result += QStringLiteral(" included");
+
+ result.prepend('<').append('>');
+
+ return result;
+}
+
+QString ServiceInfo::getUuid() const
+{
+ if (!m_service)
+ return QString();
+
+ const QBluetoothUuid uuid = m_service->serviceUuid();
+ bool success = false;
+ quint16 result16 = uuid.toUInt16(&success);
+ if (success)
+ return QStringLiteral("0x") + QString::number(result16, 16);
+
+ quint32 result32 = uuid.toUInt32(&success);
+ if (success)
+ return QStringLiteral("0x") + QString::number(result32, 16);
+
+ return uuid.toString().remove(QLatin1Char('{')).remove(QLatin1Char('}'));
+}
diff --git a/examples/bluetooth/lowenergyscanner/serviceinfo.h b/examples/bluetooth/lowenergyscanner/serviceinfo.h
new file mode 100644
index 00000000..1d35b1b7
--- /dev/null
+++ b/examples/bluetooth/lowenergyscanner/serviceinfo.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
+** of its contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef SERVICEINFO_H
+#define SERVICEINFO_H
+#include <QtBluetooth/QLowEnergyService>
+
+class ServiceInfo: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString serviceName READ getName NOTIFY serviceChanged)
+ Q_PROPERTY(QString serviceUuid READ getUuid NOTIFY serviceChanged)
+ Q_PROPERTY(QString serviceType READ getType NOTIFY serviceChanged)
+public:
+ ServiceInfo();
+ ServiceInfo(QLowEnergyService *service);
+ QLowEnergyService *service() const;
+ QString getUuid() const;
+ QString getName() const;
+ QString getType() const;
+
+Q_SIGNALS:
+ void serviceChanged();
+
+private:
+ QLowEnergyService *m_service;
+};
+
+#endif // SERVICEINFO_H
diff --git a/qtconnectivity.pro b/qtconnectivity.pro
index e2a73944..365c4104 100644
--- a/qtconnectivity.pro
+++ b/qtconnectivity.pro
@@ -1,5 +1,6 @@
load(configure)
qtCompileTest(bluez)
+qtCompileTest(bluez_le)
qtCompileTest(btapi10_2_1)
qtCompileTest(libbb2)
load(qt_parts)
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 9caaf794..719d7ed5 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -2,6 +2,7 @@ TARGET = QtBluetooth
QT = core
QT_PRIVATE = concurrent
+
QMAKE_DOCS = $$PWD/doc/qtbluetooth.qdocconf
OTHER_FILES += doc/src/*.qdoc # show .qdoc files in Qt Creator
@@ -22,7 +23,12 @@ PUBLIC_HEADERS += \
qbluetoothlocaldevice.h \
qbluetoothtransfermanager.h \
qbluetoothtransferrequest.h \
- qbluetoothtransferreply.h
+ qlowenergyserviceinfo.h \
+ qlowenergyservice.h \
+ qlowenergycharacteristic.h \
+ qlowenergydescriptor.h \
+ qbluetoothtransferreply.h \
+ qlowenergycontroller.h
PRIVATE_HEADERS += \
qbluetoothaddress_p.h\
@@ -36,7 +42,10 @@ PRIVATE_HEADERS += \
qbluetoothtransferreply_p.h \
qbluetoothtransferrequest_p.h \
qprivatelinearbuffer_p.h \
- qbluetoothlocaldevice_p.h
+ qbluetoothlocaldevice_p.h \
+ qlowenergyserviceinfo_p.h \
+ qlowenergycontroller_p.h \
+ qlowenergyserviceprivate_p.h
SOURCES += \
qbluetoothaddress.cpp\
@@ -52,7 +61,13 @@ SOURCES += \
qbluetooth.cpp \
qbluetoothtransfermanager.cpp \
qbluetoothtransferrequest.cpp \
- qbluetoothtransferreply.cpp
+ qbluetoothtransferreply.cpp \
+ qlowenergyserviceinfo.cpp \
+ qlowenergyservice.cpp \
+ qlowenergycharacteristic.cpp \
+ qlowenergydescriptor.cpp \
+ qlowenergycontroller.cpp \
+ qlowenergyserviceprivate.cpp
config_bluez:qtHaveModule(dbus) {
QT *= dbus
@@ -70,16 +85,29 @@ config_bluez:qtHaveModule(dbus) {
qbluetoothsocket_bluez.cpp \
qbluetoothserver_bluez.cpp \
qbluetoothlocaldevice_bluez.cpp \
- qbluetoothtransferreply_bluez.cpp
+ qbluetoothtransferreply_bluez.cpp \
+
+
+ # old versions of Bluez do not have the required BTLE symbols
+ config_bluez_le {
+ SOURCES += \
+ qlowenergycontroller_bluez.cpp
+ } else {
+ message("Bluez version is too old to support Bluetooth Low Energy.")
+ message("Only classic Bluetooth will be available.")
+ DEFINES += QT_BLUEZ_NO_BTLE
+ SOURCES += \
+ qlowenergycontroller_p.cpp
+ }
} else:CONFIG(blackberry) {
DEFINES += QT_QNX_BLUETOOTH
include(qnx/qnx.pri)
+ LIBS += -lbtapi
config_btapi10_2_1 {
DEFINES += QT_QNX_BT_BLUETOOTH
- LIBS += -lbtapi
}
PRIVATE_HEADERS += \
@@ -92,7 +120,8 @@ config_bluez:qtHaveModule(dbus) {
qbluetoothservicediscoveryagent_qnx.cpp \
qbluetoothsocket_qnx.cpp \
qbluetoothserver_qnx.cpp \
- qbluetoothtransferreply_qnx.cpp
+ qbluetoothtransferreply_qnx.cpp \
+ qlowenergycontroller_p.cpp
} else:android:!android-no-sdk {
include(android/android.pri)
@@ -113,7 +142,8 @@ config_bluez:qtHaveModule(dbus) {
qbluetoothserviceinfo_android.cpp \
qbluetoothservicediscoveryagent_android.cpp \
qbluetoothsocket_android.cpp \
- qbluetoothserver_android.cpp
+ qbluetoothserver_android.cpp \
+ qlowenergycontroller_p.cpp
} else {
message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.")
@@ -124,12 +154,11 @@ config_bluez:qtHaveModule(dbus) {
qbluetoothserviceinfo_p.cpp \
qbluetoothservicediscoveryagent_p.cpp \
qbluetoothsocket_p.cpp \
- qbluetoothserver_p.cpp
-
+ qbluetoothserver_p.cpp \
+ qlowenergycontroller_p.cpp
}
OTHER_FILES +=
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
-
diff --git a/src/bluetooth/bluez/bluez_data_p.h b/src/bluetooth/bluez/bluez_data_p.h
index 822f53b9..70b80ec3 100644
--- a/src/bluetooth/bluez/bluez_data_p.h
+++ b/src/bluetooth/bluez/bluez_data_p.h
@@ -44,6 +44,7 @@
#include <QtCore/qglobal.h>
#include <sys/socket.h>
+#include <QtBluetooth/QBluetoothUuid>
#define BTPROTO_L2CAP 0
#define BTPROTO_RFCOMM 3
@@ -64,6 +65,27 @@
#define L2CAP_LM_TRUSTED 0x0008
#define L2CAP_LM_SECURE 0x0020
+#define BDADDR_LE_PUBLIC 0x01
+
+/* Byte order conversions */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htobs(d) (d)
+#define htobl(d) (d)
+#define htobll(d) (d)
+#define btohs(d) (d)
+#define btohl(d) (d)
+#define btohll(d) (d)
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define htobs(d) bswap_16(d)
+#define htobl(d) bswap_32(d)
+#define htobll(d) bswap_64(d)
+#define btohs(d) bswap_16(d)
+#define btohl(d) bswap_32(d)
+#define btohll(d) bswap_64(d)
+#else
+#error "Unknown byte order"
+#endif
+
// Bluetooth address
typedef struct {
quint8 b[6];
@@ -75,6 +97,9 @@ struct sockaddr_l2 {
unsigned short l2_psm;
bdaddr_t l2_bdaddr;
unsigned short l2_cid;
+#if !defined(QT_BLUEZ_NO_BTLE)
+ quint8 l2_bdaddr_type;
+#endif
};
// RFCOMM socket
@@ -84,4 +109,65 @@ struct sockaddr_rc {
quint8 rc_channel;
};
+// Bt Low Energy related
+
+#define bt_get_unaligned(ptr) \
+({ \
+ struct __attribute__((packed)) { \
+ __typeof__(*(ptr)) __v; \
+ } *__p = (__typeof__(__p)) (ptr); \
+ __p->__v; \
+})
+
+#define bt_put_unaligned(val, ptr) \
+do { \
+ struct __attribute__((packed)) { \
+ __typeof__(*(ptr)) __v; \
+ } *__p = (__typeof__(__p)) (ptr); \
+ __p->__v = (val); \
+} while (0)
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+static inline void btoh128(const quint128 *src, quint128 *dst)
+{
+ memcpy(dst, src, sizeof(quint128));
+}
+
+static inline void ntoh128(const quint128 *src, quint128 *dst)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ dst->data[15 - i] = src->data[i];
+}
+
+static inline uint16_t bt_get_le16(const void *ptr)
+{
+ return bt_get_unaligned((const uint16_t *) ptr);
+}
+#elif __BYTE_ORDER == __BIG_ENDIAN
+static inline uint16_t bt_get_le16(const void *ptr)
+{
+ return bswap_16(bt_get_unaligned((const uint16_t *) ptr));
+}
+
+static inline void btoh128(const quint128 *src, quint128 *dst)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ dst->data[15 - i] = src->data[i];
+}
+
+static inline void ntoh128(const quint128 *src, quint128 *dst)
+{
+ memcpy(dst, src, sizeof(quint128));
+}
+#else
+#error "Unknown byte order"
+#endif
+
+#define hton128(x, y) ntoh128(x, y)
+
#endif // BLUEZ_DATA_P_H
diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc
index fe9322e9..afdf2600 100644
--- a/src/bluetooth/doc/src/bluetooth-index.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-index.qdoc
@@ -77,6 +77,8 @@ import statement in your \c .qml file:
\li \l {scanner}{QML Bluetooth Scanner}
\li \l {picturetransfer}{QML Bluetooth Picture Push}
\li \l {pingpong}{QML Bluetooth PingPong}
+ \li \l {lowenergyscanner}{Bluetooth Low Energy Scanner}
+ \li \l {heartlistener}{Heart Listener}
\endlist
\li C++
\list
diff --git a/src/bluetooth/doc/src/bluetooth-overview.qdoc b/src/bluetooth/doc/src/bluetooth-overview.qdoc
index f8040fb8..e20a1d22 100644
--- a/src/bluetooth/doc/src/bluetooth-overview.qdoc
+++ b/src/bluetooth/doc/src/bluetooth-overview.qdoc
@@ -29,7 +29,8 @@
\ingroup technology-apis
\title Qt Bluetooth Overview
\page qtbluetooth-overview.html
-\brief The Qt Bluetooth API enables connectivity with other Bluetooth enabled devices.
+\brief The Qt Bluetooth API enables connectivity with other regular Bluetooth
+ and Bluetooth Low Energy enabled devices.
\tableofcontents
@@ -41,6 +42,9 @@
\li Push files to remote devices using the OBEX Object Push Profile (OPP)
\li Connect to remote devices through a RFCOMM channel using the Serial Port Profile (SPP).
\li Create a RFCOMM server that allows incoming connections using SPP.
+ \li Retrieve specification about Bluetooth Low Energy device.
+ \li Connect to Bluetooth Low Energy device.
+ \li Receive advertisement from Bluetooth Low Energy device.
\endlist
Note that the Object Push Profile is not supported on Android.
@@ -103,4 +107,66 @@
\l QBluetoothSocket classes. A good example to start with SPP is the \l{btchat}{Bluetooth Chat}
example.
+ \section1 Bluetooth Low Energy
+
+ Bluetooth Low Energy (in later text BLE), also known as Bluetooth Smart is a wireless computer
+ network technology, which was officially introduced in 2011. It works at the same,
+ 2,4HGz frequency, as ”classic” Bluetooth. The main difference is, as stated by its technology name,
+ low energy consumption. It provides an opportunity for BLE devices to operate for months,
+ even years, on coin-cell batteries. This technology was introduced with Bluetooth v 4.0
+ and devices which support this technology are called Bluetooth Smart Ready Devices.
+ The key features of technology are:
+ \list
+ \li Ultra-low peak, average and idle mode power consumption
+ \li Ability to run for years on standard, coin-cell batteries
+ \li Low cost
+ \li Multi-vendor interoperability
+ \li Enhanced range
+ \endlist
+
+ BLE uses a client-server architecture. The server (BLE device) offers services (temperature,
+ heart rate or any other measurements) and advertises them. The client (PC, smartphone
+ or any other Bluetooth Smart Ready device) connects to the server and reads the values
+ advertised by the server. The BLE API is based on GATT (Generic Attribute Profile) concepts.
+ GATT commands are initiated by the client, and the server processes them. Each command is
+ usually answered by a reply.
+
+ Each BLE service may consist of one or more characteristics. A characteristic
+ contains data and can be further described by descriptors, which provide additional
+ information or means of manipulating the characteristic. All services, characteristics and
+ descriptors are recognized by their 128bits UUIDs. Further details on known uuids can be found
+ in \l QBluetoothUuid.
+
+ To be able to read and write characteristics, it is necessary to connect to the LE service.
+
+ \snippet heartlistener/heartrate.cpp Connect signals
+
+ We start a service discovery with a \l QBluetoothServiceDiscoveryAgent class and connect its
+ \l {QBluetoothServiceDiscoveryAgent::}{serviceDiscovered()} signal. Within the receiving slot we connect to the desired service.
+ \l QLowEnergyController is used to connect or disconnect to services, emits service-related value changes
+ and propagates errors in relation to the service management.
+
+ Even though it is possible to connect to an LE service before the service scan is done,
+ it is advisable to delay until after the service search has finished.
+
+ \snippet heartlistener/heartrate.cpp Connecting to service
+
+ In the code example above, the desired characteristics is of type
+ \l {QBluetoothUuid::HeartRateMeasurement}{HeartRateMeasurement}. Since the application measures
+ the heart rate changes, it must enable change notifications for the characteristic.
+ Note that not all characteristics provide change notifications. Since the HeartRate characteristic
+ has been standardized it is possible to assume that notifications can be received. Ultimately
+ \l QLowEnergyCharacteristicInfo::isNotificationCharacteristic() must return \c true to
+ verify the availability of notifications.
+
+ Finally, we process the value of the HeartRate characteristic, as per Bluetooth Low Energy standard:
+
+ \snippet heartlistener/heartrate.cpp Reading value
+
+ In general a characterisitic value is a series of hexadecimal numbers. The precise interpretation of
+ those hexadecimal numbers depends on the characteristic type and its value structure.
+ A significant number has been standardized by the
+ \l {https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx}{Bluetooth SIG} whereas others
+ may follow a custom protocol. The above code snippet demonstrates how to the read the standardized
+ HeartRate value.
*/
diff --git a/src/bluetooth/doc/src/examples.qdoc b/src/bluetooth/doc/src/examples.qdoc
index f9965075..f53d7127 100644
--- a/src/bluetooth/doc/src/examples.qdoc
+++ b/src/bluetooth/doc/src/examples.qdoc
@@ -69,6 +69,13 @@
\row
\li \l{picturetransfer}{QML Picture Push Example}
\li A QML application that transfers pictures between Bluetooth devices.
+ \row
+ \li \l{lowenergyscanner}{QML Bluetooth Low Energy Scanner}
+ \li Scan for Bluetooth Low Energy devices, services and characteristics.
+ \row
+ \li \l{heartlistener}{QML Bluetooth Low Energy Heart Listener}
+ \li Connect to the Bluetooth Low Energy heart belt and receive
+ measurements.
\endtable
*/
diff --git a/src/bluetooth/gatoattclient.cpp b/src/bluetooth/gatoattclient.cpp
new file mode 100644
index 00000000..abf3a760
--- /dev/null
+++ b/src/bluetooth/gatoattclient.cpp
@@ -0,0 +1,617 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QDataStream>
+#include <QtCore/QDebug>
+
+#include "gatoattclient.h"
+#include "helpers.h"
+
+#define PROTOCOL_DEBUG 0
+
+#define ATT_CID 4
+#define ATT_PSM 31
+
+#define ATT_DEFAULT_LE_MTU 23
+#define ATT_MAX_LE_MTU 0x200
+
+QT_BEGIN_NAMESPACE
+
+enum AttOpcode {
+ AttOpNone = 0,
+ AttOpErrorResponse = 0x1,
+ AttOpExchangeMTURequest = 0x2,
+ AttOpExchangeMTUResponse = 0x3,
+ AttOpFindInformationRequest = 0x4,
+ AttOpFindInformationResponse = 0x5,
+ AttOpFindByTypeValueRequest = 0x6,
+ AttOpFindByTypeValueResponse = 0x7,
+ AttOpReadByTypeRequest = 0x8,
+ AttOpReadByTypeResponse = 0x9,
+ AttOpReadRequest = 0xA,
+ AttOpReadResponse = 0xB,
+ AttOpReadBlobRequest = 0xC,
+ AttOpReadBlobResponse = 0xD,
+ AttOpReadMultipleRequest = 0xE,
+ AttOpReadMultipleResponse = 0xF,
+ AttOpReadByGroupTypeRequest = 0x10,
+ AttOpReadByGroupTypeResponse = 0x11,
+ AttOpWriteRequest = 0x12,
+ AttOpWriteResponse = 0x13,
+ AttOpWriteCommand = 0x52,
+ AttOpPrepareWriteRequest = 0x16,
+ AttOpPrepareWriteResponse = 0x17,
+ AttOpExecuteWriteRequest = 0x18,
+ AttOpExecuteWriteResponse = 0x19,
+ AttOpHandleValueNotification = 0x1B,
+ AttOpHandleValueIndication = 0x1D,
+ AttOpHandleValueConfirmation = 0x1E,
+ AttOpSignedWriteCommand = 0xD2
+};
+
+static QByteArray remove_method_signature(const char *sig)
+{
+ const char* bracketPosition = strchr(sig, '(');
+ if (!bracketPosition || !(sig[0] >= '0' && sig[0] <= '3')) {
+ qWarning("Invalid slot specification");
+ return QByteArray();
+ }
+ return QByteArray(sig + 1, bracketPosition - 1 - sig);
+}
+
+GatoAttClient::GatoAttClient(QObject *parent) :
+ QObject(parent), socket(new GatoSocket(this)), cur_mtu(ATT_DEFAULT_LE_MTU), next_id(1)
+{
+ connect(socket, SIGNAL(connected()), SLOT(handleSocketConnected()));
+ connect(socket, SIGNAL(disconnected()), SLOT(handleSocketDisconnected()));
+ connect(socket, SIGNAL(readyRead()), SLOT(handleSocketReadyRead()));
+}
+
+GatoAttClient::~GatoAttClient()
+{
+}
+
+GatoSocket::State GatoAttClient::state() const
+{
+ return socket->state();
+}
+
+bool GatoAttClient::connectTo(const GatoAddress &addr)
+{
+ return socket->connectTo(addr, ATT_CID);
+}
+
+void GatoAttClient::close()
+{
+ socket->close();
+}
+
+int GatoAttClient::mtu() const
+{
+ return cur_mtu;
+}
+
+uint GatoAttClient::request(int opcode, const QByteArray &data, QObject *receiver, const char *member)
+{
+ Request req;
+ req.id = next_id++;
+ req.opcode = opcode;
+ req.pkt = data;
+ req.pkt.prepend(static_cast<char>(opcode));
+ req.receiver = receiver;
+ req.member = remove_method_signature(member);
+
+ pending_requests.enqueue(req);
+
+ if (pending_requests.size() == 1) {
+ // So we can just send this request instead of waiting for others to complete
+ sendARequest();
+ }
+
+ return req.id;
+}
+
+void GatoAttClient::cancelRequest(uint id)
+{
+ QQueue<Request>::iterator it = pending_requests.begin();
+ while (it != pending_requests.end()) {
+ if (it->id == id) {
+ it = pending_requests.erase(it);
+ } else {
+ ++it;
+ }
+ }
+}
+
+uint GatoAttClient::requestExchangeMTU(quint16 client_mtu, QObject *receiver, const char *member)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << client_mtu;
+
+ return request(AttOpExchangeMTURequest, data, receiver, member);
+}
+
+uint GatoAttClient::requestFindInformation(GatoHandle start, GatoHandle end, QObject *receiver, const char *member)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << start << end;
+
+ return request(AttOpFindInformationRequest, data, receiver, member);
+}
+
+uint GatoAttClient::requestFindByTypeValue(GatoHandle start, GatoHandle end, const GatoUUID &uuid, const QByteArray &value, QObject *receiver, const char *member)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << start << end;
+
+ bool uuid16_ok;
+ quint16 uuid16 = uuid.toUInt16(&uuid16_ok);
+ if (uuid16_ok) {
+ s << uuid16;
+ } else {
+ qWarning() << "FindByTypeValue does not support UUIDs other than UUID16";
+ return -1;
+ }
+
+ s << value;
+
+ return request(AttOpFindByTypeValueRequest, data, receiver, member);
+}
+
+uint GatoAttClient::requestReadByType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << start << end;
+ write_gatouuid(s, uuid, true, false);
+
+ return request(AttOpReadByTypeRequest, data, receiver, member);
+}
+
+uint GatoAttClient::requestRead(GatoHandle handle, QObject *receiver, const char *member)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << handle;
+
+ return request(AttOpReadRequest, data, receiver, member);
+}
+
+uint GatoAttClient::requestReadByGroupType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << start << end;
+ write_gatouuid(s, uuid, true, false);
+
+ return request(AttOpReadByGroupTypeRequest, data, receiver, member);
+}
+
+uint GatoAttClient::requestWrite(GatoHandle handle, const QByteArray &value, QObject *receiver, const char *member)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << handle;
+ s.writeRawData(value.constData(), value.length());
+
+ return request(AttOpWriteRequest, data, receiver, member);
+}
+
+void GatoAttClient::command(int opcode, const QByteArray &data)
+{
+ QByteArray packet = data;
+ packet.prepend(static_cast<char>(opcode));
+
+ socket->send(packet);
+
+#if PROTOCOL_DEBUG
+ qDebug() << "Wrote" << packet.size() << "bytes (command)" << packet.toHex();
+#endif
+}
+
+void GatoAttClient::commandWrite(GatoHandle handle, const QByteArray &value)
+{
+ QByteArray data;
+ QDataStream s(&data, QIODevice::WriteOnly);
+ s.setByteOrder(QDataStream::LittleEndian);
+ s << handle;
+ s.writeRawData(value.constData(), value.length());
+
+ command(AttOpWriteCommand, data);
+}
+
+void GatoAttClient::sendARequest()
+{
+ if (pending_requests.isEmpty()) {
+ return;
+ }
+
+ Request &req = pending_requests.head();
+ socket->send(req.pkt);
+
+#if PROTOCOL_DEBUG
+ qDebug() << "Wrote" << req.pkt.size() << "bytes (request)" << req.pkt.toHex();
+#endif
+}
+
+bool GatoAttClient::handleEvent(const QByteArray &event)
+{
+ const char *data = event.constData();
+ quint8 opcode = event[0];
+ GatoHandle handle;
+
+ switch (opcode) {
+ case AttOpHandleValueNotification:
+ handle = read_le<GatoHandle>(&data[1]);
+ emit attributeUpdated(handle, event.mid(3), false);
+ return true;
+ case AttOpHandleValueIndication:
+ handle = read_le<GatoHandle>(&data[1]);
+
+ // Send the confirmation back
+ command(AttOpHandleValueConfirmation, QByteArray());
+
+ emit attributeUpdated(handle, event.mid(3), true);
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool GatoAttClient::handleResponse(const Request &req, const QByteArray &response)
+{
+ // If we know the request, we can provide a decoded answer
+ switch (req.opcode) {
+ case AttOpExchangeMTURequest:
+ if (response[0] == AttOpExchangeMTUResponse) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(quint16, read_le<quint16>(response.constData() + 1)));
+ }
+ return true;
+ } else if (response[0] == AttOpErrorResponse && response[1] == AttOpExchangeMTURequest) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(quint16, 0));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case AttOpFindInformationRequest:
+ if (response[0] == AttOpFindInformationResponse) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::InformationData>, parseInformationData(response.mid(1))));
+ }
+ return true;
+ } else if (response[0] == AttOpErrorResponse && response[1] == AttOpFindInformationRequest) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::InformationData>, QList<InformationData>()));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case AttOpFindByTypeValueRequest:
+ if (response[0] == AttOpFindByTypeValueResponse) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::HandleInformation>, parseHandleInformation(response.mid(1))));
+ }
+ return true;
+ } else if (response[0] == AttOpErrorResponse && response[1] == AttOpFindByTypeValueRequest) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::HandleInformation>, QList<HandleInformation>()));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case AttOpReadByTypeRequest:
+ if (response[0] == AttOpReadByTypeResponse) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::AttributeData>, parseAttributeData(response.mid(1))));
+ }
+ return true;
+ } else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadByTypeRequest) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::AttributeData>, QList<AttributeData>()));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case AttOpReadRequest:
+ if (response[0] == AttOpReadResponse) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QByteArray, response.mid(1)));
+ }
+ return true;
+ } else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadRequest) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QByteArray, QByteArray()));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case AttOpReadByGroupTypeRequest:
+ if (response[0] == AttOpReadByGroupTypeResponse) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::AttributeGroupData>, parseAttributeGroupData(response.mid(1))));
+ }
+ return true;
+ } else if (response[0] == AttOpErrorResponse && response[1] == AttOpReadByGroupTypeRequest) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(QList<GatoAttClient::AttributeGroupData>, QList<AttributeGroupData>()));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ case AttOpWriteRequest:
+ if (response[0] == AttOpWriteResponse) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(bool, true));
+ }
+ return true;
+ } else if (response[0] == AttOpErrorResponse && response[1] == AttOpWriteRequest) {
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(uint, req.id),
+ Q_ARG(bool, false));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ break;
+ default: // Otherwise just send a QByteArray.
+ if (req.receiver) {
+ QMetaObject::invokeMethod(req.receiver, req.member.constData(),
+ Q_ARG(const QByteArray&, response));
+ }
+ return true;
+ }
+}
+
+QList<GatoAttClient::InformationData> GatoAttClient::parseInformationData(const QByteArray &data)
+{
+ const int format = data[0];
+ QList<InformationData> list;
+ int item_len;
+
+ switch (format) {
+ case 1:
+ item_len = 2 + 2;
+ break;
+ case 2:
+ item_len = 2 + 16;
+ break;
+ default:
+ qWarning() << "Unknown InformationData format!";
+ return list;
+ }
+
+ int items = (data.size() - 1) / item_len;
+ list.reserve(items);
+
+ int pos = 1;
+ const char *s = data.constData();
+ for (int i = 0; i < items; i++) {
+ InformationData d;
+ QByteArray uuid;
+ d.handle = read_le<GatoHandle>(&s[pos]);
+ switch (format) {
+ case 1:
+ uuid = data.mid(pos + 2, 2);
+ break;
+ case 2:
+ uuid = data.mid(pos + 2, 16);
+ break;
+ }
+ d.uuid = bytearray_to_gatouuid(uuid);
+
+ list.append(d);
+
+ pos += item_len;
+ }
+
+ return list;
+}
+
+QList<GatoAttClient::HandleInformation> GatoAttClient::parseHandleInformation(const QByteArray &data)
+{
+ const int item_len = 2;
+ const int items = data.size() / item_len;
+ QList<HandleInformation> list;
+ list.reserve(items);
+
+ int pos = 0;
+ const char *s = data.constData();
+ for (int i = 0; i < items; i++) {
+ HandleInformation d;
+ d.start = read_le<GatoHandle>(&s[pos]);
+ d.end = read_le<GatoHandle>(&s[pos + 2]);
+ list.append(d);
+
+ pos += item_len;
+ }
+
+ return list;
+}
+
+QList<GatoAttClient::AttributeData> GatoAttClient::parseAttributeData(const QByteArray &data)
+{
+ const int item_len = data[0];
+ const int items = (data.size() - 1) / item_len;
+ QList<AttributeData> list;
+ list.reserve(items);
+
+ int pos = 1;
+ const char *s = data.constData();
+ for (int i = 0; i < items; i++) {
+ AttributeData d;
+ d.handle = read_le<GatoHandle>(&s[pos]);
+ d.value = data.mid(pos + 2, item_len - 2);
+ list.append(d);
+
+ pos += item_len;
+ }
+
+ return list;
+}
+
+QList<GatoAttClient::AttributeGroupData> GatoAttClient::parseAttributeGroupData(const QByteArray &data)
+{
+ const int item_len = data[0];
+ const int items = (data.size() - 1) / item_len;
+ QList<AttributeGroupData> list;
+ list.reserve(items);
+
+ int pos = 1;
+ const char *s = data.constData();
+ for (int i = 0; i < items; i++) {
+ AttributeGroupData d;
+ d.start = read_le<GatoHandle>(&s[pos]);
+ d.end = read_le<GatoHandle>(&s[pos + 2]);
+ d.value = data.mid(pos + 4, item_len - 4);
+ list.append(d);
+
+ pos += item_len;
+ }
+
+ return list;
+}
+
+void GatoAttClient::handleSocketConnected()
+{
+ requestExchangeMTU(ATT_MAX_LE_MTU, this, SLOT(handleServerMTU(quint16)));
+ emit connected();
+}
+
+void GatoAttClient::handleSocketDisconnected()
+{
+ emit disconnected();
+}
+
+void GatoAttClient::handleSocketReadyRead()
+{
+ QByteArray pkt = socket->receive();
+ if (!pkt.isEmpty()) {
+#if PROTOCOL_DEBUG
+ qDebug() << "Received" << pkt.size() << "bytes" << pkt.toHex();
+#endif
+
+ // Check if it is an event
+ if (handleEvent(pkt)) {
+ return;
+ }
+
+ // Otherwise, if we have a request waiting, check if this answers it
+ if (!pending_requests.isEmpty()) {
+ if (handleResponse(pending_requests.head(), pkt)) {
+ pending_requests.dequeue();
+ // Proceed to next request
+ if (!pending_requests.isEmpty()) {
+ sendARequest();
+ }
+ return;
+ }
+ }
+
+ qDebug() << "No idea what this packet ("
+ << QString("0x%1").arg(uint(pkt.at(0)), 2, 16, QLatin1Char('0'))
+ << ") is";
+ }
+}
+
+void GatoAttClient::handleServerMTU(uint req, quint16 server_mtu)
+{
+ Q_UNUSED(req);
+ if (server_mtu) {
+ cur_mtu = server_mtu;
+ if (cur_mtu < ATT_DEFAULT_LE_MTU) {
+ cur_mtu = ATT_DEFAULT_LE_MTU;
+ }
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/gatoattclient.h b/src/bluetooth/gatoattclient.h
new file mode 100644
index 00000000..6222f863
--- /dev/null
+++ b/src/bluetooth/gatoattclient.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GATOATTCLIENT_H
+#define GATOATTCLIENT_H
+
+#include <QtCore/QObject>
+#include <QtCore/QQueue>
+#include "gatosocket.h"
+#include "gatouuid.h"
+
+QT_BEGIN_NAMESPACE
+
+class GatoAttClient : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit GatoAttClient(QObject *parent = 0);
+ ~GatoAttClient();
+
+ GatoSocket::State state() const;
+
+ bool connectTo(const GatoAddress& addr);
+ void close();
+
+ struct InformationData
+ {
+ GatoHandle handle;
+ GatoUUID uuid;
+ };
+ struct HandleInformation
+ {
+ GatoHandle start;
+ GatoHandle end;
+ };
+ struct AttributeData
+ {
+ GatoHandle handle;
+ QByteArray value;
+ };
+ struct AttributeGroupData
+ {
+ GatoHandle start;
+ GatoHandle end;
+ QByteArray value;
+ };
+
+ int mtu() const;
+
+ uint request(int opcode, const QByteArray &data, QObject *receiver, const char *member);
+ uint requestExchangeMTU(quint16 client_mtu, QObject *receiver, const char *member);
+ uint requestFindInformation(GatoHandle start, GatoHandle end, QObject *receiver, const char *member);
+ uint requestFindByTypeValue(GatoHandle start, GatoHandle end, const GatoUUID &uuid, const QByteArray& value, QObject *receiver, const char *member);
+ uint requestReadByType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member);
+ uint requestRead(GatoHandle handle, QObject *receiver, const char *member);
+ uint requestReadByGroupType(GatoHandle start, GatoHandle end, const GatoUUID &uuid, QObject *receiver, const char *member);
+ uint requestWrite(GatoHandle handle, const QByteArray &value, QObject *receiver, const char *member);
+ void cancelRequest(uint id);
+
+ void command(int opcode, const QByteArray &data);
+ void commandWrite(GatoHandle handle, const QByteArray &value);
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+
+ void attributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed);
+
+private:
+ struct Request
+ {
+ uint id;
+ quint8 opcode;
+ QByteArray pkt;
+ QObject *receiver;
+ QByteArray member;
+ };
+
+ void sendARequest();
+ bool handleEvent(const QByteArray &event);
+ bool handleResponse(const Request& req, const QByteArray &response);
+
+ QList<InformationData> parseInformationData(const QByteArray &data);
+ QList<HandleInformation> parseHandleInformation(const QByteArray &data);
+ QList<AttributeData> parseAttributeData(const QByteArray &data);
+ QList<AttributeGroupData> parseAttributeGroupData(const QByteArray &data);
+
+private Q_SLOTS:
+ void handleSocketConnected();
+ void handleSocketDisconnected();
+ void handleSocketReadyRead();
+
+ void handleServerMTU(uint req, quint16 server_mtu);
+
+private:
+ GatoSocket *socket;
+ quint16 cur_mtu;
+ uint next_id;
+ QQueue<Request> pending_requests;
+};
+
+QT_END_NAMESPACE
+
+#endif // GATOATTCLIENT_H
diff --git a/src/bluetooth/gatoperipheral.cpp b/src/bluetooth/gatoperipheral.cpp
new file mode 100644
index 00000000..b44efc26
--- /dev/null
+++ b/src/bluetooth/gatoperipheral.cpp
@@ -0,0 +1,874 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QDebug>
+
+#include <assert.h>
+#include <bluetooth/bluetooth.h>
+
+#include "gatoperipheral_p.h"
+#include "gatoaddress.h"
+#include "gatouuid.h"
+#include "helpers.h"
+
+QT_BEGIN_NAMESPACE
+
+enum EIRDataFields {
+ EIRFlags = 0x01,
+ EIRIncompleteUUID16List = 0x02,
+ EIRCompleteUUID16List = 0x03,
+ EIRIncompleteUUID32List = 0x04,
+ EIRCompleteUUID32List = 0x05,
+ EIRIncompleteUUID128List = 0x06,
+ EIRCompleteUUID128List = 0x07,
+ EIRIncompleteLocalName = 0x08,
+ EIRCompleteLocalName = 0x09,
+ EIRTxPowerLevel = 0x0A,
+ EIRDeviceClass = 0x0D,
+ EIRSecurityManagerTKValue = 0x10,
+ EIRSecurityManagerOutOfBandFlags = 0x11,
+ EIRSolicitedUUID128List = 0x15
+};
+
+GatoPeripheral::GatoPeripheral(const GatoAddress &addr, QObject *parent) :
+ QObject(parent), d_ptr(new GatoPeripheralPrivate(this))
+{
+ Q_D(GatoPeripheral);
+ d->addr = addr;
+ d->att = new GatoAttClient(this);
+
+ connect(d->att, SIGNAL(connected()), d, SLOT(handleAttConnected()));
+ connect(d->att, SIGNAL(disconnected()), d, SLOT(handleAttDisconnected()));
+ connect(d->att, SIGNAL(attributeUpdated(GatoHandle,QByteArray,bool)), d, SLOT(handleAttAttributeUpdated(GatoHandle,QByteArray,bool)));
+}
+
+GatoPeripheral::~GatoPeripheral()
+{
+ if (state() != StateDisconnected) {
+ disconnect();
+ }
+ delete d_ptr;
+}
+
+GatoPeripheral::State GatoPeripheral::state() const
+{
+ Q_D(const GatoPeripheral);
+ return static_cast<State>(d->att->state());
+}
+
+GatoAddress GatoPeripheral::address() const
+{
+ Q_D(const GatoPeripheral);
+ return d->addr;
+}
+
+QString GatoPeripheral::name() const
+{
+ Q_D(const GatoPeripheral);
+ return d->name;
+}
+
+QList<GatoService> GatoPeripheral::services() const
+{
+ Q_D(const GatoPeripheral);
+ return d->services.values();
+}
+
+void GatoPeripheral::parseEIR(quint8 data[], int len)
+{
+ Q_D(GatoPeripheral);
+
+ int pos = 0;
+ while (pos < len) {
+ int item_len = data[pos];
+ pos++;
+ if (item_len == 0) break;
+ int type = data[pos];
+ assert(pos + item_len <= len);
+ switch (type) {
+ case EIRFlags:
+ d->parseEIRFlags(&data[pos + 1], item_len - 1);
+ break;
+ case EIRIncompleteUUID16List:
+ d->parseEIRUUIDs(16/8, false, &data[pos + 1], item_len - 1);
+ break;
+ case EIRCompleteUUID16List:
+ d->parseEIRUUIDs(16/8, true, &data[pos + 1], item_len - 1);
+ break;
+ case EIRIncompleteUUID32List:
+ d->parseEIRUUIDs(32/8, false, &data[pos + 1], item_len - 1);
+ break;
+ case EIRCompleteUUID32List:
+ d->parseEIRUUIDs(32/8, true, &data[pos + 1], item_len - 1);
+ break;
+ case EIRIncompleteUUID128List:
+ d->parseEIRUUIDs(128/8, false, &data[pos + 1], item_len - 1);
+ break;
+ case EIRCompleteUUID128List:
+ d->parseEIRUUIDs(128/8, true, &data[pos + 1], item_len - 1);
+ break;
+ case EIRIncompleteLocalName:
+ d->parseName(false, &data[pos + 1], item_len - 1);
+ break;
+ case EIRCompleteLocalName:
+ d->parseName(true, &data[pos + 1], item_len - 1);
+ break;
+ case EIRTxPowerLevel:
+ case EIRSolicitedUUID128List:
+ qDebug() << "Unhandled EIR data type" << type;
+ break;
+ default:
+ qWarning() << "Unknown EIR data type" << type;
+ break;
+ }
+
+ pos += item_len;
+ }
+
+ assert(pos == len);
+}
+
+bool GatoPeripheral::advertisesService(const GatoUUID &uuid) const
+{
+ Q_D(const GatoPeripheral);
+ return d->service_uuids.contains(uuid);
+}
+
+void GatoPeripheral::connectPeripheral()
+{
+ Q_D(GatoPeripheral);
+ if (d->att->state() != GatoSocket::StateDisconnected) {
+ qDebug() << "Already connecting";
+ return;
+ }
+
+ d->att->connectTo(d->addr);
+}
+
+void GatoPeripheral::disconnectPeripheral()
+{
+ Q_D(GatoPeripheral);
+
+ d->att->close();
+}
+
+void GatoPeripheral::discoverServices()
+{
+ Q_D(GatoPeripheral);
+ if (!d->complete_services && state() == StateConnected) {
+ d->clearServices();
+ d->att->requestReadByGroupType(0x0001, 0xFFFF, GatoUUID::GattPrimaryService,
+ d, SLOT(handlePrimary(QList<GatoAttClient::AttributeGroupData>)));
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::discoverServices(const QList<GatoUUID> &serviceUUIDs)
+{
+ Q_D(GatoPeripheral);
+ if (serviceUUIDs.isEmpty()) return;
+ if (state() == StateConnected) {
+ foreach (const GatoUUID& uuid, serviceUUIDs) {
+ QByteArray value = gatouuid_to_bytearray(uuid, true, false);
+ uint req = d->att->requestFindByTypeValue(0x0001, 0xFFFF, GatoUUID::GattPrimaryService, value,
+ d, SLOT(handlePrimaryForService(uint,QList<GatoAttClient::HandleInformation>)));
+ d->pending_primary_reqs.insert(req, uuid);
+ }
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::discoverCharacteristics(const GatoService &service)
+{
+ Q_D(GatoPeripheral);
+
+ if (!d->services.contains(service.startHandle())) {
+ qWarning() << "Unknown service for this peripheral";
+ return;
+ }
+
+ GatoService &our_service = d->services[service.startHandle()];
+
+ if (our_service.startHandle() != service.startHandle() ||
+ our_service.endHandle() != service.endHandle() ||
+ our_service.uuid() != service.uuid()) {
+ qWarning() << "Unknown service for this peripheral";
+ return;
+ }
+
+ if (state() == StateConnected) {
+ GatoHandle start = our_service.startHandle();
+ GatoHandle end = our_service.endHandle();
+
+ d->clearServiceCharacteristics(&our_service);
+
+ uint req = d->att->requestReadByType(start, end, GatoUUID::GattCharacteristic,
+ d, SLOT(handleCharacteristic(QList<GatoAttClient::AttributeData>)));
+ d->pending_characteristic_reqs.insert(req, start);
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::discoverCharacteristics(const GatoService &service, const QList<GatoUUID> &characteristicUUIDs)
+{
+ // TODO There seems to be no way to ask for the peripheral to filter by uuid
+ Q_UNUSED(characteristicUUIDs);
+ discoverCharacteristics(service);
+}
+
+void GatoPeripheral::discoverDescriptors(const GatoCharacteristic &characteristic)
+{
+ Q_D(GatoPeripheral);
+
+ GatoHandle char_handle = characteristic.startHandle();
+ GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
+
+ if (!service_handle) {
+ qWarning() << "Unknown characteristic for this peripheral";
+ return;
+ }
+
+ GatoService &our_service = d->services[service_handle];
+ Q_ASSERT(our_service.containsCharacteristic(char_handle));
+ GatoCharacteristic our_char = our_service.getCharacteristic(char_handle);
+ Q_ASSERT(our_char.startHandle() == char_handle);
+
+ if (state() == StateConnected) {
+ d->clearCharacteristicDescriptors(&our_char);
+ our_service.addCharacteristic(our_char); // Update service with empty descriptors list
+ uint req = d->att->requestFindInformation(our_char.startHandle() + 1, our_char.endHandle(),
+ d, SLOT(handleDescriptors(uint,QList<GatoAttClient::InformationData>)));
+ d->pending_descriptor_reqs.insert(req, char_handle);
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::readValue(const GatoCharacteristic &characteristic)
+{
+ Q_D(GatoPeripheral);
+
+ GatoHandle char_handle = characteristic.startHandle();
+ GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
+
+ if (!service_handle) {
+ qWarning() << "Unknown characteristic for this peripheral";
+ return;
+ }
+
+ GatoService &our_service = d->services[service_handle];
+ Q_ASSERT(our_service.containsCharacteristic(char_handle));
+
+ if (state() == StateConnected) {
+ uint req = d->att->requestRead(characteristic.valueHandle(),
+ d, SLOT(handleCharacteristicRead(uint,QByteArray)));
+ d->pending_characteristic_read_reqs.insert(req, char_handle);
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::readValue(const GatoDescriptor &descriptor)
+{
+ Q_D(GatoPeripheral);
+
+ GatoHandle desc_handle = descriptor.handle();
+ GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle);
+
+ if (!char_handle) {
+ qWarning() << "Unknown descriptor for this peripheral";
+ return;
+ }
+
+ GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
+ Q_ASSERT(service_handle);
+
+ GatoService &our_service = d->services[service_handle];
+ Q_ASSERT(our_service.containsCharacteristic(char_handle));
+
+ if (state() == StateConnected) {
+ uint req = d->att->requestRead(descriptor.handle(),
+ d, SLOT(handleDescriptorRead(uint,QByteArray)));
+ d->pending_descriptor_read_reqs.insert(req, char_handle);
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::writeValue(const GatoCharacteristic &characteristic, const QByteArray &data, WriteType type)
+{
+ Q_D(GatoPeripheral);
+
+ GatoHandle char_handle = characteristic.startHandle();
+ GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
+
+ if (!service_handle) {
+ qWarning() << "Unknown characteristic for this peripheral";
+ return;
+ }
+
+ GatoService &our_service = d->services[service_handle];
+ Q_ASSERT(our_service.containsCharacteristic(char_handle));
+
+ if (state() == StateConnected) {
+ switch (type) {
+ case WriteWithResponse:
+ d->att->requestWrite(characteristic.valueHandle(), data,
+ d, SLOT(handleCharacteristicWrite(uint,bool)));
+ break;
+ case WriteWithoutResponse:
+ d->att->commandWrite(characteristic.valueHandle(), data);
+ break;
+ }
+
+
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::writeValue(const GatoDescriptor &descriptor, const QByteArray &data)
+{
+ Q_D(GatoPeripheral);
+
+ GatoHandle desc_handle = descriptor.handle();
+ GatoHandle char_handle = d->descriptor_to_characteristic.value(desc_handle);
+
+ if (!char_handle) {
+ qWarning() << "Unknown descriptor for this peripheral";
+ return;
+ }
+
+ GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
+ Q_ASSERT(service_handle);
+
+ GatoService &our_service = d->services[service_handle];
+ Q_ASSERT(our_service.containsCharacteristic(char_handle));
+
+ if (state() == StateConnected) {
+ d->att->requestWrite(descriptor.handle(), data,
+ d, SLOT(handleDescriptorWrite(uint,bool)));
+ } else {
+ qWarning() << "Not connected";
+ }
+}
+
+void GatoPeripheral::setNotification(const GatoCharacteristic &characteristic, bool enabled)
+{
+ Q_D(GatoPeripheral);
+
+ GatoHandle char_handle = characteristic.startHandle();
+ GatoHandle service_handle = d->characteristic_to_service.value(char_handle);
+
+ if (!service_handle) {
+ qWarning() << "Unknown characteristic for this peripheral";
+ return;
+ }
+
+ GatoService &our_service = d->services[service_handle];
+ Q_ASSERT(our_service.containsCharacteristic(char_handle));
+ GatoCharacteristic our_char = our_service.getCharacteristic(char_handle);
+
+ if (!(our_char.properties() & GatoCharacteristic::PropertyNotify)) {
+ qWarning() << "Characteristic does not support notifications";
+ return;
+ }
+
+ if (state() != StateConnected) {
+ qWarning() << "Not connected";
+ return;
+ }
+
+ const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration);
+ if (our_char.containsDescriptor(uuid)) {
+ GatoDescriptor desc = our_char.getDescriptor(uuid);
+ d->pending_set_notify.remove(char_handle);
+ writeValue(characteristic, d->genClientCharConfiguration(true, false));
+ } else {
+ d->pending_set_notify[char_handle] = enabled;
+ discoverDescriptors(our_char); // May need to find appropiate descriptor
+ }
+}
+
+GatoPeripheralPrivate::GatoPeripheralPrivate(GatoPeripheral *parent)
+ : QObject(parent), q_ptr(parent),
+ complete_name(false), complete_services(false)
+{
+}
+
+GatoPeripheralPrivate::~GatoPeripheralPrivate()
+{
+ delete att;
+}
+
+void GatoPeripheralPrivate::parseEIRFlags(quint8 data[], int len)
+{
+ Q_UNUSED(data);
+ Q_UNUSED(len);
+ // Nothing to do for now.
+}
+
+void GatoPeripheralPrivate::parseEIRUUIDs(int size, bool complete, quint8 data[], int len)
+{
+ Q_UNUSED(complete);
+
+ if (size != 16/8 && size != 32/8 && size != 128/8) {
+ qWarning() << "Unhandled UUID size: " << size;
+ return;
+ }
+
+ for (int pos = 0; pos < len; pos += size) {
+ char *ptr = reinterpret_cast<char*>(&data[pos]);
+ QByteArray ba = QByteArray::fromRawData(ptr, size/8);
+
+ service_uuids.insert(bytearray_to_gatouuid(ba));
+ }
+}
+
+void GatoPeripheralPrivate::parseName(bool complete, quint8 data[], int len)
+{
+ Q_Q(GatoPeripheral);
+ if (complete || !complete_name) {
+ name = QString::fromUtf8(reinterpret_cast<char*>(data), len);
+ complete_name = complete;
+ emit q->nameChanged();
+ }
+}
+
+GatoCharacteristic GatoPeripheralPrivate::parseCharacteristicValue(const QByteArray &ba)
+{
+ GatoCharacteristic characteristic;
+ const char *data = ba.constData();
+
+ quint8 properties = data[0];
+ characteristic.setProperties(GatoCharacteristic::Properties(properties));
+
+ GatoHandle handle = read_le<quint16>(&data[1]);
+ characteristic.setValueHandle(handle);
+
+ GatoUUID uuid = bytearray_to_gatouuid(ba.mid(3));
+ characteristic.setUuid(uuid);
+
+ return characteristic;
+}
+
+QByteArray GatoPeripheralPrivate::genClientCharConfiguration(bool notification, bool indication)
+{
+ QByteArray ba;
+ ba.resize(sizeof(quint16));
+
+ quint16 val = 0;
+ if (notification)
+ val |= 0x1;
+ if (indication)
+ val |= 0x2;
+
+ write_le<quint16>(val, ba.data());
+
+ return ba;
+}
+
+void GatoPeripheralPrivate::clearServices()
+{
+ characteristic_to_service.clear();
+ value_to_characteristic.clear();
+ descriptor_to_characteristic.clear();
+ services.clear();
+}
+
+void GatoPeripheralPrivate::clearServiceCharacteristics(GatoService *service)
+{
+ QList<GatoCharacteristic> chars = service->characteristics();
+ QList<GatoCharacteristic>::iterator it;
+ for (it = chars.begin(); it != chars.end(); ++it) {
+ clearCharacteristicDescriptors(&*it);
+ characteristic_to_service.remove(it->startHandle());
+ value_to_characteristic.remove(it->valueHandle());
+ }
+ service->clearCharacteristics();
+}
+
+void GatoPeripheralPrivate::clearCharacteristicDescriptors(GatoCharacteristic *characteristic)
+{
+ QList<GatoDescriptor> descs = characteristic->descriptors();
+ foreach (const GatoDescriptor& d, descs) {
+ descriptor_to_characteristic.remove(d.handle());
+ }
+ characteristic->clearDescriptors();
+}
+
+void GatoPeripheralPrivate::finishSetNotifyOperations(const GatoCharacteristic &characteristic)
+{
+ Q_Q(GatoPeripheral);
+
+ GatoHandle handle = characteristic.startHandle();
+
+ if (pending_set_notify.contains(handle)) {
+ const GatoUUID uuid(GatoUUID::GattClientCharacteristicConfiguration);
+ bool notify = pending_set_notify.value(handle);
+
+ foreach (const GatoDescriptor &descriptor, characteristic.descriptors()) {
+ if (descriptor.uuid() == uuid) {
+ q->writeValue(descriptor, genClientCharConfiguration(notify, false));
+ }
+ }
+
+ pending_set_notify.remove(handle);
+ }
+}
+
+void GatoPeripheralPrivate::handleAttConnected()
+{
+ Q_Q(GatoPeripheral);
+
+ emit q->connected();
+}
+
+void GatoPeripheralPrivate::handleAttDisconnected()
+{
+ Q_Q(GatoPeripheral);
+
+ // Forget about all pending requests
+ pending_primary_reqs.clear();
+ pending_characteristic_reqs.clear();
+ pending_characteristic_read_reqs.clear();
+ pending_descriptor_reqs.clear();
+ pending_descriptor_read_reqs.clear();
+
+ emit q->disconnected();
+}
+
+void GatoPeripheralPrivate::handleAttAttributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed)
+{
+ Q_Q(GatoPeripheral);
+ Q_UNUSED(confirmed);
+
+ // Let's see if this is a handle we know about.
+ if (value_to_characteristic.contains(handle)) {
+ // Ok, it's a characteristic value.
+ GatoHandle char_handle = value_to_characteristic.value(handle);
+ GatoHandle service_handle = characteristic_to_service.value(char_handle);
+ if (!service_handle) {
+ qWarning() << "Got a notification for a characteristic I don't know about";
+ return;
+ }
+
+ GatoService &service = services[service_handle];
+ GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
+
+ emit q->valueUpdated(characteristic, value);
+ }
+}
+
+void GatoPeripheralPrivate::handlePrimary(uint req, const QList<GatoAttClient::AttributeGroupData> &list)
+{
+ Q_Q(GatoPeripheral);
+ Q_UNUSED(req);
+
+ if (list.isEmpty()) {
+ complete_services = true;
+ emit q->servicesDiscovered();
+ } else {
+ GatoHandle last_handle = 0;
+
+ foreach (const GatoAttClient::AttributeGroupData &data, list) {
+ GatoUUID uuid = bytearray_to_gatouuid(data.value);
+ GatoService service;
+
+ service.setUuid(uuid);
+ service.setStartHandle(data.start);
+ service.setEndHandle(data.end);
+
+ services.insert(data.start, service);
+ service_uuids.insert(uuid);
+
+ last_handle = data.end;
+ }
+
+ // Fetch following attributes
+ att->requestReadByGroupType(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService,
+ this, SLOT(handlePrimary(uint,QList<GatoAttClient::AttributeGroupData>)));
+ }
+}
+
+void GatoPeripheralPrivate::handlePrimaryForService(uint req, const QList<GatoAttClient::HandleInformation> &list)
+{
+ Q_Q(GatoPeripheral);
+
+ GatoUUID uuid = pending_primary_reqs.value(req, GatoUUID());
+ if (uuid.isNull()) {
+ qDebug() << "Got primary for service response for a request I did not make";
+ return;
+ }
+ pending_primary_reqs.remove(req);
+
+ if (list.isEmpty()) {
+ if (pending_primary_reqs.isEmpty()) {
+ emit q->servicesDiscovered();
+ }
+ } else {
+ GatoHandle last_handle = 0;
+
+ foreach (const GatoAttClient::HandleInformation &data, list) {
+ GatoService service;
+
+ service.setUuid(uuid);
+ service.setStartHandle(data.start);
+ service.setEndHandle(data.end);
+
+ services.insert(data.start, service);
+ service_uuids.insert(uuid);
+
+ last_handle = data.end;
+ }
+
+ // Fetch following attributes
+ QByteArray value = gatouuid_to_bytearray(uuid, true, false);
+ uint req = att->requestFindByTypeValue(last_handle + 1, 0xFFFF, GatoUUID::GattPrimaryService, value,
+ this, SLOT(handlePrimaryForService(uint,QList<GatoAttClient::HandleInformation>)));
+ pending_primary_reqs.insert(req, uuid);
+ }
+}
+
+void GatoPeripheralPrivate::handleCharacteristic(uint req, const QList<GatoAttClient::AttributeData> &list)
+{
+ Q_Q(GatoPeripheral);
+
+ GatoHandle service_start = pending_characteristic_reqs.value(req, 0);
+ if (!service_start) {
+ qDebug() << "Got characteristics for a request I did not make";
+ return;
+ }
+ pending_characteristic_reqs.remove(req);
+
+ Q_ASSERT(services.contains(service_start));
+ GatoService &service = services[service_start];
+ Q_ASSERT(service.startHandle() == service_start);
+
+ if (list.isEmpty()) {
+ emit q->characteristicsDiscovered(service);
+ } else {
+ GatoHandle last_handle = 0;
+
+ // If we are continuing a characteristic list, this means the
+ // last service we discovered in the previous iteration was not
+ // the last one, so we have to reduce its endHandle!
+ QList<GatoCharacteristic> cur_chars = service.characteristics();
+ if (!cur_chars.isEmpty()) {
+ GatoCharacteristic &last = cur_chars.back();
+ last.setEndHandle(list.front().handle - 1);
+ service.addCharacteristic(last);
+ }
+
+ for (int i = 0; i < list.size(); i++) {
+ const GatoAttClient::AttributeData &data = list.at(i);
+ GatoCharacteristic characteristic = parseCharacteristicValue(data.value);
+
+ characteristic.setStartHandle(data.handle);
+ if (i + 1 < list.size()) {
+ characteristic.setEndHandle(list.at(i + 1).handle - 1);
+ } else {
+ characteristic.setEndHandle(service.endHandle());
+ }
+
+ service.addCharacteristic(characteristic);
+ characteristic_to_service.insert(data.handle, service_start);
+ value_to_characteristic.insert(characteristic.valueHandle(), data.handle);
+
+ last_handle = data.handle;
+ }
+
+ if (last_handle >= service.endHandle()) {
+ // Already finished, no need to send another request
+ emit q->characteristicsDiscovered(service);
+ return;
+ }
+
+ // Fetch following attributes
+ uint req = att->requestReadByType(last_handle + 1, service.endHandle(), GatoUUID::GattCharacteristic,
+ this, SLOT(handleCharacteristic(uint,QList<GatoAttClient::AttributeData>)));
+ pending_characteristic_reqs.insert(req, service.startHandle());
+ }
+}
+
+void GatoPeripheralPrivate::handleDescriptors(uint req, const QList<GatoAttClient::InformationData> &list)
+{
+ Q_Q(GatoPeripheral);
+
+ GatoHandle char_handle = pending_descriptor_reqs.value(req);
+ if (!char_handle) {
+ qDebug() << "Got descriptor for a request I did not make";
+ return;
+ }
+ pending_descriptor_reqs.remove(req);
+ GatoHandle service_handle = characteristic_to_service.value(char_handle);
+ if (!service_handle) {
+ qWarning() << "Unknown characteristic during descriptor discovery: " << char_handle;
+ return;
+ }
+
+ Q_ASSERT(services.contains(service_handle));
+ GatoService &service = services[service_handle];
+ Q_ASSERT(service.startHandle() == service_handle);
+
+ Q_ASSERT(service.containsCharacteristic(char_handle));
+ GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
+
+ if (list.isEmpty()) {
+ finishSetNotifyOperations(characteristic);
+ emit q->descriptorsDiscovered(characteristic);
+ } else {
+ GatoHandle last_handle = 0;
+
+ foreach (const GatoAttClient::InformationData &data, list) {
+ // Skip the value attribute itself.
+ if (data.handle == characteristic.valueHandle()) continue;
+
+ GatoDescriptor descriptor;
+
+ descriptor.setHandle(data.handle);
+ descriptor.setUuid(data.uuid);
+
+ characteristic.addDescriptor(descriptor);
+
+ service.addCharacteristic(characteristic);
+ descriptor_to_characteristic.insert(data.handle, char_handle);
+
+ last_handle = data.handle;
+ }
+
+ service.addCharacteristic(characteristic);
+
+ if (last_handle >= characteristic.endHandle()) {
+ // Already finished, no need to send another request
+ finishSetNotifyOperations(characteristic);
+ emit q->descriptorsDiscovered(characteristic);
+ return;
+ }
+
+ // Fetch following attributes
+ uint req = att->requestFindInformation(last_handle + 1, characteristic.endHandle(),
+ this, SLOT(handleDescriptors(uint,QList<GatoAttClient::InformationData>)));
+ pending_descriptor_reqs.insert(req, char_handle);
+
+ }
+}
+
+void GatoPeripheralPrivate::handleCharacteristicRead(uint req, const QByteArray &value)
+{
+ Q_Q(GatoPeripheral);
+
+ GatoHandle char_handle = pending_characteristic_read_reqs.value(req);
+ if (!char_handle) {
+ qDebug() << "Got characteristics for a request I did not make";
+ return;
+ }
+ pending_characteristic_read_reqs.remove(req);
+ GatoHandle service_handle = characteristic_to_service.value(char_handle);
+ if (!service_handle) {
+ qWarning() << "Unknown characteristic during read: " << char_handle;
+ return;
+ }
+
+ Q_ASSERT(services.contains(service_handle));
+ GatoService &service = services[service_handle];
+ Q_ASSERT(service.startHandle() == service_handle);
+
+ Q_ASSERT(service.containsCharacteristic(char_handle));
+ GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
+
+ emit q->valueUpdated(characteristic, value);
+}
+
+void GatoPeripheralPrivate::handleDescriptorRead(uint req, const QByteArray &value)
+{
+ Q_Q(GatoPeripheral);
+
+ GatoHandle desc_handle = pending_descriptor_read_reqs.value(req);
+ if (!desc_handle) {
+ qDebug() << "Got characteristics for a request I did not make";
+ return;
+ }
+ pending_descriptor_read_reqs.remove(req);
+ GatoHandle char_handle = descriptor_to_characteristic.value(desc_handle);
+ if (!char_handle) {
+ qWarning() << "Unknown characteristic during read: " << char_handle;
+ return;
+ }
+ GatoHandle service_handle = characteristic_to_service.value(char_handle);
+ if (!service_handle) {
+ qWarning() << "Unknown characteristic during read: " << char_handle;
+ return;
+ }
+
+ Q_ASSERT(services.contains(service_handle));
+ GatoService &service = services[service_handle];
+ Q_ASSERT(service.startHandle() == service_handle);
+
+ Q_ASSERT(service.containsCharacteristic(char_handle));
+ GatoCharacteristic characteristic = service.getCharacteristic(char_handle);
+
+ Q_ASSERT(characteristic.containsDescriptor(desc_handle));
+ GatoDescriptor descriptor = characteristic.getDescriptor(desc_handle);
+
+ emit q->descriptorValueUpdated(descriptor, value);
+}
+
+void GatoPeripheralPrivate::handleCharacteristicWrite(uint req, bool ok)
+{
+ Q_UNUSED(req);
+ if (!ok) {
+ qWarning() << "Failed to write some characteristic";
+ }
+}
+
+void GatoPeripheralPrivate::handleDescriptorWrite(uint req, bool ok)
+{
+ Q_UNUSED(req);
+ if (!ok) {
+ qWarning() << "Failed to write some characteristic";
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/gatoperipheral.h b/src/bluetooth/gatoperipheral.h
new file mode 100644
index 00000000..0186d9a7
--- /dev/null
+++ b/src/bluetooth/gatoperipheral.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GATOPERIPHERAL_H
+#define GATOPERIPHERAL_H
+
+#include <QtCore/QObject>
+#include "libgato_global.h"
+#include "gatouuid.h"
+#include "gatoaddress.h"
+
+QT_BEGIN_NAMESPACE
+
+class GatoService;
+class GatoCharacteristic;
+class GatoDescriptor;
+class GatoPeripheralPrivate;
+
+class LIBGATO_EXPORT GatoPeripheral : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(GatoPeripheral)
+ Q_ENUMS(State)
+ Q_ENUMS(WriteType)
+ Q_PROPERTY(GatoAddress address READ address)
+ Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+
+public:
+ GatoPeripheral(const GatoAddress& addr, QObject *parent = 0);
+ ~GatoPeripheral();
+
+ enum State {
+ StateDisconnected,
+ StateConnecting,
+ StateConnected
+ };
+
+ enum WriteType {
+ WriteWithResponse = 0,
+ WriteWithoutResponse
+ };
+
+ State state() const;
+ GatoAddress address() const;
+ QString name() const;
+ QList<GatoService> services() const;
+
+ void parseEIR(quint8 data[], int len);
+ bool advertisesService(const GatoUUID &uuid) const;
+
+public Q_SLOTS:
+ void connectPeripheral();
+ void disconnectPeripheral();
+ void discoverServices();
+ void discoverServices(const QList<GatoUUID>& serviceUUIDs);
+ void discoverCharacteristics(const GatoService &service);
+ void discoverCharacteristics(const GatoService &service, const QList<GatoUUID>& characteristicUUIDs);
+ void discoverDescriptors(const GatoCharacteristic &characteristic);
+ void readValue(const GatoCharacteristic &characteristic);
+ void readValue(const GatoDescriptor &descriptor);
+ void writeValue(const GatoCharacteristic &characteristic, const QByteArray &data, WriteType type = WriteWithResponse);
+ void writeValue(const GatoDescriptor &descriptor, const QByteArray &data);
+ void setNotification(const GatoCharacteristic &characteristic, bool enabled);
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+ void nameChanged();
+ void servicesDiscovered();
+ void characteristicsDiscovered(const GatoService &service);
+ void descriptorsDiscovered(const GatoCharacteristic &characteristic);
+ void valueUpdated(const GatoCharacteristic &characteristic, const QByteArray &value);
+ void descriptorValueUpdated(const GatoDescriptor &descriptor, const QByteArray &value);
+
+private:
+ GatoPeripheralPrivate *const d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // GATOPERIPHERAL_H
diff --git a/src/bluetooth/gatoperipheral_p.h b/src/bluetooth/gatoperipheral_p.h
new file mode 100644
index 00000000..9873c7bd
--- /dev/null
+++ b/src/bluetooth/gatoperipheral_p.h
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Javier de San Pedro <dev.git@javispedro.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef GATOPERIPHERAL_P_H
+#define GATOPERIPHERAL_P_H
+
+#include "gatoperipheral.h"
+#include "gatoservice.h"
+#include "gatocharacteristic.h"
+#include "gatodescriptor.h"
+#include "gatoattclient.h"
+
+QT_BEGIN_NAMESPACE
+
+class GatoPeripheralPrivate : public QObject
+{
+ Q_OBJECT
+
+ Q_DECLARE_PUBLIC(GatoPeripheral)
+
+public:
+ GatoPeripheralPrivate(GatoPeripheral *parent);
+ ~GatoPeripheralPrivate();
+
+ GatoPeripheral *q_ptr;
+ GatoAddress addr;
+ QString name;
+ QSet<GatoUUID> service_uuids;
+ QMap<GatoHandle, GatoService> services;
+
+ bool complete_name : 1;
+ bool complete_services : 1;
+
+ /** Maps attribute handles to service handles. */
+ QMap<GatoHandle, GatoHandle> characteristic_to_service;
+ QMap<GatoHandle, GatoHandle> value_to_characteristic;
+ QMap<GatoHandle, GatoHandle> descriptor_to_characteristic;
+
+ GatoAttClient *att;
+ QMap<uint, GatoUUID> pending_primary_reqs;
+ QMap<uint, GatoHandle> pending_characteristic_reqs;
+ QMap<uint, GatoHandle> pending_characteristic_read_reqs;
+ QMap<uint, GatoHandle> pending_descriptor_reqs;
+ QMap<uint, GatoHandle> pending_descriptor_read_reqs;
+
+ QMap<GatoHandle, bool> pending_set_notify;
+
+ void parseEIRFlags(quint8 data[], int len);
+ void parseEIRUUIDs(int size, bool complete, quint8 data[], int len);
+ void parseName(bool complete, quint8 data[], int len);
+
+ static GatoCharacteristic parseCharacteristicValue(const QByteArray &ba);
+
+ static QByteArray genClientCharConfiguration(bool notification, bool indication);
+
+ void clearServices();
+ void clearServiceCharacteristics(GatoService *service);
+ void clearCharacteristicDescriptors(GatoCharacteristic *characteristic);
+
+ void finishSetNotifyOperations(const GatoCharacteristic &characteristic);
+
+public slots:
+ void handleAttConnected();
+ void handleAttDisconnected();
+ void handleAttAttributeUpdated(GatoHandle handle, const QByteArray &value, bool confirmed);
+ void handlePrimary(uint req, const QList<GatoAttClient::AttributeGroupData>& list);
+ void handlePrimaryForService(uint req, const QList<GatoAttClient::HandleInformation>& list);
+ void handleCharacteristic(uint req, const QList<GatoAttClient::AttributeData> &list);
+ void handleDescriptors(uint req, const QList<GatoAttClient::InformationData> &list);
+ void handleCharacteristicRead(uint req, const QByteArray &value);
+ void handleDescriptorRead(uint req, const QByteArray &value);
+ void handleCharacteristicWrite(uint req, bool ok);
+ void handleDescriptorWrite(uint req, bool ok);
+};
+
+QT_END_NAMESPACE
+
+#endif // GATOPERIPHERAL_P_H
diff --git a/src/bluetooth/qbluetooth.cpp b/src/bluetooth/qbluetooth.cpp
index de1e5629..d9f77fac 100644
--- a/src/bluetooth/qbluetooth.cpp
+++ b/src/bluetooth/qbluetooth.cpp
@@ -76,6 +76,13 @@ namespace QBluetooth {
*/
}
+/*!
+ \typedef QLowEnergyHandle
+ \relates QBluetooth
+
+ Typedef for Bluetooth Low Energy ATT attribute handles.
+*/
+
Q_LOGGING_CATEGORY(QT_BT, "qt.bluetooth")
Q_LOGGING_CATEGORY(QT_BT_ANDROID, "qt.bluetooth.android")
Q_LOGGING_CATEGORY(QT_BT_BLUEZ, "qt.bluetooth.bluez")
diff --git a/src/bluetooth/qbluetooth.h b/src/bluetooth/qbluetooth.h
index 3d92e1f8..f9650385 100644
--- a/src/bluetooth/qbluetooth.h
+++ b/src/bluetooth/qbluetooth.h
@@ -59,6 +59,8 @@ Q_DECLARE_FLAGS(SecurityFlags, Security)
Q_DECLARE_OPERATORS_FOR_FLAGS(SecurityFlags)
}
+typedef quint16 QLowEnergyHandle;
+
QT_END_NAMESPACE
#endif // QBLUETOOTH_H
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent.h b/src/bluetooth/qbluetoothdevicediscoveryagent.h
index 89158c53..ba675d90 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent.h
@@ -111,6 +111,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_discoveryFinished())
Q_PRIVATE_SLOT(d_func(), void _q_discoveryInterrupted(const QString &path))
Q_PRIVATE_SLOT(d_func(), void _q_PropertiesChanged(const QString &interface, const QVariantMap &changed_properties, const QStringList &invalidated_properties))
+ Q_PRIVATE_SLOT(d_func(), void _q_extendedDeviceDiscoveryTimeout())
#endif
};
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp
index b1d8eeeb..c7fdde05 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp
@@ -69,10 +69,11 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
managerBluez5(0),
adapterBluez5(0),
discoveryTimer(0),
+ useExtendedDiscovery(false),
q_ptr(parent)
{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
if (isBluez5()) {
- Q_Q(QBluetoothDeviceDiscoveryAgent);
managerBluez5 = new OrgFreedesktopDBusObjectManagerInterface(
QStringLiteral("org.bluez"),
QStringLiteral("/"),
@@ -84,6 +85,11 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
} else {
manager = new OrgBluezManagerInterface(QStringLiteral("org.bluez"), QStringLiteral("/"),
QDBusConnection::systemBus(), parent);
+ QObject::connect(&extendedDiscoveryTimer,
+ SIGNAL(timeout()),
+ q, SLOT(_q_extendedDeviceDiscoveryTimeout()));
+ extendedDiscoveryTimer.setInterval(10000);
+ extendedDiscoveryTimer.setSingleShot(true);
}
inquiryType = QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry;
}
@@ -169,6 +175,26 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start()
return;
}
+ if (propertiesReply.value().value(QStringLiteral("Discovering")).toBool()) {
+ /* The discovery session is already ongoing. BTLE devices are advertised
+ immediately after the start of the device discovery session. Hence if the
+ session is already ongoing, we have just missed the BTLE device
+ advertisement.
+
+ This always happens during the second device discovery run in
+ the current process. The first discovery doesn't have this issue.
+ As to why the discovery session remains active despite the previous one
+ being terminated is not known. This may be a bug in Bluez4.
+
+ To workaround this issue we have to wait for two discovery
+ sessions cycles.
+ */
+ qCDebug(QT_BT_BLUEZ) << "Using BTLE device discovery workaround.";
+ useExtendedDiscovery = true;
+ } else {
+ useExtendedDiscovery = false;
+ }
+
QDBusPendingReply<> discoveryReply = adapter->StartDiscovery();
if (discoveryReply.isError()) {
delete adapter;
@@ -283,6 +309,17 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_deviceFound(const QString &addres
uuids.append(QBluetoothUuid(u));
device.setServiceUuids(uuids, QBluetoothDeviceInfo::DataIncomplete);
device.setCached(dict.value(QStringLiteral("Cached")).toBool());
+
+
+ /*
+ * Bluez v4.1 does not have extra bit which gives information if device is Bluetooth
+ * Low Energy device and the way to discover it is with Class property of the Bluetooth device.
+ * Low Energy devices do not have property Class.
+ */
+ if (btClass == 0)
+ device.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ else
+ device.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
for (int i = 0; i < discoveredDevices.size(); i++) {
if (discoveredDevices[i].address() == device.address()) {
if (discoveredDevices[i] == device) {
@@ -326,7 +363,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFoundBluez5(const QString& dev
qCDebug(QT_BT_BLUEZ) << "Discovered: " << btAddress.toString() << btName
<< "Num UUIDs" << device.uUIDs().count()
<< "total device" << discoveredDevices.count() << "cached"
- << "RSSI" << device.rSSI();
+ << "RSSI" << device.rSSI() << "Class" << btClass;
OrgFreedesktopDBusPropertiesInterface *prop = new OrgFreedesktopDBusPropertiesInterface(
QStringLiteral("org.bluez"), devicePath, QDBusConnection::systemBus(), q);
@@ -337,6 +374,12 @@ void QBluetoothDeviceDiscoveryAgentPrivate::deviceFoundBluez5(const QString& dev
// read information
QBluetoothDeviceInfo deviceInfo(btAddress, btName, btClass);
+
+ if (!btClass)
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ else
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
+
deviceInfo.setRssi(device.rSSI());
QList<QBluetoothUuid> uuids;
foreach (const QString &u, device.uUIDs())
@@ -365,23 +408,56 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_propertyChanged(const QString &na
{
qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << name << value.variant();
- if (name == QLatin1String("Discovering") && !value.variant().toBool()) {
- Q_Q(QBluetoothDeviceDiscoveryAgent);
- adapter->deleteLater();
- adapter = 0;
- if (pendingCancel && !pendingStart) {
- emit q->canceled();
- pendingCancel = false;
- } else if (pendingStart) {
- pendingStart = false;
- pendingCancel = false;
- start();
+ if (name == QLatin1String("Discovering")) {
+ if (!value.variant().toBool()) {
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ if (pendingCancel && !pendingStart) {
+ adapter->deleteLater();
+ adapter = 0;
+
+ emit q->canceled();
+ pendingCancel = false;
+ } else if (pendingStart) {
+ adapter->deleteLater();
+ adapter = 0;
+
+ pendingStart = false;
+ pendingCancel = false;
+ start();
+ } else {
+ if (useExtendedDiscovery) {
+ useExtendedDiscovery = false;
+ /* We don't use the Start/StopDiscovery combo here
+ Using this combo surppresses the BTLE device.
+ */
+ extendedDiscoveryTimer.start();
+ return;
+ }
+
+ adapter->deleteLater();
+ adapter = 0;
+ emit q->finished();
+ }
} else {
- emit q->finished();
+ if (extendedDiscoveryTimer.isActive())
+ extendedDiscoveryTimer.stop();
}
}
}
+void QBluetoothDeviceDiscoveryAgentPrivate::_q_extendedDeviceDiscoveryTimeout()
+{
+
+ if (adapter) {
+ adapter->deleteLater();
+ adapter = 0;
+ }
+ if (isActive()) {
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ emit q->finished();
+ }
+}
+
void QBluetoothDeviceDiscoveryAgentPrivate::_q_InterfacesAdded(const QDBusObjectPath &object_path,
InterfaceList interfaces_and_properties)
{
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
index 168b6c0c..c4e804d3 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_p.h
@@ -102,6 +102,7 @@ public:
void _q_PropertiesChanged(const QString &interface,
const QVariantMap &changed_properties,
const QStringList &invalidated_properties);
+ void _q_extendedDeviceDiscoveryTimeout();
#endif
private:
@@ -136,6 +137,10 @@ private:
void deviceFoundBluez5(const QString& devicePath);
void startBluez5();
+
+ bool useExtendedDiscovery;
+ QTimer extendedDiscoveryTimer;
+
#elif defined(QT_QNX_BLUETOOTH)
private slots:
void finished();
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_qnx.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_qnx.cpp
index 3a7c1af5..771ce56f 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_qnx.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_qnx.cpp
@@ -175,11 +175,18 @@ void QBluetoothDeviceDiscoveryAgentPrivate::remoteDevicesChanged(int fd)
int cod = 0;
int dev_type = 0;
int rssi = 0;
-
+ bool hasGatt = false;
pps_decoder_get_bool(&ppsDecoder, "paired", &paired);
pps_decoder_get_int(&ppsDecoder, "cod", &cod);
pps_decoder_get_int(&ppsDecoder, "dev_type", &dev_type);
pps_decoder_get_int(&ppsDecoder, "rssi", &rssi);
+ pps_decoder_push(&ppsDecoder, "gatt_available_services");
+ const char *next_service = 0;
+
+ for (int service_count=0; pps_decoder_get_string(&ppsDecoder, 0, &next_service ) == PPS_DECODER_OK; service_count++) {
+ hasGatt = true;
+ //qBluetoothDebug() << next_service;
+ }
pps_decoder_cleanup(&ppsDecoder);
QBluetoothDeviceInfo deviceInfo(deviceAddr, deviceName, cod);
@@ -202,6 +209,20 @@ void QBluetoothDeviceDiscoveryAgentPrivate::remoteDevicesChanged(int fd)
m_finishedTimer.start(7000);
if (!deviceAddr.isNull()) {
qCDebug(QT_BT_QNX) << "Device discovered: " << deviceName << deviceAddr.toString();
+ /* Looking for device type. Only Low energy devices will be added
+ * BT_DEVICE_TYPE_LE_PUBLIC is 0 --->LE device
+ * BT_DEVICE_TYPE_LE_PRIVATE is 1 ---> LE device
+ * BT_DEVICE_TYPE_REGULAR is 32
+ * BT_DEVICE_TYPE_UNKNOWN is 255
+ */
+ if (dev_type == 0 || dev_type == 1)
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ else{
+ if (hasGatt)
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration);
+ else
+ deviceInfo.setCoreConfigurations(QBluetoothDeviceInfo::BaseRateCoreConfiguration);
+ }
discoveredDevices.append(deviceInfo);
if (!updated) // We are not allowed to emit a signal with the updated version
emit q_ptr->deviceDiscovered(discoveredDevices.last());
diff --git a/src/bluetooth/qbluetoothdeviceinfo.cpp b/src/bluetooth/qbluetoothdeviceinfo.cpp
index 408222bd..d43cbab6 100644
--- a/src/bluetooth/qbluetoothdeviceinfo.cpp
+++ b/src/bluetooth/qbluetoothdeviceinfo.cpp
@@ -246,6 +246,16 @@ QT_BEGIN_NAMESPACE
\value DataUnavailable No data is available.
*/
+/*!
+ \enum QBluetoothDeviceInfo::CoreConfiguration
+
+ This enum describes the configuration of the device.
+
+ \value BaseRateCoreConfiguration The device is a standard Bluetooth device.
+ \value BaseRateAndLowEnergyCoreConfiguration The device is a Bluetooth Smart device with support
+ for standard and Low Energy device.
+ \value LowEnergyCoreConfiguration The device is a Bluetooth Low Energy device.
+*/
QBluetoothDeviceInfoPrivate::QBluetoothDeviceInfoPrivate() :
valid(false),
cached(false),
@@ -253,7 +263,8 @@ QBluetoothDeviceInfoPrivate::QBluetoothDeviceInfoPrivate() :
serviceClasses(QBluetoothDeviceInfo::NoService),
majorDeviceClass(QBluetoothDeviceInfo::MiscellaneousDevice),
minorDeviceClass(0),
- serviceUuidsCompleteness(QBluetoothDeviceInfo::DataUnavailable)
+ serviceUuidsCompleteness(QBluetoothDeviceInfo::DataUnavailable),
+ deviceCoreConfiguration(QBluetoothDeviceInfo::BaseRateCoreConfiguration)
{
}
@@ -362,6 +373,7 @@ QBluetoothDeviceInfo &QBluetoothDeviceInfo::operator=(const QBluetoothDeviceInfo
d->serviceUuidsCompleteness = other.d_func()->serviceUuidsCompleteness;
d->serviceUuids = other.d_func()->serviceUuids;
d->rssi = other.d_func()->rssi;
+ d->deviceCoreConfiguration = other.d_func()->deviceCoreConfiguration;
return *this;
}
@@ -393,6 +405,8 @@ bool QBluetoothDeviceInfo::operator==(const QBluetoothDeviceInfo &other) const
return false;
if (d->serviceUuids != other.d_func()->serviceUuids)
return false;
+ if (d->deviceCoreConfiguration != other.d_func()->deviceCoreConfiguration)
+ return false;
return true;
}
@@ -501,6 +515,32 @@ QBluetoothDeviceInfo::DataCompleteness QBluetoothDeviceInfo::serviceUuidsComplet
}
/*!
+ Sets the CoreConfigurations of the device to \a coreConfigs. This will help to make a difference
+ between regular and Low Energy devices.
+
+ \sa coreConfigurations()
+*/
+void QBluetoothDeviceInfo::setCoreConfigurations(QBluetoothDeviceInfo::CoreConfigurations coreConfigs)
+{
+ Q_D(QBluetoothDeviceInfo);
+
+ d->deviceCoreConfiguration = coreConfigs;
+}
+
+/*!
+ Returns the configuration of the device. If device configuration is not set,
+ basic rate device configuration will be returned.
+
+ \sa setCoreConfigurations()
+*/
+QBluetoothDeviceInfo::CoreConfigurations QBluetoothDeviceInfo::coreConfigurations() const
+{
+ Q_D(const QBluetoothDeviceInfo);
+
+ return d->deviceCoreConfiguration;
+}
+
+/*!
Returns true if the QBluetoothDeviceInfo object is created from cached data.
*/
bool QBluetoothDeviceInfo::isCached() const
diff --git a/src/bluetooth/qbluetoothdeviceinfo.h b/src/bluetooth/qbluetoothdeviceinfo.h
index 13892990..f2cc1529 100644
--- a/src/bluetooth/qbluetoothdeviceinfo.h
+++ b/src/bluetooth/qbluetoothdeviceinfo.h
@@ -196,6 +196,13 @@ public:
DataUnavailable
};
+ enum CoreConfiguration {
+ LowEnergyCoreConfiguration = 0x01,
+ BaseRateCoreConfiguration = 0x02,
+ BaseRateAndLowEnergyCoreConfiguration = 0x03
+ };
+ Q_DECLARE_FLAGS(CoreConfigurations, CoreConfiguration)
+
QBluetoothDeviceInfo();
QBluetoothDeviceInfo(const QBluetoothAddress &address, const QString &name,
quint32 classOfDevice);
@@ -225,6 +232,9 @@ public:
QList<QBluetoothUuid> serviceUuids(DataCompleteness *completeness = 0) const;
DataCompleteness serviceUuidsCompleteness() const;
+ void setCoreConfigurations(QBluetoothDeviceInfo::CoreConfigurations coreConfigs);
+ QBluetoothDeviceInfo::CoreConfigurations coreConfigurations() const;
+
protected:
QBluetoothDeviceInfoPrivate *d_ptr;
diff --git a/src/bluetooth/qbluetoothdeviceinfo_p.h b/src/bluetooth/qbluetoothdeviceinfo_p.h
index b08a8fec..f5e575aa 100644
--- a/src/bluetooth/qbluetoothdeviceinfo_p.h
+++ b/src/bluetooth/qbluetoothdeviceinfo_p.h
@@ -69,6 +69,7 @@ public:
QBluetoothDeviceInfo::DataCompleteness serviceUuidsCompleteness;
QList<QBluetoothUuid> serviceUuids;
+ QBluetoothDeviceInfo::CoreConfigurations deviceCoreConfiguration;
};
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
index 0e185990..864b6d05 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp
@@ -105,6 +105,12 @@ QT_BEGIN_NAMESPACE
*/
/*!
+ \fn QBluetoothServiceDiscoveryAgent::serviceDiscovered(const QLowEnergyServiceInfo &info)
+
+ This signal is emitted when the Bluetooth Low Energy service described by \a info is discovered.
+*/
+
+/*!
\fn QBluetoothServiceDiscoveryAgent::finished()
This signal is emitted when the Bluetooth service discovery completes.
@@ -248,7 +254,6 @@ bool QBluetoothServiceDiscoveryAgent::setRemoteAddress(const QBluetoothAddress &
if (!address.isNull())
d_ptr->singleDevice = true;
d_ptr->deviceAddress = address;
-
return true;
}
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.h b/src/bluetooth/qbluetoothservicediscoveryagent.h
index 37c26a78..f1e754db 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent.h
@@ -49,6 +49,7 @@
#include <QtBluetooth/QBluetoothServiceInfo>
#include <QtBluetooth/QBluetoothUuid>
#include <QtBluetooth/QBluetoothDeviceDiscoveryAgent>
+#include <QtBluetooth/QLowEnergyServiceInfo>
QT_BEGIN_NAMESPACE
@@ -100,6 +101,7 @@ public Q_SLOTS:
Q_SIGNALS:
void serviceDiscovered(const QBluetoothServiceInfo &info);
+ void serviceDiscovered(const QLowEnergyServiceInfo &info);
void finished();
void canceled();
void error(QBluetoothServiceDiscoveryAgent::Error error);
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
index 3610d7bc..2f2f26d8 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp
@@ -41,6 +41,7 @@
#include "qbluetoothservicediscoveryagent.h"
#include "qbluetoothservicediscoveryagent_p.h"
+#include "qlowenergyserviceinfo_p.h"
#include "bluez/manager_p.h"
#include "bluez/adapter_p.h"
@@ -126,6 +127,15 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr
adapter = new OrgBluezAdapterInterface(QStringLiteral("org.bluez"), reply.value().path(),
QDBusConnection::systemBus());
+ if (m_deviceAdapterAddress.isNull()) {
+ QDBusPendingReply<QVariantMap> reply = adapter->GetProperties();
+ reply.waitForFinished();
+ if (!reply.isError()) {
+ const QBluetoothAddress path_address(reply.value().value(QStringLiteral("Address")).toString());
+ m_deviceAdapterAddress = path_address;
+ }
+ }
+
QDBusPendingReply<QDBusObjectPath> deviceObjectPath = adapter->CreateDevice(address.toString());
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(deviceObjectPath, q);
@@ -287,7 +297,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_finishSdpScan(QBluetoothServiceD
emit q->error(error);
} else if (!xmlRecords.isEmpty() && discoveryState() != Inactive) {
foreach (const QString &record, xmlRecords) {
- const QBluetoothServiceInfo serviceInfo = parseServiceXml(record);
+ bool isBtleService = false;
+ const QBluetoothServiceInfo serviceInfo = parseServiceXml(record, &isBtleService);
//apply uuidFilter
if (!uuidFilter.isEmpty()) {
@@ -389,17 +400,73 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_createdDevice(QDBusPendingCallWa
delete adapter;
adapter = 0;
- QString pattern;
- foreach (const QBluetoothUuid &uuid, uuidFilter)
- pattern += uuid.toString().remove(QLatin1Char('{')).remove(QLatin1Char('}')) + QLatin1Char(' ');
+ QVariantMap deviceProperties;
+ QString classType;
+ QDBusPendingReply<QVariantMap> deviceReply = device->GetProperties();
+ deviceReply.waitForFinished();
+ if (!deviceReply.isError()) {
+ deviceProperties = deviceReply.value();
+ classType = deviceProperties.value(QStringLiteral("Class")).toString();
+ }
- pattern = pattern.trimmed();
- qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << "Discover restrictions:" << pattern;
+ /*
+ * Low Energy services in bluez are represented as the list of the paths that can be
+ * accessed with org.bluez.Characteristic
+ */
+ //QDBusArgument services = v.value(QLatin1String("Services")).value<QDBusArgument>();
+
+
+ /*
+ * Bluez v4.1 does not have extra bit which gives information if device is Bluetooth
+ * Low Energy device and the way to discover it is with Class property of the Bluetooth device.
+ * Low Energy devices do not have property Class.
+ * In case we have LE device finish service discovery; otherwise search for regular services.
+ */
+ if (classType.isEmpty()) { //is BLE device or device properties above not retrievable
+ qCDebug(QT_BT_BLUEZ) << "Discovered BLE-only device. Normal service discovery skipped.";
+ delete device;
+ device = 0;
- QDBusPendingReply<ServiceMap> discoverReply = device->DiscoverServices(pattern);
- watcher = new QDBusPendingCallWatcher(discoverReply, q);
- QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
- q, SLOT(_q_discoveredServices(QDBusPendingCallWatcher*)));
+ const QStringList deviceUuids = deviceProperties.value(QStringLiteral("UUIDs")).toStringList();
+ for (int i = 0; i < deviceUuids.size(); i++) {
+ QString b = deviceUuids.at(i);
+ b = b.remove(QLatin1Char('{')).remove(QLatin1Char('}'));
+ const QBluetoothUuid uuid(b);
+
+ qCDebug(QT_BT_BLUEZ) << "Discovered BLE service" << uuid << uuidFilter.size();
+ QLowEnergyServiceInfo lowEnergyService(uuid);
+ lowEnergyService.setDevice(discoveredDevices.at(0));
+ if (uuidFilter.isEmpty())
+ emit q->serviceDiscovered(lowEnergyService);
+ else {
+ for (int j = 0; j < uuidFilter.size(); j++) {
+ if (uuidFilter.at(j) == uuid)
+ emit q->serviceDiscovered(lowEnergyService);
+
+ }
+ }
+
+ }
+
+ if (singleDevice && deviceReply.isError()) {
+ error = QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = QBluetoothServiceDiscoveryAgent::tr("Unable to access device");
+ emit q->error(error);
+ }
+ _q_serviceDiscoveryFinished();
+ } else {
+ QString pattern;
+ foreach (const QBluetoothUuid &uuid, uuidFilter)
+ pattern += uuid.toString().remove(QLatin1Char('{')).remove(QLatin1Char('}')) + QLatin1Char(' ');
+
+ pattern = pattern.trimmed();
+ qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << "Discover restrictions:" << pattern;
+
+ QDBusPendingReply<ServiceMap> discoverReply = device->DiscoverServices(pattern);
+ watcher = new QDBusPendingCallWatcher(discoverReply, q);
+ QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ q, SLOT(_q_discoveredServices(QDBusPendingCallWatcher*)));
+ }
}
// Bluez 4
@@ -409,13 +476,13 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC
return;
qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO;
+ Q_Q(QBluetoothServiceDiscoveryAgent);
QDBusPendingReply<ServiceMap> reply = *watcher;
if (reply.isError()) {
qCDebug(QT_BT_BLUEZ) << "discoveredServices error: " << error << reply.error().message();
watcher->deleteLater();
if (singleDevice) {
- Q_Q(QBluetoothServiceDiscoveryAgent);
error = QBluetoothServiceDiscoveryAgent::UnknownError;
errorString = reply.error().message();
emit q->error(error);
@@ -430,8 +497,17 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC
qCDebug(QT_BT_BLUEZ) << "Parsing xml" << discoveredDevices.at(0).address().toString() << discoveredDevices.count() << map.count();
+
+
foreach (const QString &record, reply.value()) {
- QBluetoothServiceInfo serviceInfo = parseServiceXml(record);
+ bool isBtleService = false;
+ QBluetoothServiceInfo serviceInfo = parseServiceXml(record, &isBtleService);
+
+ if (isBtleService) {
+ qCDebug(QT_BT_BLUEZ) << "Discovered BLE services" << discoveredDevices.at(0).address().toString()
+ << serviceInfo.serviceName() << serviceInfo.serviceUuid() << serviceInfo.serviceClassUuids();
+ continue;
+ }
if (!serviceInfo.isValid())
continue;
@@ -474,7 +550,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::_q_discoveredServices(QDBusPendingC
_q_serviceDiscoveryFinished();
}
-QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(const QString& xmlRecord)
+QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(
+ const QString& xmlRecord, bool *isBtleService)
{
QXmlStreamReader xml(xmlRecord);
@@ -491,7 +568,24 @@ QBluetoothServiceInfo QBluetoothServiceDiscoveryAgentPrivate::parseServiceXml(co
if (xml.readNextStartElement()) {
QVariant value = readAttributeValue(xml);
-
+ if (isBtleService) {
+ if (attributeId == 1) {// Attribute with id 1 contains UUID of the service
+ const QBluetoothServiceInfo::Sequence seq =
+ value.value<QBluetoothServiceInfo::Sequence>();
+ for (int i = 0; i < seq.count(); i++) {
+ const QBluetoothUuid uuid = seq.at(i).value<QBluetoothUuid>();
+ if ((uuid.data1 & 0x1800) == 0x1800) {// We are taking into consideration that LE services starts at 0x1800
+ //TODO don't emit in the middle of nowhere
+ Q_Q(QBluetoothServiceDiscoveryAgent);
+ QLowEnergyServiceInfo leService(uuid);
+ leService.setDevice(discoveredDevices.at(0));
+ *isBtleService = true;
+ emit q->serviceDiscovered(leService);
+ break;
+ }
+ }
+ }
+ }
serviceInfo.setAttribute(attributeId, value);
}
}
@@ -532,7 +626,6 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
== details.value(QStringLiteral("Address")).toString()) {
uuidStrings = details.value(QStringLiteral("UUIDs")).toStringList();
break;
-
}
}
}
@@ -541,6 +634,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
}
if (uuidStrings.isEmpty() || discoveredDevices.isEmpty()) {
+ qCWarning(QT_BT_BLUEZ) << "No uuids found for" << deviceAddress.toString();
// nothing found -> go to next uuid
_q_serviceDiscoveryFinished();
return;
@@ -558,6 +652,7 @@ void QBluetoothServiceDiscoveryAgentPrivate::performMinimalServiceDiscovery(cons
if (!uuidFilter.isEmpty() && !uuidFilter.contains(uuid))
continue;
+ // TODO deal with BTLE services under Bluez 5 -> right now they are normal services
QBluetoothServiceInfo serviceInfo;
serviceInfo.setDevice(discoveredDevices.at(0));
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
index 2bf4ff72..0377ce1a 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h
@@ -46,14 +46,17 @@
#include "qbluetoothdeviceinfo.h"
#include "qbluetoothserviceinfo.h"
#include "qbluetoothservicediscoveryagent.h"
+#include "qlowenergyserviceinfo.h"
#include <QStack>
+#include <QStringList>
#ifdef QT_BLUEZ_BLUETOOTH
class OrgBluezManagerInterface;
class OrgBluezAdapterInterface;
class OrgBluezDeviceInterface;
class OrgFreedesktopDBusObjectManagerInterface;
+
QT_BEGIN_NAMESPACE
class QDBusPendingCallWatcher;
class QXmlStreamReader;
@@ -109,7 +112,6 @@ public:
void setDiscoveryMode(QBluetoothServiceDiscoveryAgent::DiscoveryMode m) { mode = m; }
QBluetoothServiceDiscoveryAgent::DiscoveryMode DiscoveryMode() { return mode; }
- // private slots
void _q_deviceDiscoveryFinished();
void _q_deviceDiscovered(const QBluetoothDeviceInfo &info);
void _q_serviceDiscoveryFinished();
@@ -117,6 +119,12 @@ public:
#ifdef QT_BLUEZ_BLUETOOTH
void _q_discoveredServices(QDBusPendingCallWatcher *watcher);
void _q_createdDevice(QDBusPendingCallWatcher *watcher);
+ //Slots below are used for discovering Bluetooth Low Energy devices. It will be used with Bluez 5.x version.
+ /*
+ void _g_discoveredGattService();
+ void _q_discoverGattCharacteristics(QDBusPendingCallWatcher *watcher);
+ void _q_discoveredGattCharacteristic(QDBusPendingCallWatcher *watcher);
+ */
void _q_finishSdpScan(QBluetoothServiceDiscoveryAgent::Error errorCode,
const QString &errorDescription,
const QStringList &xmlRecords);
@@ -140,7 +148,7 @@ private:
void runSdpScan(const QBluetoothAddress &remoteAddress,
const QBluetoothAddress localAddress);
QVariant readAttributeValue(QXmlStreamReader &xml);
- QBluetoothServiceInfo parseServiceXml(const QString& xml);
+ QBluetoothServiceInfo parseServiceXml(const QString& xml, bool *isBtleService);
void performMinimalServiceDiscovery(const QBluetoothAddress &deviceAddress);
#endif
@@ -159,6 +167,7 @@ private:
QSocketNotifier *rdNotifier;
QTimer m_queryTimer;
bool m_btInitialized;
+ bool m_serviceScanDone;
#endif
public:
@@ -178,7 +187,6 @@ private:
QBluetoothServiceDiscoveryAgent::DiscoveryMode mode;
bool singleDevice;
-
#ifdef QT_BLUEZ_BLUETOOTH
QString foundHostAdapterPath;
OrgBluezManagerInterface *manager;
diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_qnx.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_qnx.cpp
index d65d638d..a2abbebc 100644
--- a/src/bluetooth/qbluetoothservicediscoveryagent_qnx.cpp
+++ b/src/bluetooth/qbluetoothservicediscoveryagent_qnx.cpp
@@ -45,16 +45,21 @@
#include "qbluetoothdeviceinfo.h"
#include "qbluetoothdevicediscoveryagent.h"
+#include "qlowenergyserviceinfo_p.h"
+
#include <QStringList>
#include "qbluetoothuuid.h"
-
+#include <stdio.h>
+#include <unistd.h>
#include <sys/pps.h>
#ifdef QT_QNX_BT_BLUETOOTH
#include <errno.h>
#include <QPointer>
#endif
-
#include <QFile>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
#include <QtCore/private/qcore_unix_p.h>
@@ -196,33 +201,45 @@ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &addr
#else
qCDebug(QT_BT_QNX) << "Starting Service discovery for" << address.toString();
const QString filePath = QStringLiteral("/pps/services/bluetooth/remote_devices/").append(address.toString());
+ bool hasError = false;
if ((m_rdfd = qt_safe_open(filePath.toLocal8Bit().constData(), O_RDONLY)) == -1) {
if (QFile::exists(filePath + QStringLiteral("-00")) ||
- QFile::exists(filePath + QStringLiteral("-01"))) {
+ QFile::exists(filePath + QStringLiteral("-01")))
+ {
qCDebug(QT_BT_QNX) << "LE device discovered...skipping";
+ QString lePath = filePath + QStringLiteral("-00");
+ if ((m_rdfd = qt_safe_open(lePath.toLocal8Bit().constData(), O_RDONLY)) == -1) {
+ lePath = filePath + QStringLiteral("-01");
+ if ((m_rdfd = qt_safe_open(lePath.toLocal8Bit().constData(), O_RDONLY)) == -1)
+ hasError = true;
+ }
} else {
- qCWarning(QT_BT_QNX) << "Failed to open " << filePath;
- error = QBluetoothServiceDiscoveryAgent::InputOutputError;
- errorString = QBluetoothServiceDiscoveryAgent::tr("Failed to open remote device file");
- q->error(error);
+ hasError = true;
}
+ }
+ if (hasError) {
+ qCWarning(QT_BT_QNX) << "Failed to open " << filePath;
+ error = QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = QBluetoothServiceDiscoveryAgent::tr("Failed to open remote device file");
+ q->error(error);
_q_serviceDiscoveryFinished();
return;
+ }
+
+ if (rdNotifier)
+ delete rdNotifier;
+ rdNotifier = new QSocketNotifier(m_rdfd, QSocketNotifier::Read, this);
+ if (rdNotifier) {
+ connect(rdNotifier, SIGNAL(activated(int)), this, SLOT(remoteDevicesChanged(int)));
} else {
- if (rdNotifier)
- delete rdNotifier;
- rdNotifier = new QSocketNotifier(m_rdfd, QSocketNotifier::Read, this);
- if (rdNotifier) {
- connect(rdNotifier, SIGNAL(activated(int)), this, SLOT(remoteDevicesChanged(int)));
- } else {
- qCWarning(QT_BT_QNX) << "Service Discovery: Failed to connect to rdNotifier";
- error = QBluetoothServiceDiscoveryAgent::InputOutputError;
- errorString = QBluetoothServiceDiscoveryAgent::tr("Failed to connect to notifier");
- q->error(error);
- _q_serviceDiscoveryFinished();
- return;
- }
+ qWarning() << "Service Discovery: Failed to connect to rdNotifier";
+ error = QBluetoothServiceDiscoveryAgent::InputOutputError;
+ errorString = QStringLiteral("Failed to connect to rdNotifier");
+ q->error(error);
+ _q_serviceDiscoveryFinished();
+ return;
}
+
m_queryTimer.start(10000);
ppsSendControlMessage("service_query", QStringLiteral("{\"addr\":\"%1\"}").arg(address.toString()), this);
#endif
@@ -258,14 +275,14 @@ void QBluetoothServiceDiscoveryAgentPrivate::remoteDevicesChanged(int fd)
pps_decoder_cleanup(&ppsDecoder);
return;
}
-
+ // Checking for standard Bluetooth services
pps_decoder_push(&ppsDecoder, "available_services");
-
+ bool standardService = false;
const char *next_service = 0;
for (int service_count=0; pps_decoder_get_string(&ppsDecoder, 0, &next_service ) == PPS_DECODER_OK; service_count++) {
if (next_service == 0)
break;
-
+ standardService = true;
qCDebug(QT_BT_QNX) << Q_FUNC_INFO << "Service" << next_service;
QBluetoothServiceInfo serviceInfo;
@@ -314,7 +331,38 @@ void QBluetoothServiceDiscoveryAgentPrivate::remoteDevicesChanged(int fd)
}
}
+ if (standardService) // we need to pop back for the LE service scan
+ pps_decoder_pop(&ppsDecoder);
+ //Checking for Bluetooth Low Energy services
+ pps_decoder_push(&ppsDecoder, "gatt_available_services");
+
+ for (int service_count=0; pps_decoder_get_string(&ppsDecoder, 0, &next_service ) == PPS_DECODER_OK; service_count++) {
+ if (next_service == 0)
+ break;
+
+ QString lowEnergyUuid(next_service);
+ qCDebug(QT_BT_QNX) << "LE Service: " << lowEnergyUuid << next_service;
+ QBluetoothUuid leUuid;
+
+ //In case of custom UUIDs (e.g. Texas Instruments SenstorTag LE Device)
+ if ( lowEnergyUuid.length() > 4 ) {
+ leUuid = QBluetoothUuid(lowEnergyUuid);
+ }
+ else {// Official UUIDs are presented in 4 characters (for instance 180A)
+ lowEnergyUuid = QStringLiteral("0x") + lowEnergyUuid;
+ leUuid = QBluetoothUuid(lowEnergyUuid.toUShort(0,0));
+ }
+
+ QLowEnergyServiceInfo lowEnergyService(leUuid);
+ lowEnergyService.setDevice(discoveredDevices.at(0));
+ qCDebug(QT_BT_QNX) << "Adding Low Energy service" << lowEnergyService.serviceUuid();
+ q_ptr->serviceDiscovered(lowEnergyService);
+ }
+
pps_decoder_cleanup(&ppsDecoder);
+ //Deleting notifier since services will not change.
+ delete rdNotifier;
+ rdNotifier = 0;
}
void QBluetoothServiceDiscoveryAgentPrivate::controlReply(ppsResult result)
@@ -327,6 +375,8 @@ void QBluetoothServiceDiscoveryAgentPrivate::controlReply(ppsResult result)
if (!result.errorMsg.isEmpty()) {
qCWarning(QT_BT_QNX) << Q_FUNC_INFO << result.errorMsg;
errorString = result.errorMsg;
+ if (errorString == QObject::tr("Operation canceled"))
+ _q_serviceDiscoveryFinished();
error = QBluetoothServiceDiscoveryAgent::InputOutputError;
q->error(error);
} else {
diff --git a/src/bluetooth/qbluetoothsocket.h b/src/bluetooth/qbluetoothsocket.h
index b06330c7..9f56764f 100644
--- a/src/bluetooth/qbluetoothsocket.h
+++ b/src/bluetooth/qbluetoothsocket.h
@@ -162,6 +162,7 @@ protected:
private:
Q_PRIVATE_SLOT(d_func(), void _q_readNotify())
Q_PRIVATE_SLOT(d_func(), void _q_writeNotify())
+ friend class QLowEnergyControllerPrivate;
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/bluetooth/qbluetoothsocket_bluez.cpp b/src/bluetooth/qbluetoothsocket_bluez.cpp
index 57071153..1807a225 100644
--- a/src/bluetooth/qbluetoothsocket_bluez.cpp
+++ b/src/bluetooth/qbluetoothsocket_bluez.cpp
@@ -71,7 +71,8 @@ QBluetoothSocketPrivate::QBluetoothSocketPrivate()
readNotifier(0),
connectWriteNotifier(0),
connecting(false),
- discoveryAgent(0)
+ discoveryAgent(0),
+ isLowEnergySocket(false)
{
}
@@ -158,7 +159,22 @@ void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address,
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
+ // This is an ugly hack but the socket class does what's needed already.
+ // For L2CP GATT we need a channel rather than a socket and the LE address type
+ // We don't want to make this public API offering for now especially since
+ // only Linux (of all platforms supported by this library) supports this type
+ // of socket.
+
+#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+ if (isLowEnergySocket) {
+ addr.l2_cid = htobs(port);
+ addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
+ } else {
+ addr.l2_psm = port;
+ }
+#else
addr.l2_psm = port;
+#endif
convertAddress(address.toUInt64(), addr.l2_bdaddr.b);
diff --git a/src/bluetooth/qbluetoothsocket_p.h b/src/bluetooth/qbluetoothsocket_p.h
index 63309069..2c44bb0c 100644
--- a/src/bluetooth/qbluetoothsocket_p.h
+++ b/src/bluetooth/qbluetoothsocket_p.h
@@ -187,6 +187,11 @@ private slots:
void controlReply(ppsResult result);
void controlEvent(ppsResult result);
#endif
+
+#ifdef QT_BLUEZ_BLUETOOTH
+public:
+ bool isLowEnergySocket;
+#endif
};
diff --git a/src/bluetooth/qbluetoothuuid.cpp b/src/bluetooth/qbluetoothuuid.cpp
index 0d20b2ec..3f8487b4 100644
--- a/src/bluetooth/qbluetoothuuid.cpp
+++ b/src/bluetooth/qbluetoothuuid.cpp
@@ -108,73 +108,295 @@ Q_GLOBAL_STATIC_WITH_ARGS(QUuid, baseUuid, ("{00000000-0000-1000-8000-00805F9B34
The list below explicitly states as what type each UUID shall be used.
- \value ServiceDiscoveryServer Service discovery server UUID (service)
- \value BrowseGroupDescriptor Browser group descriptor (service)
- \value PublicBrowseGroup Public browse group service class. Services which have the public
- browse group in their \l {QBluetoothServiceInfo::BrowseGroupList}{browse group list}
- are discoverable by the remote devices.
- \value SerialPort Serial Port Profile UUID (service & profile)
- \value LANAccessUsingPPP LAN Access Profile UUID (service & profile)
- \value DialupNetworking Dial-up Networking Profile UUID (service & profile)
- \value IrMCSync Synchronization Profile UUID (service & profile)
- \value ObexObjectPush OBEX object push service UUID (service & profile)
- \value OBEXFileTransfer File Transfer Profile (FTP) UUID (service & profile)
- \value IrMCSyncCommand Synchronization Profile UUID (profile)
- \value Headset Headset Profile (HSP) UUID (service & profile)
- \value AudioSource Advanced Audio Distribution Profile (A2DP) UUID (service)
- \value AudioSink Advanced Audio Distribution Profile (A2DP) UUID (service)
- \value AV_RemoteControlTarget Audio/Video Remote Control Profile (AVRCP) UUID (service)
- \value AdvancedAudioDistribution Advanced Audio Distribution Profile (A2DP) UUID (profile)
- \value AV_RemoteControl Audio/Video Remote Control Profile (AVRCP) UUID (service & profile)
+ \value ServiceDiscoveryServer Service discovery server UUID (service)
+ \value BrowseGroupDescriptor Browser group descriptor (service)
+ \value PublicBrowseGroup Public browse group service class. Services which have the public
+ browse group in their \l {QBluetoothServiceInfo::BrowseGroupList}{browse group list}
+ are discoverable by the remote devices.
+ \value SerialPort Serial Port Profile UUID (service & profile)
+ \value LANAccessUsingPPP LAN Access Profile UUID (service & profile)
+ \value DialupNetworking Dial-up Networking Profile UUID (service & profile)
+ \value IrMCSync Synchronization Profile UUID (service & profile)
+ \value ObexObjectPush OBEX object push service UUID (service & profile)
+ \value OBEXFileTransfer File Transfer Profile (FTP) UUID (service & profile)
+ \value IrMCSyncCommand Synchronization Profile UUID (profile)
+ \value Headset Headset Profile (HSP) UUID (service & profile)
+ \value AudioSource Advanced Audio Distribution Profile (A2DP) UUID (service)
+ \value AudioSink Advanced Audio Distribution Profile (A2DP) UUID (service)
+ \value AV_RemoteControlTarget Audio/Video Remote Control Profile (AVRCP) UUID (service)
+ \value AdvancedAudioDistribution Advanced Audio Distribution Profile (A2DP) UUID (profile)
+ \value AV_RemoteControl Audio/Video Remote Control Profile (AVRCP) UUID (service & profile)
\value AV_RemoteControlController Audio/Video Remote Control Profile UUID (service)
- \value HeadsetAG Headset Profile (HSP) UUID (service)
- \value PANU Personal Area Networking Profile (PAN) UUID (service & profile)
- \value NAP Personal Area Networking Profile (PAN) UUID (service & profile)
- \value GN Personal Area Networking Profile (PAN) UUID (service & profile)
- \value DirectPrinting Basic Printing Profile (BPP) UUID (service)
- \value ReferencePrinting Related to Basic Printing Profile (BPP) UUID (service)
- \value BasicImage Basic Imaging Profile (BIP) UUID (profile)
- \value ImagingResponder Basic Imaging Profile (BIP) UUID (service)
+ \value HeadsetAG Headset Profile (HSP) UUID (service)
+ \value PANU Personal Area Networking Profile (PAN) UUID (service & profile)
+ \value NAP Personal Area Networking Profile (PAN) UUID (service & profile)
+ \value GN Personal Area Networking Profile (PAN) UUID (service & profile)
+ \value DirectPrinting Basic Printing Profile (BPP) UUID (service)
+ \value ReferencePrinting Related to Basic Printing Profile (BPP) UUID (service)
+ \value BasicImage Basic Imaging Profile (BIP) UUID (profile)
+ \value ImagingResponder Basic Imaging Profile (BIP) UUID (service)
\value ImagingAutomaticArchive Basic Imaging Profile (BIP) UUID (service)
\value ImagingReferenceObjects Basic Imaging Profile (BIP) UUID (service)
- \value Handsfree Hands-Free Profile (HFP) UUID (service & profile)
- \value HandsfreeAudioGateway Hands-Free Audio Gateway (HFP) UUID (service)
+ \value Handsfree Hands-Free Profile (HFP) UUID (service & profile)
+ \value HandsfreeAudioGateway Hands-Free Audio Gateway (HFP) UUID (service)
\value DirectPrintingReferenceObjectsService Basic Printing Profile (BPP) UUID (service)
- \value ReflectedUI Basic Printing Profile (BPP) UUID (service)
- \value BasicPrinting Basic Printing Profile (BPP) UUID (profile)
- \value PrintingStatus Basic Printing Profile (BPP) UUID (service)
+ \value ReflectedUI Basic Printing Profile (BPP) UUID (service)
+ \value BasicPrinting Basic Printing Profile (BPP) UUID (profile)
+ \value PrintingStatus Basic Printing Profile (BPP) UUID (service)
\value HumanInterfaceDeviceService Human Interface Device (HID) UUID (service & profile)
\value HardcopyCableReplacement Hardcopy Cable Replacement Profile (HCRP) (profile)
- \value HCRPrint Hardcopy Cable Replacement Profile (HCRP) (service)
- \value HCRScan Hardcopy Cable Replacement Profile (HCRP) (service)
- \value SIMAccess SIM Access Profile (SAP) UUID (service and profile)
- \value PhonebookAccessPCE Phonebook Access Profile (PBAP) UUID (service)
- \value PhonebookAccessPSE Phonebook Access Profile (PBAP) UUID (service)
- \value PhonebookAccess Phonebook Access Profile (PBAP) (profile)
- \value HeadsetHS Headset Profile (HSP) UUID (service)
- \value MessageAccessServer Message Access Profile (MAP) UUID (service)
- \value MessageNotificationServer Message Access Profile (MAP) UUID (service)
- \value MessageAccessProfile Message Access Profile (MAP) UUID (profile)
- \value GNSS Global Navigation Satellite System UUID (profile)
- \value GNSSServer Global Navigation Satellite System Server (UUID) (service)
- \value Display3D 3D Synchronization Display UUID (service)
- \value Glasses3D 3D Synchronization Glasses UUID (service)
- \value Synchronization3D 3D Synchronization UUID (profile)
- \value MPSProfile Multi-Profile Specification UUID (profile)
- \value MPSService Multi-Profile Specification UUID (service)
- \value PnPInformation Device Identification (DID) UUID (service & profile)
- \value GenericNetworking Generic networking UUID (service)
- \value GenericFileTransfer Generic file transfer UUID (service)
- \value GenericAudio Generic audio UUID (service)
- \value GenericTelephony Generic telephone UUID (service)
- \value VideoSource Video Distribution Profile (VDP) UUID (service)
- \value VideoSink Video Distribution Profile (VDP) UUID (service)
- \value VideoDistribution Video Distribution Profile (VDP) UUID (profile)
- \value HDP Health Device Profile (HDP) UUID (profile)
- \value HDPSource Health Device Profile Source (HDP) UUID (service)
- \value HDPSink Health Device Profile Sink (HDP) UUID (service)
-
- \sa QBluetoothServiceInfo::ServiceClassIds
+ \value HCRPrint Hardcopy Cable Replacement Profile (HCRP) (service)
+ \value HCRScan Hardcopy Cable Replacement Profile (HCRP) (service)
+ \value SIMAccess SIM Access Profile (SAP) UUID (service and profile)
+ \value PhonebookAccessPCE Phonebook Access Profile (PBAP) UUID (service)
+ \value PhonebookAccessPSE Phonebook Access Profile (PBAP) UUID (service)
+ \value PhonebookAccess Phonebook Access Profile (PBAP) (profile)
+ \value HeadsetHS Headset Profile (HSP) UUID (service)
+ \value MessageAccessServer Message Access Profile (MAP) UUID (service)
+ \value MessageNotificationServer Message Access Profile (MAP) UUID (service)
+ \value MessageAccessProfile Message Access Profile (MAP) UUID (profile)
+ \value GNSS Global Navigation Satellite System UUID (profile)
+ \value GNSSServer Global Navigation Satellite System Server (UUID) (service)
+ \value Display3D 3D Synchronization Display UUID (service)
+ \value Glasses3D 3D Synchronization Glasses UUID (service)
+ \value Synchronization3D 3D Synchronization UUID (profile)
+ \value MPSProfile Multi-Profile Specification UUID (profile)
+ \value MPSService Multi-Profile Specification UUID (service)
+ \value PnPInformation Device Identification (DID) UUID (service & profile)
+ \value GenericNetworking Generic networking UUID (service)
+ \value GenericFileTransfer Generic file transfer UUID (service)
+ \value GenericAudio Generic audio UUID (service)
+ \value GenericTelephony Generic telephone UUID (service)
+ \value VideoSource Video Distribution Profile (VDP) UUID (service)
+ \value VideoSink Video Distribution Profile (VDP) UUID (service)
+ \value VideoDistribution Video Distribution Profile (VDP) UUID (profile)
+ \value HDP Health Device Profile (HDP) UUID (profile)
+ \value HDPSource Health Device Profile Source (HDP) UUID (service)
+ \value HDPSink Health Device Profile Sink (HDP) UUID (service)
+ \value GenericAccess Generic access service for Bluetooth Low Energy devices UUID (service).
+ It contains generic information about the device. All available Characteristics are readonly.
+ \value GenericAttribute
+ \value ImmediateAlert Immediate Alert UUID (service). The service exposes a control point to allow a peer
+ device to cause the device to immediately alert.
+ \value LinkLoss Link Loss UUID (service). The service defines behavior when a link is lost between two devices.
+ \value TxPower Transmission Power UUID (service). The service exposes a device’s current
+ transmit power level when in a connection.
+ \value CurrentTimeService Current Time UUID (service). The service defines how the current time can be exposed using
+ the Generic Attribute Profile (GATT).
+ \value ReferenceTimeUpdateService Reference Time update UUID (service). The service defines how a client can request
+ an update from a reference time source from a time server.
+ \value NextDSTChangeService Next DST change UUID (service). The service defines how the information about an
+ upcoming DST change can be exposed.
+ \value Glucose Glucose UUID (service). The service exposes glucose and other data from a glucose sensor
+ for use in consumer and professional healthcare applications.
+ \value HealthThermometer Health Thermometer UUID (service). The Health Thermometer service exposes temperature
+ and other data from a thermometer intended for healthcare and fitness applications.
+ \value DeviceInformation Device Information UUID (service). The Device Information Service exposes manufacturer
+ and/or vendor information about a device.
+ \value HeartRate Heart Rate UUID (service). The service exposes the heart rate and other data from a
+ Heart Rate Sensor intended for fitness applications.
+ \value PhoneAlertStatusService Phone Alert Status UUID (service). The service exposes the phone alert status when
+ in a connection.
+ \value BatteryService Battery UUID (service). The Battery Service exposes the state of a battery within a device.
+ \value BloodPressure Blood Pressure UUID (service). The service exposes blood pressure and other data from a blood pressure
+ monitor intended for healthcare applications.
+ \value AlertNotificationService Alert Notification UUID (service). The Alert Notification service exposes alert
+ information on a device.
+ \value HumanInterfaceDevice Human Interface UUID (service). The service exposes the HID reports and other HID data
+ intended for HID Hosts and HID Devices.
+ \value ScanParameters Scan Parameters UUID (service). The Scan Parameters Service enables a GATT Server device
+ to expose a characteristic for the GATT Client to write its scan interval and scan window
+ on the GATT Server device.
+ \value RunningSpeedAndCadence Runnung Speed and Cadence UUID (service). The service exposes speed, cadence and other
+ data from a Running Speed and Cadence Sensor intended for fitness applications.
+ \value CyclingSpeedAndCadence Cycling Speed and Cadence UUID (service). The service exposes speed-related and
+ cadence-related data from a Cycling Speed and Cadence sensor intended for fitness
+ applications.
+ \value CyclingPower Cycling Speed UUID (service). The service exposes power- and force-related data and
+ optionally speed- and cadence-related data from a Cycling Power
+ sensor intended for sports and fitness applications.
+ \value LocationAndNavigation Location Navigation UUID (service). The service exposes location and navigation-related
+ data from a Location and Navigation sensor intended for outdoor activity applications.
+*/
+
+/*!
+ \enum QBluetoothUuid::CharacteristicType
+
+ This enum is a convienience type for Bluetooth low energy service characteristics class UUIDs. Values of this type
+ will be implicitly converted into a QBluetoothUuid when necessary.
+
+ \value AlertCategoryID Categories of alerts/messages.
+ \value AlertCategoryIDBitMask Categories of alerts/messages.
+ \value AlertLevel The level of an alert a device is to sound.
+ If this level is changed while the alert is being sounded,
+ the new level should take effect.
+ \value AlertNotificationControlPoint Control point of the Alert Notification server.
+ Client can write the command here to request the several
+ functions toward the server.
+ \value AlertStatus The Alert Status characteristic defines the Status of alert.
+ \value Appearance The external appearance of this device. The values are composed
+ of a category (10-bits) and sub-categories (6-bits).
+ \value BatteryLevel The current charge level of a battery. 100% represents fully charged
+ while 0% represents fully discharged.
+ \value BloodPressureFeature The Blood Pressure Feature characteristic is used to describe the supported
+ features of the Blood Pressure Sensor.
+ \value BloodPressureMeasurement The Blood Pressure Measurement characteristic is a variable length structure
+ containing a Flags field, a Blood Pressure Measurement Compound Value field,
+ and contains additional fields such as Time Stamp, Pulse Rate and User ID
+ as determined by the contents of the Flags field.
+ \value BodySensorLocation
+ \value BootKeyboardInputReport The Boot Keyboard Input Report characteristic is used to transfer fixed format
+ and length Input Report data between a HID Host operating in Boot Protocol Mode
+ and a HID Service corresponding to a boot keyboard.
+ \value BootKeyboardOutputReport The Boot Keyboard Output Report characteristic is used to transfer fixed format
+ and length Output Report data between a HID Host operating in Boot Protocol Mode
+ and a HID Service corresponding to a boot keyboard.
+ \value BootMouseInputReport The Boot Mouse Input Report characteristic is used to transfer fixed format and
+ length Input Report data between a HID Host operating in Boot Protocol Mode and
+ a HID Service corresponding to a boot mouse.
+ \value CSCFeature The CSC (Cycling Speed and Cadence) Feature characteristic is used to describe
+ the supported features of the Server.
+ \value CSCMeasurement The CSC Measurement characteristic (CSC refers to Cycling Speed and Cadence)
+ is a variable length structure containing a Flags field and, based on the contents
+ of the Flags field, may contain one or more additional fields as shown in the tables
+ below.
+ \value CurrentTime
+ \value CyclingPowerControlPoint The Cycling Power Control Point characteristic is used to request a specific function
+ to be executed on the receiving device.
+ \value CyclingPowerFeature The CP Feature characteristic is used to report a list of features supported by
+ the device.
+ \value CyclingPowerMeasurement The Cycling Power Measurement characteristic is a variable length structure containing
+ a Flags field, an Instantaneous Power field and, based on the contents of the Flags
+ field, may contain one or more additional fields as shown in the table below.
+ \value CyclingPowerVector The Cycling Power Vector characteristic is a variable length structure containing
+ a Flags fieldand based on the contents of the Flags field, may contain one or more
+ additional fields as shown in the table below.
+ \value DateTime The Date Time characteristic is used to represent time.
+ \value DayDateTime
+ \value DayOfWeek
+ \value DeviceName
+ \value DSTOffset
+ \value ExactTime256
+ \value FirmwareRevisionString The value of this characteristic is a UTF-8 string representing the firmware revision
+ for the firmware within the device.
+ \value GlucoseFeature The Glucose Feature characteristic is used to describe the supported features
+ of the Server. When read, the Glucose Feature characteristic returns a value
+ that is used by a Client to determine the supported features of the Server.
+ \value GlucoseMeasurement The Glucose Measurement characteristic is a variable length structure containing
+ a Flags field, a Sequence Number field, a Base Time field and, based upon the contents
+ of the Flags field, may contain a Time Offset field, Glucose Concentration field,
+ Type-Sample Location field and a Sensor Status Annunciation field.
+ \value GlucoseMeasurementContext
+ \value HardwareRevisionString The value of this characteristic is a UTF-8 string representing the hardware revision
+ for the hardware within the device.
+ \value HeartRateControlPoint
+ \value HeartRateMeasurement
+ \value HIDControlPoint The HID Control Point characteristic is a control-point attribute that defines the
+ HID Commands when written.
+ \value HIDInformation The HID Information Characteristic returns the HID attributes when read.
+ \value IEEE1107320601RegulatoryCertificationDataList The value of the characteristic is an opaque structure listing
+ various regulatory and/or certification compliance items to which the device
+ claims adherence.
+ \value IntermediateCuffPressure This characteristic has the same format as the Blood Pressure Measurement
+ characteristic.
+ \value IntermediateTemperature The Intermediate Temperature characteristic has the same format as the
+ Temperature Measurement characteristic
+ \value LNControlPoint The LN Control Point characteristic is used to request a specific function
+ to be executed on the receiving device.
+ \value LNFeature The LN Feature characteristic is used to report a list of features supported
+ by the device.
+ \value LocalTimeInformation
+ \value LocationAndSpeed The Location and Speed characteristic is a variable length structure containing
+ a Flags field and, based on the contents of the Flags field, may contain a combination
+ of data fields.
+ \value ManufacturerNameString The value of this characteristic is a UTF-8 string representing the name of the
+ manufacturer of the device.
+ \value MeasurementInterval The Measurement Interval characteristic defines the time between measurements.
+ \value ModelNumberString The value of this characteristic is a UTF-8 string representing the model number
+ assigned by the device vendor.
+ \value Navigation The Navigation characteristic is a variable length structure containing a Flags field,
+ a Bearing field, a Heading field and, based on the contents of the Flags field.
+ \value NewAlert This characteristic defines the category of the alert and how many new alerts of
+ that category have occurred in the server device.
+ \value PeripheralPreferredConnectionParameters
+ \value PeripheralPrivacyFlag
+ \value PnPID The PnP_ID characteristic returns its value when read using the GATT Characteristic
+ Value Read procedure.
+ \value PositionQuality The Position Quality characteristic is a variable length structure containing a
+ Flags field and at least one of the optional data
+ \value ProtocolMode The Protocol Mode characteristic is used to expose the current protocol mode of
+ the HID Service with which it is associated, or to set the desired protocol
+ mode of the HID Service.
+ \value ReconnectionAddress The Information included in this page is informative. The normative descriptions
+ are contained in the applicable specification.
+ \value RecordAccessControlPoint This control point is used with a service to provide basic management functionality
+ for the Glucose Sensor patient record database.
+ \value ReferenceTimeInformation
+ \value Report The Report characteristic is used to exchange data between a HID Device and a HID Host.
+ \value ReportMap Only a single instance of this characteristic exists as part of a HID Service.
+ \value RingerControlPoint The Ringer Control Point characteristic defines the Control Point of Ringer.
+ \value RingerSetting The Ringer Setting characteristic defines the Setting of the Ringer.
+ \value RSCFeature The RSC (Running Speed and Cadence) Feature characteristic is used to describe the
+ supported features of the Server.
+ \value RSCMeasurement RSC refers to Running Speed and Cadence.
+ \value SCControlPoint The SC Control Point characteristic is used to request a specific function to be
+ executed on the receiving device.
+ \value ScanIntervalWindow The Scan Interval Window characteristic is used to store the scan parameters of
+ the GATT Client.
+ \value ScanRefresh The Scan Refresh characteristic is used to notify the Client that the Server
+ requires the Scan Interval Window characteristic to be written with the latest
+ values upon notification.
+ \value SensorLocation The Sensor Location characteristic is used to expose the location of the sensor.
+ \value SerialNumberString The value of this characteristic is a variable-length UTF-8 string representing
+ the serial number for a particular instance of the device.
+ \value ServiceChanged
+ \value SoftwareRevisionString The value of this characteristic is a UTF-8 string representing the software
+ revision for the software within the device.
+ \value SupportedNewAlertCategory Category that the server supports for new alert.
+ \value SupportedUnreadAlertCategory Category that the server supports for unread alert.
+ \value SystemID If the system ID is based of a Bluetooth Device Address with a Company Identifier
+ (OUI) is 0x123456 and the Company Assigned Identifier is 0x9ABCDE, then the System
+ Identifier is required to be 0x123456FFFE9ABCDE.
+ \value TemperatureMeasurement The Temperature Measurement characteristic is a variable length structure containing
+ a Flags field, a Temperature Measurement Value field and, based upon the contents of
+ the Flags field, optionally a Time Stamp field and/or a Temperature Type field.
+ \value TemperatureType The Temperature Type characteristic is an enumeration that indicates where the
+ temperature was measured.
+ \value TimeAccuracy
+ \value TimeSource
+ \value TimeUpdateControlPoint
+ \value TimeUpdateState
+ \value TimeWithDST
+ \value TimeZone
+ \value TxPowerLevel The value of the characteristic is a signed 8 bit integer that has a fixed point
+ exponent of 0.
+ \value UnreadAlertStatus This characteristic shows how many numbers of unread alerts exist in the specific
+ category in the device.
+*/
+
+/*!
+ \enum QBluetoothUuid::DescriptorType
+
+ Descriptors are attributes that describe Bluetooth Low Energy characteristic values.
+
+ This enum is a convienience type for descriptor class UUIDs. Values of this type
+ will be implicitly converted into a QBluetoothUuid when necessary.
+
+ \value CharacteristicExtendedProperties Descriptor defines additional Characteristic Properties.
+ \value CharacteristicUserDescription Descriptor provides a textual user description for a characteristic value.
+ \value ClientCharacteristicConfiguration Descriptor defines how the characteristic may be configured by a specific client.
+ \value ServerCharacteristicConfiguration Descriptor defines how the characteristic descriptor is associated with may be
+ configured for the server.
+ \value CharacteristicPresentationFormat Descriptor defines the format of the Characteristic Value.
+ \value CharacteristicAggregateFormat Descriptor defines the format of an aggregated Characteristic Value.
+ \value ValidRange descriptor is used for defining the range of a characteristics.
+ Two mandatory fields are contained (upper and lower bounds) which define the range.
+ \value ExternalReportReference Allows a HID Host to map information from the Report Map characteristic value for
+ Input Report, Output Report or Feature Report data to the Characteristic UUID of
+ external service characteristics used to transfer the associated data.
+ \value ReportReference Mapping information in the form of a Report ID and Report Type which maps the
+ current parent characteristic to the Report ID(s) and Report Type (s) defined
+ within the Report Map characteristic.
*/
/*!
@@ -206,6 +428,27 @@ QBluetoothUuid::QBluetoothUuid(ServiceClassUuid uuid)
}
/*!
+ Constructs a new Bluetooth UUID from the characteristic type \a uuid.
+*/
+QBluetoothUuid::QBluetoothUuid(CharacteristicType uuid)
+: QUuid(uuid, baseUuid()->data2, baseUuid()->data3, baseUuid()->data4[0], baseUuid()->data4[1],
+ baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5],
+ baseUuid()->data4[6], baseUuid()->data4[7])
+{
+}
+
+/*!
+ Constructs a new Bluetooth UUID from the descriptor type \a uuid.
+*/
+QBluetoothUuid::QBluetoothUuid(DescriptorType uuid)
+ : QUuid(uuid, baseUuid()->data2, baseUuid()->data3, baseUuid()->data4[0], baseUuid()->data4[1],
+ baseUuid()->data4[2], baseUuid()->data4[3], baseUuid()->data4[4], baseUuid()->data4[5],
+ baseUuid()->data4[6], baseUuid()->data4[7])
+{
+
+}
+
+/*!
Constructs a new Bluetooth UUID from the 16 bit \a uuid.
*/
QBluetoothUuid::QBluetoothUuid(quint16 uuid)
@@ -227,6 +470,8 @@ QBluetoothUuid::QBluetoothUuid(quint32 uuid)
/*!
Constructs a new Bluetooth UUID from the 128 bit \a uuid.
+
+ Note that \a uuid must be in big endian order.
*/
QBluetoothUuid::QBluetoothUuid(quint128 uuid)
{
@@ -437,6 +682,28 @@ QString QBluetoothUuid::serviceClassToString(QBluetoothUuid::ServiceClassUuid uu
case QBluetoothUuid::HDP: return QBluetoothServiceDiscoveryAgent::tr("Health Device");
case QBluetoothUuid::HDPSource: return QBluetoothServiceDiscoveryAgent::tr("Health Device Source");
case QBluetoothUuid::HDPSink: return QBluetoothServiceDiscoveryAgent::tr("Health Device Sink");
+ case QBluetoothUuid::GenericAccess: return QBluetoothServiceDiscoveryAgent::tr("Generic Access");
+ case QBluetoothUuid::GenericAttribute: return QBluetoothServiceDiscoveryAgent::tr("Generic Attribute");
+ case QBluetoothUuid::ImmediateAlert: return QBluetoothServiceDiscoveryAgent::tr("Immediate Alert");
+ case QBluetoothUuid::LinkLoss: return QBluetoothServiceDiscoveryAgent::tr("Link Loss");
+ case QBluetoothUuid::TxPower: return QBluetoothServiceDiscoveryAgent::tr("Tx Power");
+ case QBluetoothUuid::CurrentTimeService: return QBluetoothServiceDiscoveryAgent::tr("Current Time Service");
+ case QBluetoothUuid::ReferenceTimeUpdateService: return QBluetoothServiceDiscoveryAgent::tr("Reference Time Update Service");
+ case QBluetoothUuid::NextDSTChangeService: return QBluetoothServiceDiscoveryAgent::tr("Next DST Change Service");
+ case QBluetoothUuid::Glucose: return QBluetoothServiceDiscoveryAgent::tr("Glucose");
+ case QBluetoothUuid::HealthThermometer: return QBluetoothServiceDiscoveryAgent::tr("Health Thermometer");
+ case QBluetoothUuid::DeviceInformation: return QBluetoothServiceDiscoveryAgent::tr("Device Information");
+ case QBluetoothUuid::HeartRate: return QBluetoothServiceDiscoveryAgent::tr("Heart Rate");
+ case QBluetoothUuid::PhoneAlertStatusService: return QBluetoothServiceDiscoveryAgent::tr("Phone Alert Status Service");
+ case QBluetoothUuid::BatteryService: return QBluetoothServiceDiscoveryAgent::tr("Battery Service");
+ case QBluetoothUuid::BloodPressure: return QBluetoothServiceDiscoveryAgent::tr("Blood Pressure");
+ case QBluetoothUuid::AlertNotificationService: return QBluetoothServiceDiscoveryAgent::tr("Alert Notification Service");
+ case QBluetoothUuid::HumanInterfaceDevice: return QBluetoothServiceDiscoveryAgent::tr("Human Interface Device");
+ case QBluetoothUuid::ScanParameters: return QBluetoothServiceDiscoveryAgent::tr("Scan Parameters");
+ case QBluetoothUuid::RunningSpeedAndCadence: return QBluetoothServiceDiscoveryAgent::tr("Running Speed and Cadance");
+ case QBluetoothUuid::CyclingSpeedAndCadence: return QBluetoothServiceDiscoveryAgent::tr("Cycling Speed and Cadance");
+ case QBluetoothUuid::CyclingPower: return QBluetoothServiceDiscoveryAgent::tr("Cycling Power");
+ case QBluetoothUuid::LocationAndNavigation: return QBluetoothServiceDiscoveryAgent::tr("Location and Navigation");
default:
break;
}
@@ -451,7 +718,7 @@ QString QBluetoothUuid::serviceClassToString(QBluetoothUuid::ServiceClassUuid uu
\sa QBluetoothUuid::ProtocolUuid
- \since Qt 5.4
+ \since 5.4
*/
QString QBluetoothUuid::protocolToString(QBluetoothUuid::ProtocolUuid uuid)
{
@@ -489,6 +756,161 @@ QString QBluetoothUuid::protocolToString(QBluetoothUuid::ProtocolUuid uuid)
}
/*!
+ Returns a human-readable and translated name for the given characteristic type
+ represented by \a uuid.
+
+ \sa QBluetoothUuid::CharacteristicType
+
+ \since 5.4
+*/
+QString QBluetoothUuid::characteristicToString(CharacteristicType uuid)
+{
+ switch (uuid) {
+ case QBluetoothUuid::DeviceName: return QBluetoothServiceDiscoveryAgent::tr("GAP Device Name");
+ case QBluetoothUuid::Appearance: return QBluetoothServiceDiscoveryAgent::tr("GAP Appearance");
+ case QBluetoothUuid::PeripheralPrivacyFlag:
+ return QBluetoothServiceDiscoveryAgent::tr("GAP Peripheral Privacy Flag");
+ case QBluetoothUuid::ReconnectionAddress:
+ return QBluetoothServiceDiscoveryAgent::tr("GAP Reconnection Address");
+ case QBluetoothUuid::PeripheralPreferredConnectionParameters:
+ return QBluetoothServiceDiscoveryAgent::tr("GAP Peripheral Preferred Connection Parameters");
+ case QBluetoothUuid::ServiceChanged: return QBluetoothServiceDiscoveryAgent::tr("GATT Service Changed");
+ case QBluetoothUuid::AlertLevel: return QBluetoothServiceDiscoveryAgent::tr("Alert Level");
+ case QBluetoothUuid::TxPowerLevel: return QBluetoothServiceDiscoveryAgent::tr("TX Power");
+ case QBluetoothUuid::DateTime: return QBluetoothServiceDiscoveryAgent::tr("Date Time");
+ case QBluetoothUuid::DayOfWeek: return QBluetoothServiceDiscoveryAgent::tr("Day Of Week");
+ case QBluetoothUuid::DayDateTime: return QBluetoothServiceDiscoveryAgent::tr("Day Date Time");
+ case QBluetoothUuid::ExactTime256: return QBluetoothServiceDiscoveryAgent::tr("Exact Time 256");
+ case QBluetoothUuid::DSTOffset: return QBluetoothServiceDiscoveryAgent::tr("DST Offset");
+ case QBluetoothUuid::TimeZone: return QBluetoothServiceDiscoveryAgent::tr("Time Zone");
+ case QBluetoothUuid::LocalTimeInformation:
+ return QBluetoothServiceDiscoveryAgent::tr("Local Time Information");
+ case QBluetoothUuid::TimeWithDST: return QBluetoothServiceDiscoveryAgent::tr("Time With DST");
+ case QBluetoothUuid::TimeAccuracy: return QBluetoothServiceDiscoveryAgent::tr("Time Accuracy");
+ case QBluetoothUuid::TimeSource: return QBluetoothServiceDiscoveryAgent::tr("Time Source");
+ case QBluetoothUuid::ReferenceTimeInformation:
+ return QBluetoothServiceDiscoveryAgent::tr("Reference Time Information");
+ case QBluetoothUuid::TimeUpdateControlPoint:
+ return QBluetoothServiceDiscoveryAgent::tr("Time Update Control Point");
+ case QBluetoothUuid::TimeUpdateState: return QBluetoothServiceDiscoveryAgent::tr("Time Update State");
+ case QBluetoothUuid::GlucoseMeasurement: return QBluetoothServiceDiscoveryAgent::tr("Glucose Measurement");
+ case QBluetoothUuid::BatteryLevel: return QBluetoothServiceDiscoveryAgent::tr("Battery Level");
+ case QBluetoothUuid::TemperatureMeasurement:
+ return QBluetoothServiceDiscoveryAgent::tr("Temperature Measurement");
+ case QBluetoothUuid::TemperatureType: return QBluetoothServiceDiscoveryAgent::tr("Temperature Type");
+ case QBluetoothUuid::IntermediateTemperature:
+ return QBluetoothServiceDiscoveryAgent::tr("Intermediate Temperature");
+ case QBluetoothUuid::MeasurementInterval: return QBluetoothServiceDiscoveryAgent::tr("Measurement Interval");
+ case QBluetoothUuid::BootKeyboardInputReport: return QBluetoothServiceDiscoveryAgent::tr("Boot Keyboard Input Report");
+ case QBluetoothUuid::SystemID: return QBluetoothServiceDiscoveryAgent::tr("System ID");
+ case QBluetoothUuid::ModelNumberString: return QBluetoothServiceDiscoveryAgent::tr("Model Number String");
+ case QBluetoothUuid::SerialNumberString: return QBluetoothServiceDiscoveryAgent::tr("Serial Number String");
+ case QBluetoothUuid::FirmwareRevisionString: return QBluetoothServiceDiscoveryAgent::tr("Firmware Revision String");
+ case QBluetoothUuid::HardwareRevisionString: return QBluetoothServiceDiscoveryAgent::tr("Hardware Revision String");
+ case QBluetoothUuid::SoftwareRevisionString: return QBluetoothServiceDiscoveryAgent::tr("Software Revision String");
+ case QBluetoothUuid::ManufacturerNameString: return QBluetoothServiceDiscoveryAgent::tr("Manufacturer Name String");
+ case QBluetoothUuid::IEEE1107320601RegulatoryCertificationDataList:
+ return QBluetoothServiceDiscoveryAgent::tr("IEEE 11073 20601 Regulatory Certification Data List");
+ case QBluetoothUuid::CurrentTime: return QBluetoothServiceDiscoveryAgent::tr("Current Time");
+ case QBluetoothUuid::ScanRefresh: return QBluetoothServiceDiscoveryAgent::tr("Scan Refresh");
+ case QBluetoothUuid::BootKeyboardOutputReport:
+ return QBluetoothServiceDiscoveryAgent::tr("Boot Keyboard Output Report");
+ case QBluetoothUuid::BootMouseInputReport: return QBluetoothServiceDiscoveryAgent::tr("Boot Mouse Input Report");
+ case QBluetoothUuid::GlucoseMeasurementContext:
+ return QBluetoothServiceDiscoveryAgent::tr("Glucose Measurement Context");
+ case QBluetoothUuid::BloodPressureMeasurement:
+ return QBluetoothServiceDiscoveryAgent::tr("Blood Pressure Measurement");
+ case QBluetoothUuid::IntermediateCuffPressure:
+ return QBluetoothServiceDiscoveryAgent::tr("Intermediate Cuff Pressure");
+ case QBluetoothUuid::HeartRateMeasurement: return QBluetoothServiceDiscoveryAgent::tr("Heart Rate Measurement");
+ case QBluetoothUuid::BodySensorLocation: return QBluetoothServiceDiscoveryAgent::tr("Body Sensor Location");
+ case QBluetoothUuid::HeartRateControlPoint: return QBluetoothServiceDiscoveryAgent::tr("Heart Rate Control Point");
+ case QBluetoothUuid::AlertStatus: return QBluetoothServiceDiscoveryAgent::tr("Alert Status");
+ case QBluetoothUuid::RingerControlPoint: return QBluetoothServiceDiscoveryAgent::tr("Ringer Control Point");
+ case QBluetoothUuid::RingerSetting: return QBluetoothServiceDiscoveryAgent::tr("Ringer Setting");
+ case QBluetoothUuid::AlertCategoryIDBitMask:
+ return QBluetoothServiceDiscoveryAgent::tr("Alert Category ID Bit Mask");
+ case QBluetoothUuid::AlertCategoryID: return QBluetoothServiceDiscoveryAgent::tr("Alert Category ID");
+ case QBluetoothUuid::AlertNotificationControlPoint:
+ return QBluetoothServiceDiscoveryAgent::tr("Alert Notification Control Point");
+ case QBluetoothUuid::UnreadAlertStatus: return QBluetoothServiceDiscoveryAgent::tr("Unread Alert Status");
+ case QBluetoothUuid::NewAlert: return QBluetoothServiceDiscoveryAgent::tr("New Alert");
+ case QBluetoothUuid::SupportedNewAlertCategory:
+ return QBluetoothServiceDiscoveryAgent::tr("Supported New Alert Category");
+ case QBluetoothUuid::SupportedUnreadAlertCategory:
+ return QBluetoothServiceDiscoveryAgent::tr("Supported Unread Alert Category");
+ case QBluetoothUuid::BloodPressureFeature: return QBluetoothServiceDiscoveryAgent::tr("Blood Pressure Feature");
+ case QBluetoothUuid::HIDInformation: return QBluetoothServiceDiscoveryAgent::tr("HID Information");
+ case QBluetoothUuid::ReportMap: return QBluetoothServiceDiscoveryAgent::tr("Report Map");
+ case QBluetoothUuid::HIDControlPoint: return QBluetoothServiceDiscoveryAgent::tr("HID Control Point");
+ case QBluetoothUuid::Report: return QBluetoothServiceDiscoveryAgent::tr("Report");
+ case QBluetoothUuid::ProtocolMode: return QBluetoothServiceDiscoveryAgent::tr("Protocol Mode");
+ case QBluetoothUuid::ScanIntervalWindow: return QBluetoothServiceDiscoveryAgent::tr("Scan Interval Window");
+ case QBluetoothUuid::PnPID: return QBluetoothServiceDiscoveryAgent::tr("PnP ID");
+ case QBluetoothUuid::GlucoseFeature: return QBluetoothServiceDiscoveryAgent::tr("Glucose Feature");
+ case QBluetoothUuid::RecordAccessControlPoint:
+ return QBluetoothServiceDiscoveryAgent::tr("Record Access Control Point");
+ case QBluetoothUuid::RSCMeasurement: return QBluetoothServiceDiscoveryAgent::tr("RSC Measurement");
+ case QBluetoothUuid::RSCFeature: return QBluetoothServiceDiscoveryAgent::tr("RSC Feature");
+ case QBluetoothUuid::SCControlPoint: return QBluetoothServiceDiscoveryAgent::tr("SC Control Point");
+ case QBluetoothUuid::CSCMeasurement: return QBluetoothServiceDiscoveryAgent::tr("CSC Measurement");
+ case QBluetoothUuid::CSCFeature: return QBluetoothServiceDiscoveryAgent::tr("CSC Feature");
+ case QBluetoothUuid::SensorLocation: return QBluetoothServiceDiscoveryAgent::tr("Sensor Location");
+ case QBluetoothUuid::CyclingPowerMeasurement:
+ return QBluetoothServiceDiscoveryAgent::tr("Cycling Power Measurement");
+ case QBluetoothUuid::CyclingPowerVector: return QBluetoothServiceDiscoveryAgent::tr("Cycling Power Vector");
+ case QBluetoothUuid::CyclingPowerFeature: return QBluetoothServiceDiscoveryAgent::tr("Cycling Power Feature");
+ case QBluetoothUuid::CyclingPowerControlPoint:
+ return QBluetoothServiceDiscoveryAgent::tr("Cycling Power COntrol Point");
+ case QBluetoothUuid::LocationAndSpeed: return QBluetoothServiceDiscoveryAgent::tr("Location And Speed");
+ case QBluetoothUuid::Navigation: return QBluetoothServiceDiscoveryAgent::tr("Navigation");
+ case QBluetoothUuid::PositionQuality: return QBluetoothServiceDiscoveryAgent::tr("Position Quality");
+ case QBluetoothUuid::LNFeature: return QBluetoothServiceDiscoveryAgent::tr("LN Feature");
+ case QBluetoothUuid::LNControlPoint: return QBluetoothServiceDiscoveryAgent::tr("LN Control Point");
+ default:
+ break;
+ }
+
+ return QString();
+}
+
+/*!
+ Returns a human-readable and translated name for the given descriptor type
+ represented by \a uuid.
+
+ \sa QBluetoothUuid::CharacteristicType
+
+ \since 5.4
+*/
+QString QBluetoothUuid::descriptorToString(QBluetoothUuid::DescriptorType uuid)
+{
+ switch (uuid) {
+ case QBluetoothUuid::CharacteristicExtendedProperties:
+ return QBluetoothServiceDiscoveryAgent::tr("Characteristic Extended Properties");
+ case QBluetoothUuid::CharacteristicUserDescription:
+ return QBluetoothServiceDiscoveryAgent::tr("Characteristic User Description");
+ case QBluetoothUuid::ClientCharacteristicConfiguration:
+ return QBluetoothServiceDiscoveryAgent::tr("Client Characteristic Configuration");
+ case QBluetoothUuid::ServerCharacteristicConfiguration:
+ return QBluetoothServiceDiscoveryAgent::tr("Server Characteristic Configuratio");
+ case QBluetoothUuid::CharacteristicPresentationFormat:
+ return QBluetoothServiceDiscoveryAgent::tr("Characteristic Presentation Format");
+ case QBluetoothUuid::CharacteristicAggregateFormat:
+ return QBluetoothServiceDiscoveryAgent::tr("Characteristic Aggregate Format");
+ case QBluetoothUuid::ValidRange:
+ return QBluetoothServiceDiscoveryAgent::tr("Valid Range");
+ case QBluetoothUuid::ExternalReportReference:
+ return QBluetoothServiceDiscoveryAgent::tr("External Report Reference");
+ case QBluetoothUuid::ReportReference:
+ return QBluetoothServiceDiscoveryAgent::tr("Report Reference");
+ default:
+ break;
+ }
+
+ return QString();
+}
+
+/*!
Returns true if \a other is equal to this Bluetooth UUID, otherwise false.
*/
bool QBluetoothUuid::operator==(const QBluetoothUuid &other) const
diff --git a/src/bluetooth/qbluetoothuuid.h b/src/bluetooth/qbluetoothuuid.h
index 957a4ed2..274e98ab 100644
--- a/src/bluetooth/qbluetoothuuid.h
+++ b/src/bluetooth/qbluetoothuuid.h
@@ -151,12 +151,133 @@ public:
VideoDistribution = 0x1305,
HDP = 0x1400,
HDPSource = 0x1401,
- HDPSink = 0x1402
+ HDPSink = 0x1402,
+ GenericAccess = 0x1800,
+ GenericAttribute = 0x1801,
+ ImmediateAlert = 0x1802,
+ LinkLoss = 0x1803,
+ TxPower = 0x1804,
+ CurrentTimeService = 0x1805,
+ ReferenceTimeUpdateService = 0x1806,
+ NextDSTChangeService = 0x1807,
+ Glucose = 0x1808,
+ HealthThermometer = 0x1809,
+ DeviceInformation = 0x180a,
+ HeartRate = 0x180d,
+ PhoneAlertStatusService = 0x180e,
+ BatteryService = 0x180f,
+ BloodPressure = 0x1810,
+ AlertNotificationService = 0x1811,
+ HumanInterfaceDevice = 0x1812,
+ ScanParameters = 0x1813,
+ RunningSpeedAndCadence = 0x1814,
+ CyclingSpeedAndCadence = 0x1816,
+ CyclingPower = 0x1818,
+ LocationAndNavigation = 0x1819,
+ };
+
+ enum CharacteristicType {
+ DeviceName = 0x2a00,
+ Appearance = 0x2a01,
+ PeripheralPrivacyFlag = 0x2a02,
+ ReconnectionAddress = 0x2a03,
+ PeripheralPreferredConnectionParameters = 0x2a04,
+ ServiceChanged = 0x2a05,
+ AlertLevel = 0x2a06,
+ TxPowerLevel = 0x2a07,
+ DateTime = 0x2a08,
+ DayOfWeek = 0x2a09,
+ DayDateTime = 0x2a0a,
+ ExactTime256 = 0x2a0c,
+ DSTOffset = 0x2a0d,
+ TimeZone = 0x2a0e,
+ LocalTimeInformation = 0x2a0f,
+ TimeWithDST = 0x2a11,
+ TimeAccuracy = 0x2a12,
+ TimeSource = 0x2a13,
+ ReferenceTimeInformation = 0x2a14,
+ TimeUpdateControlPoint = 0x2a16,
+ TimeUpdateState = 0x2a17,
+ GlucoseMeasurement = 0x2a18,
+ BatteryLevel = 0x2a19,
+ TemperatureMeasurement = 0x2a1c,
+ TemperatureType = 0x2a1d,
+ IntermediateTemperature = 0x2a1e,
+ MeasurementInterval = 0x2a21,
+ BootKeyboardInputReport = 0x2a22,
+ SystemID = 0x2a23,
+ ModelNumberString = 0x2a24,
+ SerialNumberString = 0x2a25,
+ FirmwareRevisionString = 0x2a26,
+ HardwareRevisionString = 0x2a27,
+ SoftwareRevisionString = 0x2a28,
+ ManufacturerNameString = 0x2a29,
+ IEEE1107320601RegulatoryCertificationDataList = 0x2a2a,
+ CurrentTime = 0x2a2b,
+ ScanRefresh = 0x2a31,
+ BootKeyboardOutputReport = 0x2a32,
+ BootMouseInputReport = 0x2a33,
+ GlucoseMeasurementContext = 0x2a34,
+ BloodPressureMeasurement = 0x2a35,
+ IntermediateCuffPressure = 0x2a36,
+ HeartRateMeasurement = 0x2a37,
+ BodySensorLocation = 0x2a38,
+ HeartRateControlPoint = 0x2a39,
+ AlertStatus = 0x2a3f,
+ RingerControlPoint = 0x2a40,
+ RingerSetting = 0x2a41,
+ AlertCategoryIDBitMask = 0x2a42,
+ AlertCategoryID = 0x2a43,
+ AlertNotificationControlPoint = 0x2a44,
+ UnreadAlertStatus = 0x2a45,
+ NewAlert = 0x2a46,
+ SupportedNewAlertCategory = 0x2a47,
+ SupportedUnreadAlertCategory = 0x2a48,
+ BloodPressureFeature = 0x2a49,
+ HIDInformation = 0x2a4a,
+ ReportMap = 0x2a4b,
+ HIDControlPoint = 0x2a4c,
+ Report = 0x2a4d,
+ ProtocolMode = 0x2a4e,
+ ScanIntervalWindow = 0x2a4f,
+ PnPID = 0x2a50,
+ GlucoseFeature = 0x2a51,
+ RecordAccessControlPoint = 0x2a52,
+ RSCMeasurement = 0x2a53,
+ RSCFeature = 0x2a54,
+ SCControlPoint = 0x2a55,
+ CSCMeasurement = 0x2a5b,
+ CSCFeature = 0x2a5c,
+ SensorLocation = 0x2a5d,
+ CyclingPowerMeasurement = 0x2a63,
+ CyclingPowerVector = 0x2a64,
+ CyclingPowerFeature = 0x2a65,
+ CyclingPowerControlPoint = 0x2a66,
+ LocationAndSpeed = 0x2a67,
+ Navigation = 0x2a68,
+ PositionQuality = 0x2a69,
+ LNFeature = 0x2a6a,
+ LNControlPoint = 0x2a6b,
+ };
+
+ enum DescriptorType {
+ UnknownDescriptorType = 0x0,
+ CharacteristicExtendedProperties = 0x2900,
+ CharacteristicUserDescription = 0x2901,
+ ClientCharacteristicConfiguration = 0x2902,
+ ServerCharacteristicConfiguration = 0x2903,
+ CharacteristicPresentationFormat = 0x2904,
+ CharacteristicAggregateFormat = 0x2905,
+ ValidRange = 0x2906,
+ ExternalReportReference = 0x2907,
+ ReportReference = 0x2908
};
QBluetoothUuid();
QBluetoothUuid(ProtocolUuid uuid);
QBluetoothUuid(ServiceClassUuid uuid);
+ QBluetoothUuid(CharacteristicType uuid);
+ QBluetoothUuid(DescriptorType uuid);
explicit QBluetoothUuid(quint16 uuid);
explicit QBluetoothUuid(quint32 uuid);
explicit QBluetoothUuid(quint128 uuid);
@@ -175,6 +296,8 @@ public:
static QString serviceClassToString(ServiceClassUuid uuid);
static QString protocolToString(ProtocolUuid uuid);
+ static QString characteristicToString(CharacteristicType uuid);
+ static QString descriptorToString(DescriptorType uuid);
};
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/bluetooth/qlowenergycharacteristic.cpp b/src/bluetooth/qlowenergycharacteristic.cpp
new file mode 100644
index 00000000..0c3dc7af
--- /dev/null
+++ b/src/bluetooth/qlowenergycharacteristic.cpp
@@ -0,0 +1,325 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergycharacteristic.h"
+#include "qlowenergyserviceprivate_p.h"
+#include <QHash>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QLowEnergyCharacteristic
+ \inmodule QtBluetooth
+ \brief The QLowEnergyCharacteristic class stores information about a Bluetooth
+ Low Energy service characteristic.
+
+ \since 5.4
+
+ QLowEnergyCharacteristic provides information about a Bluetooth Low Energy
+ service characteristic's name, UUID, value, permissions, handle and descriptors.
+ To get the full characteristic specification and information it is necessary to
+ connect to the service using QLowEnergyServiceInfo and QLowEnergyController classes.
+ Some characteristics can contain none, one or more descriptors.
+*/
+
+/*!
+ \enum QLowEnergyCharacteristic::PropertyType
+
+ This enum describes the properties of a characteristic.
+
+ \value Unknown The type is not known.
+ \value Broadcasting Allow for the broadcasting of Generic Attributes (GATT) characteristic values.
+ \value Read Allow the characteristic values to be read.
+ \value WriteNoResponse Allow characteristic values without responses to be written.
+ \value Write Allow for characteristic values to be written.
+ \value Notify Permits notification of characteristic values.
+ \value Indicate Permits indications of characteristic values.
+ \value WriteSigned Permits signed writes of the GATT characteristic values.
+ \value ExtendedProperty Additional characteristic properties are defined in the characteristic
+ extended properties descriptor.
+
+ \sa properties()
+*/
+
+struct QLowEnergyCharacteristicPrivate
+{
+ QLowEnergyHandle handle;
+};
+
+/*!
+ Construct a new QLowEnergyCharacteristic.
+*/
+QLowEnergyCharacteristic::QLowEnergyCharacteristic():
+ d_ptr(0), data(0)
+{
+
+}
+
+/*!
+ Construct a new QLowEnergyCharacteristic that is a copy of \a other.
+
+ The two copies continue to share the same underlying data which does not detach
+ upon write.
+*/
+QLowEnergyCharacteristic::QLowEnergyCharacteristic(const QLowEnergyCharacteristic &other):
+ d_ptr(other.d_ptr), data(0)
+{
+ if (other.data) {
+ data = new QLowEnergyCharacteristicPrivate();
+ data->handle = other.data->handle;
+ }
+}
+
+/*!
+ \internal
+*/
+QLowEnergyCharacteristic::QLowEnergyCharacteristic(
+ QSharedPointer<QLowEnergyServicePrivate> p, QLowEnergyHandle handle):
+ d_ptr(p)
+{
+ data = new QLowEnergyCharacteristicPrivate();
+ data->handle = handle;
+}
+
+/*!
+ Destroys the QLowEnergyCharacteristic object.
+*/
+QLowEnergyCharacteristic::~QLowEnergyCharacteristic()
+{
+ delete data;
+}
+
+/*!
+ Returns the name of the gatt characteristic type.
+*/
+QString QLowEnergyCharacteristic::name() const
+{
+ return QBluetoothUuid::characteristicToString(
+ static_cast<QBluetoothUuid::CharacteristicType>(uuid().toUInt16()));
+}
+
+/*!
+ Returns the UUID of the gatt characteristic.
+*/
+QBluetoothUuid QLowEnergyCharacteristic::uuid() const
+{
+ if (d_ptr.isNull() || !data
+ || !d_ptr->characteristicList.contains(data->handle))
+ return QBluetoothUuid();
+
+ return d_ptr->characteristicList[data->handle].uuid;
+}
+
+/*!
+ Returns the properties of the gatt characteristic.
+*/
+QLowEnergyCharacteristic::PropertyTypes QLowEnergyCharacteristic::properties() const
+{
+ if (d_ptr.isNull() || !data
+ || !d_ptr->characteristicList.contains(data->handle))
+ return QLowEnergyCharacteristic::Unknown;
+
+ return d_ptr->characteristicList[data->handle].properties;
+}
+
+/*!
+ Returns value of the GATT characteristic.
+*/
+QByteArray QLowEnergyCharacteristic::value() const
+{
+ if (d_ptr.isNull() || !data
+ || !d_ptr->characteristicList.contains(data->handle))
+ return QByteArray();
+
+ return d_ptr->characteristicList[data->handle].value;
+}
+
+/*!
+ Returns the handle of the GATT characteristic's value attribute;
+ otherwise returns \c 0.
+*/
+QLowEnergyHandle QLowEnergyCharacteristic::handle() const
+{
+ if (d_ptr.isNull() || !data
+ || !d_ptr->characteristicList.contains(data->handle))
+ return 0;
+
+ return d_ptr->characteristicList[data->handle].valueHandle;
+}
+
+/*!
+ Makes a copy of \a other and assigns it to this QLowEnergyCharacteristic object.
+ The two copies continue to share the same service and registration details.
+*/
+QLowEnergyCharacteristic &QLowEnergyCharacteristic::operator=(const QLowEnergyCharacteristic &other)
+{
+ d_ptr = other.d_ptr;
+
+ if (!other.data) {
+ if (data) {
+ delete data;
+ data = 0;
+ }
+ } else {
+ if (!data)
+ data = new QLowEnergyCharacteristicPrivate();
+
+ data->handle = other.data->handle;
+ }
+ return *this;
+}
+
+/*!
+ Returns \c true if \a other is equal to this QLowEnergyCharacteristic; otherwise \c false.
+
+ Two QLowEnergyCharcteristic instances are considered to be equal if they refer to
+ the same charcteristic on the same remote Bluetooth Low Energy device.
+ */
+bool QLowEnergyCharacteristic::operator==(const QLowEnergyCharacteristic &other) const
+{
+ if (d_ptr != other.d_ptr)
+ return false;
+
+ if ((data && !other.data) || (!data && other.data))
+ return false;
+
+ if (!data)
+ return true;
+
+ if (data->handle != other.data->handle)
+ return false;
+
+ return true;
+}
+
+/*!
+ Returns \c true if \a other is not equal to this QLowEnergyCharacteristic; otherwise \c false.
+
+ Two QLowEnergyCharcteristic instances are considered to be equal if they refer to
+ the same charcteristic on the same remote Bluetooth Low Energy device.
+ */
+
+bool QLowEnergyCharacteristic::operator!=(const QLowEnergyCharacteristic &other) const
+{
+ return !(*this == other);
+}
+
+/*!
+ Returns \c true if the QLowEnergyCharacteristic object is valid, otherwise returns \c false.
+
+ An invalid characteristic object is not associated to any service
+ or the associated service is no longer valid due to for example a disconnect from
+ the underlying Bluetooth Low Energy device. Once the object is invalid
+ it cannot become valid anymore.
+
+ \note If a QLowEnergyCharacteristic instance turns invalid due to a disconnect
+ from the underlying device, the information encapsulated by the current
+ instance remains as it was at the time of the disconnect.
+*/
+bool QLowEnergyCharacteristic::isValid() const
+{
+ if (d_ptr.isNull() || !data)
+ return false;
+
+ if (d_ptr->state == QLowEnergyService::InvalidService)
+ return false;
+
+ return true;
+}
+
+QLowEnergyHandle QLowEnergyCharacteristic::attributeHandle() const
+{
+ if (d_ptr.isNull() || !data)
+ return 0;
+
+ return data->handle;
+}
+
+
+/*!
+ Returns the descriptor with \a uuid; otherwise an invalid \c QLowEnergyDescriptor
+ instance.
+
+ \sa descriptors()
+*/
+QLowEnergyDescriptor QLowEnergyCharacteristic::descriptor(const QBluetoothUuid &uuid) const
+{
+ if (d_ptr.isNull() || !data)
+ return QLowEnergyDescriptor();
+
+ QList<QLowEnergyHandle> descriptorKeys = d_ptr->characteristicList[data->handle].
+ descriptorList.keys();
+ foreach (const QLowEnergyHandle descHandle, descriptorKeys) {
+ if (uuid == d_ptr->characteristicList[data->handle].descriptorList[descHandle].uuid)
+ return QLowEnergyDescriptor(d_ptr, data->handle, descHandle);
+ }
+
+ return QLowEnergyDescriptor();
+}
+
+/*!
+ Returns the list of descriptors belonging to this characteristic; otherwise
+ an empty list.
+
+ \sa descriptor()
+*/
+QList<QLowEnergyDescriptor> QLowEnergyCharacteristic::descriptors() const
+{
+ QList<QLowEnergyDescriptor> result;
+
+ if (d_ptr.isNull() || !data
+ || !d_ptr->characteristicList.contains(data->handle))
+ return result;
+
+ QList<QLowEnergyHandle> descriptorKeys = d_ptr->characteristicList[data->handle].
+ descriptorList.keys();
+
+ std::sort(descriptorKeys.begin(), descriptorKeys.end());
+
+ foreach (const QLowEnergyHandle descHandle, descriptorKeys) {
+ QLowEnergyDescriptor descriptor(d_ptr, data->handle, descHandle);
+ result.append(descriptor);
+ }
+
+ return result;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h
new file mode 100644
index 00000000..1c30a939
--- /dev/null
+++ b/src/bluetooth/qlowenergycharacteristic.h
@@ -0,0 +1,111 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYCHARACTERISTIC_H
+#define QLOWENERGYCHARACTERISTIC_H
+#include <QtCore/QSharedPointer>
+#include <QtCore/QObject>
+#include <QtBluetooth/qbluetooth.h>
+#include <QtBluetooth/QBluetoothUuid>
+#include <QtBluetooth/QLowEnergyDescriptor>
+
+QT_BEGIN_NAMESPACE
+
+class QBluetoothUuid;
+class QLowEnergyServicePrivate;
+struct QLowEnergyCharacteristicPrivate;
+class Q_BLUETOOTH_EXPORT QLowEnergyCharacteristic
+{
+public:
+
+ enum PropertyType {
+ Unknown = 0x00,
+ Broadcasting = 0x01,
+ Read = 0x02,
+ WriteNoResponse = 0x04,
+ Write = 0x08,
+ Notify = 0x10,
+ Indicate = 0x20,
+ WriteSigned = 0x40,
+ ExtendedProperty = 0x80
+ };
+ Q_DECLARE_FLAGS(PropertyTypes, PropertyType)
+
+ QLowEnergyCharacteristic();
+ QLowEnergyCharacteristic(const QLowEnergyCharacteristic &other);
+ ~QLowEnergyCharacteristic();
+
+ QLowEnergyCharacteristic &operator=(const QLowEnergyCharacteristic &other);
+ bool operator==(const QLowEnergyCharacteristic &other) const;
+ bool operator!=(const QLowEnergyCharacteristic &other) const;
+
+ QString name() const;
+
+ QBluetoothUuid uuid() const;
+
+ QByteArray value() const;
+
+ QLowEnergyCharacteristic::PropertyTypes properties() const;
+ QLowEnergyHandle handle() const;
+
+ QLowEnergyDescriptor descriptor(const QBluetoothUuid &uuid) const;
+ QList<QLowEnergyDescriptor> descriptors() const;
+
+ bool isValid() const;
+
+protected:
+ QLowEnergyHandle attributeHandle() const;
+
+ QSharedPointer<QLowEnergyServicePrivate> d_ptr;
+
+ friend class QLowEnergyService;
+ friend class QLowEnergyControllerPrivate;
+ QLowEnergyCharacteristicPrivate *data;
+ QLowEnergyCharacteristic(QSharedPointer<QLowEnergyServicePrivate> p,
+ QLowEnergyHandle handle);
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QLowEnergyCharacteristic::PropertyTypes)
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYCHARACTERISTIC_H
diff --git a/src/bluetooth/qlowenergycontroller.cpp b/src/bluetooth/qlowenergycontroller.cpp
new file mode 100644
index 00000000..d0c70ba3
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller.cpp
@@ -0,0 +1,360 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergycontroller.h"
+#include "qlowenergycontroller_p.h"
+
+#include <QtBluetooth/QBluetoothLocalDevice>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+void QLowEnergyControllerPrivate::setError(
+ QLowEnergyController::Error newError)
+{
+ Q_Q(QLowEnergyController);
+ error = newError;
+
+ switch (newError) {
+ case QLowEnergyController::UnknownRemoteDeviceError:
+ errorString = QLowEnergyController::tr("Remote device cannot be found");
+ break;
+ case QLowEnergyController::InvalidBluetoothAdapterError:
+ errorString = QLowEnergyController::tr("Cannot find local adapter");
+ break;
+ case QLowEnergyController::NetworkError:
+ errorString = QLowEnergyController::tr("Error occurred during connection I/O");
+ break;
+ case QLowEnergyController::UnknownError:
+ default:
+ errorString = QLowEnergyController::tr("Unknown Error");
+ break;
+ }
+
+ emit q->error(newError);
+}
+
+bool QLowEnergyControllerPrivate::isValidLocalAdapter()
+{
+ if (localAdapter.isNull())
+ return false;
+
+ const QList<QBluetoothHostInfo> foundAdapters = QBluetoothLocalDevice::allDevices();
+ bool adapterFound = false;
+
+ foreach (const QBluetoothHostInfo &info, foundAdapters) {
+ if (info.address() == localAdapter) {
+ adapterFound = true;
+ break;
+ }
+ }
+
+ return adapterFound;
+}
+
+void QLowEnergyControllerPrivate::setState(
+ QLowEnergyController::ControllerState newState)
+{
+ Q_Q(QLowEnergyController);
+ if (state == newState)
+ return;
+
+ state = newState;
+ emit q->stateChanged(state);
+}
+
+void QLowEnergyControllerPrivate::invalidateServices()
+{
+ foreach (const QSharedPointer<QLowEnergyServicePrivate> service, serviceList.values()) {
+ service->setController(0);
+ service->setState(QLowEnergyService::InvalidService);
+ }
+
+ serviceList.clear();
+}
+
+QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivate::serviceForHandle(
+ QLowEnergyHandle handle)
+{
+ foreach (QSharedPointer<QLowEnergyServicePrivate> service, serviceList.values())
+ if (service->startHandle <= handle && handle <= service->endHandle)
+ return service;
+
+ return QSharedPointer<QLowEnergyServicePrivate>();
+}
+
+/*!
+ Returns a valid characteristic if the given handle is the
+ handle of the characteristic itself or one of its descriptors
+ */
+QLowEnergyCharacteristic QLowEnergyControllerPrivate::characteristicForHandle(
+ QLowEnergyHandle handle)
+{
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(handle);
+ if (service.isNull())
+ return QLowEnergyCharacteristic();
+
+ if (service->characteristicList.isEmpty())
+ return QLowEnergyCharacteristic();
+
+ // check whether it is the handle of a characteristic header
+ if (service->characteristicList.contains(handle))
+ return QLowEnergyCharacteristic(service, handle);
+
+ // check whether it is the handle of the characteristic value or its descriptors
+ QList<QLowEnergyHandle> charHandles = service->characteristicList.keys();
+ std::sort(charHandles.begin(), charHandles.end());
+ for (int i = charHandles.size() - 1; i >= 0; i--) {
+ if (charHandles.at(i) > handle)
+ continue;
+
+ return QLowEnergyCharacteristic(service, charHandles.at(i));
+ }
+
+ return QLowEnergyCharacteristic();
+}
+
+/*!
+ Returns a valid descriptor if \a handle blongs to a descriptor;
+ otherwise an invalid one.
+ */
+QLowEnergyDescriptor QLowEnergyControllerPrivate::descriptorForHandle(
+ QLowEnergyHandle handle)
+{
+ const QLowEnergyCharacteristic matchingChar = characteristicForHandle(handle);
+ if (!matchingChar.isValid())
+ return QLowEnergyDescriptor();
+
+ const QLowEnergyServicePrivate::CharData charData = matchingChar.
+ d_ptr->characteristicList[matchingChar.attributeHandle()];
+
+ if (charData.descriptorList.contains(handle))
+ return QLowEnergyDescriptor(matchingChar.d_ptr, matchingChar.attributeHandle(),
+ handle);
+
+ return QLowEnergyDescriptor();
+}
+
+/*!
+ Returns the length of the updated characteristic value.
+ */
+quint16 QLowEnergyControllerPrivate::updateValueOfCharacteristic(
+ QLowEnergyHandle charHandle,const QByteArray &value, bool appendValue)
+{
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ if (!service.isNull() && service->characteristicList.contains(charHandle)) {
+ if (appendValue)
+ service->characteristicList[charHandle].value += value;
+ else
+ service->characteristicList[charHandle].value = value;
+
+ return service->characteristicList[charHandle].value.size();
+ }
+
+ return 0;
+}
+
+/*!
+ Returns the length of the updated descriptor value.
+ */
+quint16 QLowEnergyControllerPrivate::updateValueOfDescriptor(
+ QLowEnergyHandle charHandle, QLowEnergyHandle descriptorHandle,
+ const QByteArray &value, bool appendValue)
+{
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ if (service.isNull() || !service->characteristicList.contains(charHandle))
+ return 0;
+
+ if (!service->characteristicList[charHandle].descriptorList.contains(descriptorHandle))
+ return 0;
+
+ if (appendValue)
+ service->characteristicList[charHandle].descriptorList[descriptorHandle].value += value;
+ else
+ service->characteristicList[charHandle].descriptorList[descriptorHandle].value = value;
+
+ return service->characteristicList[charHandle].descriptorList[descriptorHandle].value.size();
+}
+
+QLowEnergyController::QLowEnergyController(
+ const QBluetoothAddress &remoteDevice,
+ QObject *parent)
+ : QObject(parent), d_ptr(new QLowEnergyControllerPrivate())
+{
+ Q_D(QLowEnergyController);
+ d->q_ptr = this;
+ d->remoteDevice = remoteDevice;
+ d->localAdapter = QBluetoothLocalDevice().address();
+}
+
+QLowEnergyController::QLowEnergyController(
+ const QBluetoothAddress &remoteDevice,
+ const QBluetoothAddress &localDevice,
+ QObject *parent)
+ : QObject(parent), d_ptr(new QLowEnergyControllerPrivate())
+{
+ Q_D(QLowEnergyController);
+ d->q_ptr = this;
+ d->remoteDevice = remoteDevice;
+ d->localAdapter = localDevice;
+}
+
+QLowEnergyController::~QLowEnergyController()
+{
+ disconnectFromDevice(); //in case we were connected
+ delete d_ptr;
+}
+
+/*!
+ Returns the address of the local Bluetooth adapter being used for the
+ communication.
+
+ If this class instance was requested to use the default adapter
+ but there was no default adapter when creating this
+ class instance, the returned \l QBluetoothAddress will be null.
+
+ \sa QBluetoothAddress::isNull()
+ */
+QBluetoothAddress QLowEnergyController::localAddress() const
+{
+ return d_ptr->localAdapter;
+}
+
+QBluetoothAddress QLowEnergyController::remoteAddress() const
+{
+ return d_ptr->remoteDevice;
+}
+
+QLowEnergyController::ControllerState QLowEnergyController::state() const
+{
+ return d_ptr->state;
+}
+
+void QLowEnergyController::connectToDevice()
+{
+ Q_D(QLowEnergyController);
+
+ if (!d->isValidLocalAdapter()) {
+ d->setError(QLowEnergyController::InvalidBluetoothAdapterError);
+ return;
+ }
+
+ if (state() != QLowEnergyController::UnconnectedState)
+ return;
+
+ d->connectToDevice();
+}
+
+void QLowEnergyController::disconnectFromDevice()
+{
+ Q_D(QLowEnergyController);
+
+ if (state() == QLowEnergyController::UnconnectedState)
+ return;
+
+ d->invalidateServices();
+ d->disconnectFromDevice();
+}
+
+void QLowEnergyController::discoverServices()
+{
+ Q_D(QLowEnergyController);
+
+ if (d->state != QLowEnergyController::ConnectedState)
+ return;
+
+ d->discoverServices();
+}
+
+/*!
+ Returns the list of services offered by the remote device.
+
+ The list contains all primary and secondary services.
+
+ \sa createServiceObject()
+ */
+QList<QBluetoothUuid> QLowEnergyController::services() const
+{
+ return d_ptr->serviceList.keys();
+}
+
+/*!
+ Creates an instance of the service represented by \a serviceUuid.
+ The \a serviceUuid parameter must have been obtained via
+ \l services().
+
+ The caller takes ownership of the returned pointer and may pass
+ a \a parent parameter as default owner.
+
+ This function returns a null pointer if no service with
+ \a serviceUUid can be found on the remote device.
+
+ This function can return instances for secondary services
+ too. The include relationships between services can be expressed
+ via \l QLowEnergyService::includedServices().
+
+ \sa services()
+ */
+QLowEnergyService *QLowEnergyController::createServiceObject(
+ const QBluetoothUuid &serviceUuid, QObject *parent)
+{
+ Q_D(QLowEnergyController);
+ if (!d->serviceList.contains(serviceUuid))
+ return 0;
+
+ QLowEnergyService *service = new QLowEnergyService(
+ d->serviceList.value(serviceUuid), parent);
+
+ return service;
+}
+
+QLowEnergyController::Error QLowEnergyController::error() const
+{
+ return d_ptr->error;
+}
+
+QString QLowEnergyController::errorString() const
+{
+ return d_ptr->errorString;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller.h b/src/bluetooth/qlowenergycontroller.h
new file mode 100644
index 00000000..27421cf1
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller.h
@@ -0,0 +1,112 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYCONTROLLER_H
+#define QLOWENERGYCONTROLLER_H
+
+#include <QObject>
+#include <QtBluetooth/QBluetoothAddress>
+#include <QtBluetooth/QBluetoothUuid>
+#include <QtBluetooth/QLowEnergyService>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyControllerPrivate;
+class Q_BLUETOOTH_EXPORT QLowEnergyController : public QObject
+{
+ Q_OBJECT
+public:
+ enum Error {
+ NoError,
+ UnknownError,
+ UnknownRemoteDeviceError,
+ NetworkError,
+ InvalidBluetoothAdapterError
+ };
+
+ enum ControllerState {
+ UnconnectedState = 0,
+ ConnectingState,
+ ConnectedState,
+ ClosingState,
+ };
+
+ explicit QLowEnergyController(const QBluetoothAddress &remoteDevice,
+ QObject *parent = 0);
+ explicit QLowEnergyController(const QBluetoothAddress &remoteDevice,
+ const QBluetoothAddress &localDevice,
+ QObject *parent = 0);
+ ~QLowEnergyController();
+
+ QBluetoothAddress localAddress() const;
+ QBluetoothAddress remoteAddress() const;
+
+ ControllerState state() const;
+
+ void connectToDevice();
+ void disconnectFromDevice();
+
+ // TODO add a way of detecting whether discoverDetails() as already called
+ void discoverServices();
+ QList<QBluetoothUuid> services() const;
+ QLowEnergyService *createServiceObject(
+ const QBluetoothUuid &service, QObject *parent = 0);
+
+ Error error() const;
+ QString errorString() const;
+
+Q_SIGNALS:
+ void connected();
+ void disconnected();
+ void stateChanged(QLowEnergyController::ControllerState state);
+ void error(QLowEnergyController::Error newError);
+
+ void serviceDiscovered(const QBluetoothUuid &newService);
+ void discoveryFinished();
+
+private:
+ Q_DECLARE_PRIVATE(QLowEnergyController)
+ QLowEnergyControllerPrivate *d_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYCONTROLLER_H
diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp
new file mode 100644
index 00000000..dcb99736
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_bluez.cpp
@@ -0,0 +1,1192 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Javier S. Pedro <maemo@javispedro.com>
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergycontroller_p.h"
+#include "qbluetoothsocket_p.h"
+#include "bluez/bluez_data_p.h"
+
+#include <QtCore/QLoggingCategory>
+#include <QtBluetooth/QBluetoothSocket>
+#include <QtBluetooth/QLowEnergyService>
+
+#define ATTRIBUTE_CHANNEL_ID 4
+
+#define ATT_DEFAULT_LE_MTU 23
+#define ATT_MAX_LE_MTU 0x200
+
+#define GATT_PRIMARY_SERVICE 0x2800
+#define GATT_SECONDARY_SERVICE 0x2801
+#define GATT_INCLUDED_SERVICE 0x2802
+#define GATT_CHARACTERISTIC 0x2803
+
+// GATT commands
+#define ATT_OP_ERROR_RESPONSE 0x1
+#define ATT_OP_EXCHANGE_MTU_REQUEST 0x2 //send own mtu
+#define ATT_OP_EXCHANGE_MTU_RESPONSE 0x3 //receive server MTU
+#define ATT_OP_FIND_INFORMATION_REQUEST 0x4 //discover individual attribute info
+#define ATT_OP_FIND_INFORMATION_RESPONSE 0x5
+#define ATT_OP_READ_BY_TYPE_REQUEST 0x8 //discover characteristics
+#define ATT_OP_READ_BY_TYPE_RESPONSE 0x9
+#define ATT_OP_READ_REQUEST 0xA //read characteristic & descriptor values
+#define ATT_OP_READ_RESPONSE 0xB
+#define ATT_OP_READ_BLOB_REQUEST 0xC //read values longer than MTU-1
+#define ATT_OP_READ_BLOB_RESPONSE 0xD
+#define ATT_OP_READ_BY_GROUP_REQUEST 0x10 //discover services
+#define ATT_OP_READ_BY_GROUP_RESPONSE 0x11
+#define ATT_OP_WRITE_REQUEST 0x12 //write characteristic
+#define ATT_OP_WRITE_RESPONSE 0x13
+#define ATT_OP_HANDLE_VAL_NOTIFICATION 0x1b //informs about value change
+#define ATT_OP_HANDLE_VAL_INDICATION 0x1d //informs about value change -> requires reply
+#define ATT_OP_HANDLE_VAL_CONFIRMATION 0x1e //answer for ATT_OP_HANDLE_VAL_INDICATION
+
+//GATT command sizes in bytes
+#define FIND_INFO_REQUEST_SIZE 5
+#define GRP_TYPE_REQ_SIZE 7
+#define READ_BY_TYPE_REQ_SIZE 7
+#define READ_REQUEST_SIZE 3
+#define READ_BLOB_REQUEST_SIZE 5
+#define WRITE_REQUEST_SIZE 3
+#define MTU_EXCHANGE_SIZE 3
+
+// GATT error codes
+#define ATT_ERROR_INVALID_HANDLE 0x01
+#define ATT_ERROR_READ_NOT_PERM 0x02
+#define ATT_ERROR_WRITE_NOT_PERM 0x03
+#define ATT_ERROR_INVALID_PDU 0x04
+#define ATT_ERROR_INSUF_AUTHENTICATION 0x05
+#define ATT_ERROR_REQUEST_NOT_SUPPORTED 0x06
+#define ATT_ERROR_INVALID_OFFSET 0x07
+#define ATT_ERROR_INSUF_AUTHORIZATION 0x08
+#define ATT_ERROR_PREPARE_QUEUE_FULL 0x09
+#define ATT_ERROR_ATTRIBUTE_NOT_FOUND 0x0A
+#define ATT_ERROR_ATTRIBUTE_NOT_LONG 0x0B
+#define ATT_ERROR_INSUF_ENCR_KEY_SIZE 0x0C
+#define ATT_ERROR_INVAL_ATTR_VALUE_LEN 0x0D
+#define ATT_ERROR_UNLIKELY 0x0E
+#define ATT_ERROR_INSUF_ENCRYPTION 0x0F
+#define ATT_ERROR_APPLICATION 0x10
+#define ATT_ERROR_INSUF_RESOURCES 0x11
+
+#define APPEND_VALUE true
+#define NEW_VALUE false
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
+
+static inline QBluetoothUuid convert_uuid128(const quint128 *p)
+{
+ quint128 dst_hostOrder, dst_bigEndian;
+
+ // Bluetooth LE data comes as little endian
+ // uuids are constructed using high endian
+ btoh128(p, &dst_hostOrder);
+ hton128(&dst_hostOrder, &dst_bigEndian);
+
+ // convert to Qt's own data type
+ quint128 qtdst;
+ memcpy(&qtdst, &dst_bigEndian, sizeof(quint128));
+
+ return QBluetoothUuid(qtdst);
+}
+
+static void dumpErrorInformation(const QByteArray &response)
+{
+ const char *data = response.constData();
+ if (response.size() != 5 || data[0] != ATT_OP_ERROR_RESPONSE) {
+ qCWarning(QT_BT_BLUEZ) << QLatin1String("Not a valid error response");
+ return;
+ }
+
+ quint8 lastCommand = data[1];
+ quint16 handle = bt_get_le16(&data[2]);
+ quint8 errorCode = data[4];
+
+ QString errorString;
+ switch (errorCode) {
+ case ATT_ERROR_INVALID_HANDLE:
+ errorString = QStringLiteral("invalid handle"); break;
+ case ATT_ERROR_READ_NOT_PERM:
+ errorString = QStringLiteral("not readable attribute - permissions"); break;
+ case ATT_ERROR_WRITE_NOT_PERM:
+ errorString = QStringLiteral("not writable attribute - permissions"); break;
+ case ATT_ERROR_INVALID_PDU:
+ errorString = QStringLiteral("PDU invalid"); break;
+ case ATT_ERROR_INSUF_AUTHENTICATION:
+ errorString = QStringLiteral("needs authentication - permissions"); break;
+ case ATT_ERROR_REQUEST_NOT_SUPPORTED:
+ errorString = QStringLiteral("server does not support request"); break;
+ case ATT_ERROR_INVALID_OFFSET:
+ errorString = QStringLiteral("offset past end of attribute"); break;
+ case ATT_ERROR_INSUF_AUTHORIZATION:
+ errorString = QStringLiteral("need authorization - permissions"); break;
+ case ATT_ERROR_PREPARE_QUEUE_FULL:
+ errorString = QStringLiteral("run out of prepare queue space"); break;
+ case ATT_ERROR_ATTRIBUTE_NOT_FOUND:
+ errorString = QStringLiteral("no attribute in given range found"); break;
+ case ATT_ERROR_ATTRIBUTE_NOT_LONG:
+ errorString = QStringLiteral("attribute not read/written using read blob"); break;
+ case ATT_ERROR_INSUF_ENCR_KEY_SIZE:
+ errorString = QStringLiteral("need encryption key size - permissions"); break;
+ case ATT_ERROR_INVAL_ATTR_VALUE_LEN:
+ errorString = QStringLiteral("written value is invalid size"); break;
+ case ATT_ERROR_UNLIKELY:
+ errorString = QStringLiteral("unlikely error"); break;
+ case ATT_ERROR_INSUF_ENCRYPTION:
+ errorString = QStringLiteral("needs encryption - permissions"); break;
+ case ATT_ERROR_APPLICATION:
+ errorString = QStringLiteral("application error"); break;
+ case ATT_ERROR_INSUF_RESOURCES:
+ errorString = QStringLiteral("insufficient resources to complete request"); break;
+ default:
+ errorString = QStringLiteral("unknown error code"); break;
+ }
+
+ qCDebug(QT_BT_BLUEZ) << "Error1:" << errorString
+ << "last command:" << hex << lastCommand
+ << "handle:" << handle;
+}
+
+QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
+ : QObject(),
+ state(QLowEnergyController::UnconnectedState),
+ error(QLowEnergyController::NoError),
+ l2cpSocket(0), requestPending(false),
+ mtuSize(ATT_DEFAULT_LE_MTU)
+{
+ qRegisterMetaType<QList<QLowEnergyHandle> >();
+}
+
+QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
+{
+}
+
+void QLowEnergyControllerPrivate::connectToDevice()
+{
+ setState(QLowEnergyController::ConnectingState);
+ if (l2cpSocket)
+ delete l2cpSocket;
+
+ l2cpSocket = new QBluetoothSocket(QBluetoothServiceInfo::L2capProtocol, this);
+ connect(l2cpSocket, SIGNAL(connected()), this, SLOT(l2cpConnected()));
+ connect(l2cpSocket, SIGNAL(disconnected()), this, SLOT(l2cpDisconnected()));
+ connect(l2cpSocket, SIGNAL(error(QBluetoothSocket::SocketError)),
+ this, SLOT(l2cpErrorChanged(QBluetoothSocket::SocketError)));
+ connect(l2cpSocket, SIGNAL(readyRead()), this, SLOT(l2cpReadyRead()));
+
+ l2cpSocket->d_ptr->isLowEnergySocket = true;
+
+ // bind the socket to the local device
+ int sockfd = l2cpSocket->socketDescriptor();
+ if (sockfd < 0) {
+ qCWarning(QT_BT_BLUEZ) << "l2cp socket not initialised";
+ setError(QLowEnergyController::UnknownError);
+ return;
+ }
+
+ struct sockaddr_l2 addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ addr.l2_cid = htobs(ATTRIBUTE_CHANNEL_ID);
+ addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
+ convertAddress(localAdapter.toUInt64(), addr.l2_bdaddr.b);
+
+ if (::bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ qCWarning(QT_BT_BLUEZ) << qt_error_string(errno);
+ setError(QLowEnergyController::UnknownError);
+ return;
+ }
+
+ // connect
+ l2cpSocket->connectToService(remoteDevice, ATTRIBUTE_CHANNEL_ID);
+}
+
+void QLowEnergyControllerPrivate::l2cpConnected()
+{
+ Q_Q(QLowEnergyController);
+
+ exchangeMTU();
+
+ setState(QLowEnergyController::ConnectedState);
+ emit q->connected();
+}
+
+void QLowEnergyControllerPrivate::disconnectFromDevice()
+{
+ setState(QLowEnergyController::ClosingState);
+ l2cpSocket->close();
+}
+
+void QLowEnergyControllerPrivate::l2cpDisconnected()
+{
+ Q_Q(QLowEnergyController);
+
+ setState(QLowEnergyController::UnconnectedState);
+ emit q->disconnected();
+}
+
+void QLowEnergyControllerPrivate::l2cpErrorChanged(QBluetoothSocket::SocketError e)
+{
+ switch (e) {
+ case QBluetoothSocket::HostNotFoundError:
+ setError(QLowEnergyController::UnknownRemoteDeviceError);
+ qCDebug(QT_BT_BLUEZ) << "The passed remote device address cannot be found";
+ break;
+ case QBluetoothSocket::NetworkError:
+ setError(QLowEnergyController::NetworkError);
+ qCDebug(QT_BT_BLUEZ) << "Network IO error while talking to LE device";
+ break;
+ case QBluetoothSocket::UnknownSocketError:
+ case QBluetoothSocket::UnsupportedProtocolError:
+ case QBluetoothSocket::OperationError:
+ case QBluetoothSocket::ServiceNotFoundError:
+ default:
+ // these errors shouldn't happen -> as it means
+ // the code in this file has bugs
+ qCDebug(QT_BT_BLUEZ) << "Unknown l2cp socket error: " << e << l2cpSocket->errorString();
+ setError(QLowEnergyController::UnknownError);
+ break;
+ }
+
+ invalidateServices();
+ setState(QLowEnergyController::UnconnectedState);
+}
+
+void QLowEnergyControllerPrivate::l2cpReadyRead()
+{
+ const QByteArray reply = l2cpSocket->readAll();
+ qCDebug(QT_BT_BLUEZ) << "Received size:" << reply.size() << "data:" << reply.toHex();
+ if (reply.isEmpty())
+ return;
+
+ const quint8 command = reply.constData()[0];
+ switch (command) {
+ case ATT_OP_HANDLE_VAL_NOTIFICATION:
+ {
+ processUnsolicitedReply(reply);
+ return;
+ }
+ case ATT_OP_HANDLE_VAL_INDICATION:
+ {
+ //send confirmation
+ QByteArray packet;
+ packet.append(static_cast<char>(ATT_OP_HANDLE_VAL_CONFIRMATION));
+ sendCommand(packet);
+
+ processUnsolicitedReply(reply);
+ return;
+ }
+ case ATT_OP_EXCHANGE_MTU_REQUEST:
+ case ATT_OP_READ_BY_GROUP_REQUEST:
+ case ATT_OP_READ_BY_TYPE_REQUEST:
+ case ATT_OP_READ_REQUEST:
+ case ATT_OP_FIND_INFORMATION_REQUEST:
+ case ATT_OP_WRITE_REQUEST:
+ qCWarning(QT_BT_BLUEZ) << "Unexpected message type" << hex << command
+ << "will be ignored" ;
+ return;
+ default:
+ //only solicited replies finish pending requests
+ requestPending = false;
+ break;
+ }
+
+ Q_ASSERT(!openRequests.isEmpty());
+ const Request request = openRequests.dequeue();
+ processReply(request, reply);
+
+ sendNextPendingRequest();
+}
+
+void QLowEnergyControllerPrivate::sendCommand(const QByteArray &packet)
+{
+ qint64 result = l2cpSocket->write(packet.constData(),
+ packet.size());
+ if (result == -1) {
+ qCDebug(QT_BT_BLUEZ) << "Cannot write L2CP command:" << hex
+ << packet.toHex()
+ << l2cpSocket->errorString();
+ setError(QLowEnergyController::NetworkError);
+ }
+}
+
+void QLowEnergyControllerPrivate::sendNextPendingRequest()
+{
+ if (openRequests.isEmpty() || requestPending)
+ return;
+
+ const Request &request = openRequests.head();
+// qCDebug(QT_BT_BLUEZ) << "Sending request, type:" << hex << request.command
+// << request.payload.toHex();
+
+ requestPending = true;
+ sendCommand(request.payload);
+}
+
+QLowEnergyHandle parseReadByTypeCharDiscovery(
+ QLowEnergyServicePrivate::CharData *charData,
+ const char *data, quint16 elementLength)
+{
+ Q_ASSERT(charData);
+ Q_ASSERT(data);
+
+ QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
+ charData->properties =
+ (QLowEnergyCharacteristic::PropertyTypes)data[2];
+ charData->valueHandle = bt_get_le16(&data[3]);
+
+ if (elementLength == 7) // 16 bit uuid
+ charData->uuid = QBluetoothUuid(bt_get_le16(&data[5]));
+ else
+ charData->uuid = convert_uuid128((quint128 *)&data[5]);
+
+ qCDebug(QT_BT_BLUEZ) << "Found handle:" << hex << attributeHandle
+ << "properties:" << charData->properties
+ << "value handle:" << charData->valueHandle
+ << "uuid:" << charData->uuid.toString();
+
+ return attributeHandle;
+}
+
+QLowEnergyHandle parseReadByTypeIncludeDiscovery(
+ QList<QBluetoothUuid> *foundServices,
+ const char *data, quint16 elementLength)
+{
+ Q_ASSERT(foundServices);
+ Q_ASSERT(data);
+
+ QLowEnergyHandle attributeHandle = bt_get_le16(&data[0]);
+
+ // the next 2 elements are not required as we have discovered
+ // all (primary/secondary) services already. Now we are only
+ // interested in their relationship to each other
+ // data[2] -> included service start handle
+ // data[4] -> included service end handle
+
+ if (elementLength == 8) //16 bit uuid
+ foundServices->append(QBluetoothUuid(bt_get_le16(&data[6])));
+ else
+ foundServices->append(convert_uuid128((quint128 *) &data[6]));
+
+ qCDebug(QT_BT_BLUEZ) << "Found included service: " << hex
+ << attributeHandle << "uuid:" << *foundServices;
+
+ return attributeHandle;
+}
+
+void QLowEnergyControllerPrivate::processReply(
+ const Request &request, const QByteArray &response)
+{
+ Q_Q(QLowEnergyController);
+
+ quint8 command = response.constData()[0];
+
+ bool isErrorResponse = false;
+ // if error occurred 2. byte is previous request type
+ if (command == ATT_OP_ERROR_RESPONSE) {
+ dumpErrorInformation(response);
+ command = response.constData()[1];
+ isErrorResponse = true;
+ }
+
+ switch (command) {
+ case ATT_OP_EXCHANGE_MTU_REQUEST: // in case of error
+ case ATT_OP_EXCHANGE_MTU_RESPONSE:
+ {
+ Q_ASSERT(request.command == ATT_OP_EXCHANGE_MTU_REQUEST);
+ if (isErrorResponse) {
+ mtuSize = ATT_DEFAULT_LE_MTU;
+ break;
+ }
+
+ const char *data = response.constData();
+ quint16 mtu = bt_get_le16(&data[1]);
+ mtuSize = mtu;
+ if (mtuSize < ATT_DEFAULT_LE_MTU)
+ mtuSize = ATT_DEFAULT_LE_MTU;
+
+ qCDebug(QT_BT_BLUEZ) << "Server MTU:" << mtu << "resulting mtu:" << mtuSize;
+ }
+ break;
+ case ATT_OP_READ_BY_GROUP_REQUEST: // in case of error
+ case ATT_OP_READ_BY_GROUP_RESPONSE:
+ {
+ // Discovering services
+ Q_ASSERT(request.command == ATT_OP_READ_BY_GROUP_REQUEST);
+
+ const quint16 type = request.reference.toUInt();
+
+ if (isErrorResponse) {
+ if (type == GATT_SECONDARY_SERVICE)
+ q->discoveryFinished();
+ else // search for secondary services
+ sendReadByGroupRequest(0x0001, 0xFFFF, GATT_SECONDARY_SERVICE);
+ break;
+ }
+
+ QLowEnergyHandle start = 0, end = 0;
+ const quint16 elementLength = response.constData()[1];
+ const quint16 numElements = (response.size() - 2) / elementLength;
+ quint16 offset = 2;
+ const char *data = response.constData();
+ for (int i = 0; i < numElements; i++) {
+ start = bt_get_le16(&data[offset]);
+ end = bt_get_le16(&data[offset+2]);
+
+ QBluetoothUuid uuid;
+ if (elementLength == 6) //16 bit uuid
+ uuid = QBluetoothUuid(bt_get_le16(&data[offset+4]));
+ else if (elementLength == 20) //128 bit uuid
+ uuid = convert_uuid128((quint128 *)&data[offset+4]);
+ //else -> do nothing
+
+ offset += elementLength;
+
+
+ qCDebug(QT_BT_BLUEZ) << "Found uuid:" << uuid << "start handle:" << hex
+ << start << "end handle:" << end;
+
+ QLowEnergyServicePrivate *priv = new QLowEnergyServicePrivate();
+ priv->uuid = uuid;
+ priv->startHandle = start;
+ priv->endHandle = end;
+ if (type != GATT_PRIMARY_SERVICE) //unset PrimaryService bit
+ priv->type &= ~QLowEnergyService::PrimaryService;
+ priv->setController(this);
+
+ QSharedPointer<QLowEnergyServicePrivate> pointer(priv);
+
+ serviceList.insert(uuid, pointer);
+ emit q->serviceDiscovered(uuid);
+ }
+
+ if (end != 0xFFFF) {
+ sendReadByGroupRequest(end+1, 0xFFFF, type);
+ } else {
+ if (type == GATT_SECONDARY_SERVICE)
+ emit q->discoveryFinished();
+ else // search for secondary services
+ sendReadByGroupRequest(0x0001, 0xFFFF, GATT_SECONDARY_SERVICE);
+ }
+ }
+ break;
+ case ATT_OP_READ_BY_TYPE_REQUEST: //in case of error
+ case ATT_OP_READ_BY_TYPE_RESPONSE:
+ {
+ // Discovering characteristics
+ Q_ASSERT(request.command == ATT_OP_READ_BY_TYPE_REQUEST);
+
+ QSharedPointer<QLowEnergyServicePrivate> p =
+ request.reference.value<QSharedPointer<QLowEnergyServicePrivate> >();
+ const quint16 attributeType = request.reference2.toUInt();
+
+ if (isErrorResponse) {
+ if (attributeType == GATT_CHARACTERISTIC) {
+ // we reached end of service handle
+ // just finished up characteristic discovery
+ // continue with values of characteristics
+ if (!p->characteristicList.isEmpty()) {
+ readServiceValues(p->uuid, true);
+ } else {
+ // discovery finished since the service doesn't have any
+ // characteristics
+ p->setState(QLowEnergyService::ServiceDiscovered);
+ }
+ } else if (attributeType == GATT_INCLUDED_SERVICE) {
+ // finished up include discovery
+ // continue with characteristic discovery
+ sendReadByTypeRequest(p, p->startHandle, GATT_CHARACTERISTIC);
+ }
+ break;
+ }
+
+ /* packet format:
+ * if GATT_CHARACTERISTIC discovery
+ * <opcode><elementLength>
+ * [<handle><property><charHandle><uuid>]+
+ *
+ * if GATT_INCLUDE discovery
+ * <opcode><elementLength>
+ * [<handle><startHandle_included><endHandle_included><uuid>]+
+ *
+ * The uuid can be 16 or 128 bit.
+ */
+ QLowEnergyHandle lastHandle;
+ const quint16 elementLength = response.constData()[1];
+ const quint16 numElements = (response.size() - 2) / elementLength;
+ quint16 offset = 2;
+ const char *data = response.constData();
+ for (int i = 0; i < numElements; i++) {
+ if (attributeType == GATT_CHARACTERISTIC) {
+ QLowEnergyServicePrivate::CharData characteristic;
+ lastHandle = parseReadByTypeCharDiscovery(
+ &characteristic, &data[offset], elementLength);
+ p->characteristicList[lastHandle] = characteristic;
+ } else if (attributeType == GATT_INCLUDED_SERVICE) {
+ QList<QBluetoothUuid> includedServices;
+ lastHandle = parseReadByTypeIncludeDiscovery(
+ &includedServices, &data[offset], elementLength);
+ p->includedServices = includedServices;
+ foreach (const QBluetoothUuid &uuid, includedServices) {
+ if (serviceList.contains(uuid))
+ serviceList[uuid]->type |= QLowEnergyService::IncludedService;
+ }
+ }
+ }
+
+ if (lastHandle + 1 < p->endHandle) { // more chars to discover
+ sendReadByTypeRequest(p, lastHandle + 1, attributeType);
+ } else {
+ if (attributeType == GATT_INCLUDED_SERVICE)
+ sendReadByTypeRequest(p, p->startHandle, GATT_CHARACTERISTIC);
+ else
+ readServiceValues(p->uuid, true);
+ }
+ }
+ break;
+ case ATT_OP_READ_REQUEST: //error case
+ case ATT_OP_READ_RESPONSE:
+ {
+ //Reading characteristics and descriptors
+ Q_ASSERT(request.command == ATT_OP_READ_REQUEST);
+
+ uint handleData = request.reference.toUInt();
+ const QLowEnergyHandle charHandle = (handleData & 0xffff);
+ const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
+
+ // we ignore error response
+ if (!isErrorResponse) {
+ if (!descriptorHandle)
+ updateValueOfCharacteristic(charHandle, response.mid(1), NEW_VALUE);
+ else
+ updateValueOfDescriptor(charHandle, descriptorHandle,
+ response.mid(1), NEW_VALUE);
+
+ if (response.size() == mtuSize) {
+ // Potentially more data -> switch to blob reads
+ readServiceValuesByOffset(handleData, mtuSize-1,
+ request.reference2.toBool());
+ break;
+ }
+ }
+
+ if (request.reference2.toBool()) {
+ //last characteristic -> progress to descriptor discovery
+ //last descriptor -> service discovery is done
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ Q_ASSERT(!service.isNull());
+ if (!descriptorHandle)
+ discoverServiceDescriptors(service->uuid);
+ else
+ service->setState(QLowEnergyService::ServiceDiscovered);
+ }
+ }
+ break;
+ case ATT_OP_READ_BLOB_REQUEST: //error case
+ case ATT_OP_READ_BLOB_RESPONSE:
+ {
+ //Reading characteristic or descriptor with value longer than MTU
+ Q_ASSERT(request.command == ATT_OP_READ_BLOB_REQUEST);
+
+ uint handleData = request.reference.toUInt();
+ const QLowEnergyHandle charHandle = (handleData & 0xffff);
+ const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
+
+ //ignore errors
+ if (!isErrorResponse) {
+ quint16 length = 0;
+ if (!descriptorHandle)
+ length = updateValueOfCharacteristic(charHandle, response.mid(1), APPEND_VALUE);
+ else
+ length = updateValueOfDescriptor(charHandle, descriptorHandle,
+ response.mid(1), APPEND_VALUE);
+ if (response.size() == mtuSize) {
+ readServiceValuesByOffset(handleData, length,
+ request.reference2.toBool());
+ break;
+ }
+ }
+
+ if (request.reference2.toBool()) {
+ //last overlong characteristic -> progress to descriptor discovery
+ //last overlong descriptor -> service discovery is done
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ Q_ASSERT(!service.isNull());
+ if (!descriptorHandle)
+ discoverServiceDescriptors(service->uuid);
+ else
+ service->setState(QLowEnergyService::ServiceDiscovered);
+ }
+
+ }
+ break;
+ case ATT_OP_FIND_INFORMATION_REQUEST: //error case
+ case ATT_OP_FIND_INFORMATION_RESPONSE:
+ {
+ //Discovering descriptors
+ Q_ASSERT(request.command == ATT_OP_FIND_INFORMATION_REQUEST);
+
+ /* packet format:
+ * <opcode><format>[<handle><descriptor_uuid>]+
+ *
+ * The uuid can be 16 or 128 bit which is indicated by format.
+ */
+
+ QList<QLowEnergyHandle> keys = request.reference.value<QList<QLowEnergyHandle> >();
+ if (keys.isEmpty()) {
+ qCWarning(QT_BT_BLUEZ) << "Descriptor discovery for unknown characteristic received";
+ break;
+ }
+ QLowEnergyHandle charHandle = keys.first();
+
+ QSharedPointer<QLowEnergyServicePrivate> p =
+ serviceForHandle(charHandle);
+ Q_ASSERT(!p.isNull());
+
+ if (isErrorResponse) {
+ readServiceValues(p->uuid, false); //read descriptor values
+ break;
+ }
+
+ const quint8 format = response[1];
+ quint16 elementLength;
+ switch (format) {
+ case 0x01:
+ elementLength = 2 + 2; //sizeof(QLowEnergyHandle) + 16bit uuid
+ break;
+ case 0x02:
+ elementLength = 2 + 16; //sizeof(QLowEnergyHandle) + 128bit uuid
+ break;
+ default:
+ qCWarning(QT_BT_BLUEZ) << "Unknown format in FIND_INFORMATION_RESPONSE";
+ return;
+ }
+
+ const quint16 numElements = (response.size() - 2) / elementLength;
+
+ quint16 offset = 2;
+ QLowEnergyHandle descriptorHandle;
+ QBluetoothUuid uuid;
+ const char *data = response.constData();
+ for (int i = 0; i < numElements; i++) {
+ descriptorHandle = bt_get_le16(&data[offset]);
+
+ if (format == 0x01)
+ uuid = QBluetoothUuid(bt_get_le16(&data[offset+2]));
+ else if (format == 0x02)
+ uuid = convert_uuid128((quint128 *)&data[offset+2]);
+
+ offset += elementLength;
+
+ // ignore all attributes which are not of type descriptor
+ // examples are the characteristics value or
+ bool ok = false;
+ quint16 shortUuid = uuid.toUInt16(&ok);
+ if (ok && shortUuid >= QLowEnergyServicePrivate::PrimaryService
+ && shortUuid <= QLowEnergyServicePrivate::Characteristic){
+ qCDebug(QT_BT_BLUEZ) << "Suppressing primary/characteristic" << hex << shortUuid;
+ continue;
+ }
+
+ // ignore value handle
+ if (descriptorHandle == p->characteristicList[charHandle].valueHandle) {
+ qCDebug(QT_BT_BLUEZ) << "Suppressing char handle" << hex << descriptorHandle;
+ continue;
+ }
+
+ QLowEnergyServicePrivate::DescData data;
+ data.uuid = uuid;
+ p->characteristicList[charHandle].descriptorList.insert(
+ descriptorHandle, data);
+
+ qCDebug(QT_BT_BLUEZ) << "Descriptor found, uuid:"
+ << uuid.toString()
+ << "descriptor handle:" << hex << descriptorHandle;
+ }
+
+ const QLowEnergyHandle nextPotentialHandle = descriptorHandle + 1;
+ if (keys.count() == 1) {
+ // Reached last characteristic of service
+
+ // The endhandle of a service is always the last handle of
+ // the current service. We must either continue until we have reached
+ // the starting handle of the next service (endHandle+1) or
+ // the last physical handle address (0xffff). Note that
+ // the endHandle of the last service on the device is 0xffff.
+
+ if ((p->endHandle != 0xffff && nextPotentialHandle >= p->endHandle + 1)
+ || (descriptorHandle == 0xffff)) {
+ keys.removeFirst();
+ // last descriptor of last characteristic found
+ // continue with reading descriptor values
+ readServiceValues(p->uuid, false);
+ } else {
+ discoverNextDescriptor(p, keys, nextPotentialHandle);
+ }
+ } else {
+ if (nextPotentialHandle >= keys[1]) //reached next char
+ keys.removeFirst();
+ discoverNextDescriptor(p, keys, nextPotentialHandle);
+ }
+ }
+ break;
+ case ATT_OP_WRITE_REQUEST: //error case
+ case ATT_OP_WRITE_RESPONSE:
+ {
+ //Write command response
+ Q_ASSERT(request.command == ATT_OP_WRITE_REQUEST);
+
+ uint ref = request.reference.toUInt();
+ const QLowEnergyHandle charHandle = (ref & 0xffff);
+ const QLowEnergyHandle descriptorHandle = ((ref >> 16) & 0xffff);
+
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ if (service.isNull() || !service->characteristicList.contains(charHandle))
+ break;
+
+ if (isErrorResponse) {
+ if (!descriptorHandle)
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+ else
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ break;
+ }
+
+ const QByteArray newValue = request.reference2.toByteArray();
+ if (!descriptorHandle) {
+ service->characteristicList[charHandle].value = newValue;
+ QLowEnergyCharacteristic ch(service, charHandle);
+ emit service->characteristicChanged(ch, newValue);
+ } else {
+ service->characteristicList[charHandle].descriptorList[descriptorHandle].value = newValue;
+ QLowEnergyDescriptor descriptor(service, charHandle, descriptorHandle);
+ emit service->descriptorChanged(descriptor, newValue);
+ }
+ }
+ break;
+ default:
+ qCDebug(QT_BT_BLUEZ) << "Unknown packet: " << response.toHex();
+ break;
+ }
+}
+
+void QLowEnergyControllerPrivate::discoverServices()
+{
+ sendReadByGroupRequest(0x0001, 0xFFFF, GATT_PRIMARY_SERVICE);
+}
+
+void QLowEnergyControllerPrivate::sendReadByGroupRequest(
+ QLowEnergyHandle start, QLowEnergyHandle end, quint16 type)
+{
+ //call for primary and secondary services
+ quint8 packet[GRP_TYPE_REQ_SIZE];
+
+ packet[0] = ATT_OP_READ_BY_GROUP_REQUEST;
+ bt_put_unaligned(htobs(start), (quint16 *) &packet[1]);
+ bt_put_unaligned(htobs(end), (quint16 *) &packet[3]);
+ bt_put_unaligned(htobs(type), (quint16 *) &packet[5]);
+
+ QByteArray data(GRP_TYPE_REQ_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, GRP_TYPE_REQ_SIZE);
+ qCDebug(QT_BT_BLUEZ) << "Sending read_by_group_type request, startHandle:" << hex
+ << start << "endHandle:" << end << type;
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_READ_BY_GROUP_REQUEST;
+ request.reference = type;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+void QLowEnergyControllerPrivate::discoverServiceDetails(const QBluetoothUuid &service)
+{
+ if (!serviceList.contains(service)) {
+ qCWarning(QT_BT_BLUEZ) << "Discovery of unknown service" << service.toString()
+ << "not possible";
+ return;
+ }
+
+ QSharedPointer<QLowEnergyServicePrivate> serviceData = serviceList.value(service);
+ serviceData->characteristicList.clear();
+ sendReadByTypeRequest(serviceData, serviceData->startHandle, GATT_INCLUDED_SERVICE);
+}
+
+void QLowEnergyControllerPrivate::sendReadByTypeRequest(
+ QSharedPointer<QLowEnergyServicePrivate> serviceData,
+ QLowEnergyHandle nextHandle, quint16 attributeType)
+{
+ quint8 packet[READ_BY_TYPE_REQ_SIZE];
+
+ packet[0] = ATT_OP_READ_BY_TYPE_REQUEST;
+ bt_put_unaligned(htobs(nextHandle), (quint16 *) &packet[1]);
+ bt_put_unaligned(htobs(serviceData->endHandle), (quint16 *) &packet[3]);
+ bt_put_unaligned(htobs(attributeType), (quint16 *) &packet[5]);
+
+ QByteArray data(READ_BY_TYPE_REQ_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, READ_BY_TYPE_REQ_SIZE);
+ qCDebug(QT_BT_BLUEZ) << "Sending read_by_type request, startHandle:" << hex
+ << nextHandle << "endHandle:" << serviceData->endHandle
+ << "type:" << attributeType << "packet:" << data.toHex();
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_READ_BY_TYPE_REQUEST;
+ request.reference = QVariant::fromValue(serviceData);
+ request.reference2 = attributeType;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+/*!
+ \internal
+
+ Reads the value of characteristics and descriptors.
+
+ \a readCharacteristics determines whether we intend to read a characteristic;
+ otherwise we read a descriptor.
+ */
+void QLowEnergyControllerPrivate::readServiceValues(
+ const QBluetoothUuid &serviceUuid, bool readCharacteristics)
+{
+ // TODO Long charactertistic value reads not yet supported (larger than MTU)
+ quint8 packet[READ_REQUEST_SIZE];
+ if (QT_BT_BLUEZ().isDebugEnabled()) {
+ if (readCharacteristics)
+ qCDebug(QT_BT_BLUEZ) << "Reading characteristic values for"
+ << serviceUuid.toString();
+ else
+ qCDebug(QT_BT_BLUEZ) << "Reading descriptor values for"
+ << serviceUuid.toString();
+ }
+
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
+
+ // pair.first -> target attribute
+ // pair.second -> context information for read request
+ QPair<QLowEnergyHandle, quint32> pair;
+
+ // Create list of attribute handles which need to be read
+ QList<QPair<QLowEnergyHandle, quint32> > targetHandles;
+ const QList<QLowEnergyHandle> keys = service->characteristicList.keys();
+ for (int i = 0; i < keys.count(); i++) {
+ const QLowEnergyHandle charHandle = keys[i];
+ const QLowEnergyServicePrivate::CharData &charDetails =
+ service->characteristicList[charHandle];
+
+
+ if (readCharacteristics) {
+ // Collect handles of all characteristic value attributes
+
+ // Don't try to read writeOnly characteristic
+ if (!(charDetails.properties & QLowEnergyCharacteristic::Read))
+ continue;
+
+ pair.first = charDetails.valueHandle;
+ pair.second = charHandle;
+ targetHandles.append(pair);
+
+ } else {
+ // Collect handles of all descriptor attributes
+ foreach (QLowEnergyHandle descriptorHandle, charDetails.descriptorList.keys()) {
+ pair.first = descriptorHandle;
+ pair.second = (charHandle | (descriptorHandle << 16));
+ targetHandles.append(pair);
+ }
+ }
+ }
+
+
+ if (targetHandles.isEmpty()) {
+ if (readCharacteristics) {
+ // none of the characteristics is readable
+ // -> continue with descriptor discovery
+ discoverServiceDescriptors(service->uuid);
+ } else {
+ // characteristic w/o descriptors
+ service->setState(QLowEnergyService::ServiceDiscovered);
+ }
+ return;
+ }
+
+ for (int i = 0; i < targetHandles.count(); i++) {
+ pair = targetHandles.at(i);
+ packet[0] = ATT_OP_READ_REQUEST;
+ bt_put_unaligned(htobs(pair.first), (quint16 *) &packet[1]);
+
+ QByteArray data(READ_REQUEST_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, READ_REQUEST_SIZE);
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_READ_REQUEST;
+ request.reference = pair.second;
+ // last entry?
+ request.reference2 = QVariant((bool)(i + 1 == targetHandles.count()));
+ openRequests.enqueue(request);
+ }
+
+ sendNextPendingRequest();
+}
+
+/*!
+ \internal
+
+ This function is used when reading a handle value that is
+ longer than the mtuSize.
+
+ The BLOB read request is prepended to the list of
+ open requests to finish the current value read up before
+ starting the next read request.
+ */
+void QLowEnergyControllerPrivate::readServiceValuesByOffset(
+ quint16 handleData, quint16 offset, bool isLastValue)
+{
+ const QLowEnergyHandle charHandle = (handleData & 0xffff);
+ const QLowEnergyHandle descriptorHandle = ((handleData >> 16) & 0xffff);
+ quint8 packet[READ_REQUEST_SIZE];
+
+ packet[0] = ATT_OP_READ_BLOB_REQUEST;
+
+ QLowEnergyHandle handleToRead = charHandle;
+ if (descriptorHandle) {
+ handleToRead = descriptorHandle;
+ qCDebug(QT_BT_BLUEZ) << "Reading descriptor via blob request"
+ << hex << descriptorHandle;
+ } else {
+ //charHandle is not the char's value handle
+ QSharedPointer<QLowEnergyServicePrivate> service =
+ serviceForHandle(charHandle);
+ if (!service.isNull()
+ && service->characteristicList.contains(charHandle)) {
+ handleToRead = service->characteristicList[charHandle].valueHandle;
+ qCDebug(QT_BT_BLUEZ) << "Reading characteristic via blob request"
+ << hex << handleToRead;
+ } else {
+ Q_ASSERT(false);
+ }
+ }
+
+ bt_put_unaligned(htobs(handleToRead), (quint16 *) &packet[1]);
+ bt_put_unaligned(htobs(offset), (quint16 *) &packet[3]);
+
+ QByteArray data(READ_BLOB_REQUEST_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, READ_BLOB_REQUEST_SIZE);
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_READ_BLOB_REQUEST;
+ request.reference = handleData;
+ request.reference2 = isLastValue;
+ openRequests.prepend(request);
+}
+
+void QLowEnergyControllerPrivate::discoverServiceDescriptors(
+ const QBluetoothUuid &serviceUuid)
+{
+ qCDebug(QT_BT_BLUEZ) << "Discovering descriptor values for"
+ << serviceUuid.toString();
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceList.value(serviceUuid);
+ // start handle of all known characteristics
+ QList<QLowEnergyHandle> keys = service->characteristicList.keys();
+
+ if (keys.isEmpty()) { // service has no characteristics
+ // implies that characteristic & descriptor discovery can be skipped
+ service->setState(QLowEnergyService::ServiceDiscovered);
+ return;
+ }
+
+ std::sort(keys.begin(), keys.end());
+
+ discoverNextDescriptor(service, keys, keys[0]);
+}
+
+void QLowEnergyControllerPrivate::processUnsolicitedReply(const QByteArray &payload)
+{
+ const char *data = payload.constData();
+ bool isNotification = (data[0] == ATT_OP_HANDLE_VAL_NOTIFICATION);
+ const QLowEnergyHandle changedHandle = bt_get_le16(&data[1]);
+
+ if (QT_BT_BLUEZ().isDebugEnabled()) {
+ if (isNotification)
+ qCDebug(QT_BT_BLUEZ) << "Change notification for handle" << hex << changedHandle;
+ else
+ qCDebug(QT_BT_BLUEZ) << "Change indication for handle" << hex << changedHandle;
+ }
+
+ const QLowEnergyCharacteristic ch = characteristicForHandle(changedHandle);
+ if (ch.isValid() && ch.handle() == changedHandle) {
+ updateValueOfCharacteristic(ch.attributeHandle(), payload.mid(3), NEW_VALUE);
+ emit ch.d_ptr->characteristicChanged(ch, payload.mid(3));
+ } else {
+ qCWarning(QT_BT_BLUEZ) << "Cannot find matching characteristic for "
+ "notification/indication";
+ }
+}
+
+void QLowEnergyControllerPrivate::exchangeMTU()
+{
+ qCDebug(QT_BT_BLUEZ) << "Exchanging MTU";
+
+ quint8 packet[MTU_EXCHANGE_SIZE];
+ packet[0] = ATT_OP_EXCHANGE_MTU_REQUEST;
+ bt_put_unaligned(htobs(ATT_MAX_LE_MTU), (quint16 *) &packet[1]);
+
+ QByteArray data(MTU_EXCHANGE_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, MTU_EXCHANGE_SIZE);
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_EXCHANGE_MTU_REQUEST;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+void QLowEnergyControllerPrivate::discoverNextDescriptor(
+ QSharedPointer<QLowEnergyServicePrivate> serviceData,
+ const QList<QLowEnergyHandle> pendingCharHandles,
+ const QLowEnergyHandle startingHandle)
+{
+ Q_ASSERT(!pendingCharHandles.isEmpty());
+ Q_ASSERT(!serviceData.isNull());
+
+ qCDebug(QT_BT_BLUEZ) << "Sending find_info request" << hex
+ << pendingCharHandles << startingHandle;
+
+ quint8 packet[FIND_INFO_REQUEST_SIZE];
+ packet[0] = ATT_OP_FIND_INFORMATION_REQUEST;
+
+ const QLowEnergyHandle charStartHandle = startingHandle;
+ QLowEnergyHandle charEndHandle = 0;
+ if (pendingCharHandles.count() == 1) //single characteristic
+ charEndHandle = serviceData->endHandle;
+ else
+ charEndHandle = pendingCharHandles[1] - 1;
+
+ bt_put_unaligned(htobs(charStartHandle), (quint16 *) &packet[1]);
+ bt_put_unaligned(htobs(charEndHandle), (quint16 *) &packet[3]);
+
+ QByteArray data(FIND_INFO_REQUEST_SIZE, Qt::Uninitialized);
+ memcpy(data.data(), packet, FIND_INFO_REQUEST_SIZE);
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_FIND_INFORMATION_REQUEST;
+ request.reference = QVariant::fromValue<QList<QLowEnergyHandle> >(pendingCharHandles);
+ request.reference2 = startingHandle;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+void QLowEnergyControllerPrivate::writeCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue)
+{
+ Q_ASSERT(!service.isNull());
+
+ if (!service->characteristicList.contains(charHandle))
+ return;
+
+ const QLowEnergyHandle valueHandle = service->characteristicList[charHandle].valueHandle;
+ // sizeof(command) + sizeof(handle) + sizeof(newValue)
+ const int size = 1 + 2 + newValue.size();
+
+ quint8 packet[WRITE_REQUEST_SIZE];
+ packet[0] = ATT_OP_WRITE_REQUEST;
+ bt_put_unaligned(htobs(valueHandle), (quint16 *) &packet[1]);
+
+
+ QByteArray data(size, Qt::Uninitialized);
+ memcpy(data.data(), packet, WRITE_REQUEST_SIZE);
+ memcpy(&(data.data()[WRITE_REQUEST_SIZE]), newValue.constData(), newValue.size());
+
+ qCDebug(QT_BT_BLUEZ) << "Writing characteristic" << hex << charHandle
+ << "(size:" << size << ")";
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_WRITE_REQUEST;
+ request.reference = charHandle;
+ request.reference2 = newValue;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+void QLowEnergyControllerPrivate::writeDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue)
+{
+ Q_ASSERT(!service.isNull());
+
+ // sizeof(command) + sizeof(handle) + sizeof(newValue)
+ const int size = 1 + 2 + newValue.size();
+
+ quint8 packet[WRITE_REQUEST_SIZE];
+ packet[0] = ATT_OP_WRITE_REQUEST;
+ bt_put_unaligned(htobs(descriptorHandle), (quint16 *) &packet[1]);
+
+ QByteArray data(size, Qt::Uninitialized);
+ memcpy(data.data(), packet, WRITE_REQUEST_SIZE);
+ memcpy(&(data.data()[WRITE_REQUEST_SIZE]), newValue.constData(), newValue.size());
+
+ qCDebug(QT_BT_BLUEZ) << "Writing descriptor" << hex << descriptorHandle
+ << "(size:" << size << ")";
+
+ Request request;
+ request.payload = data;
+ request.command = ATT_OP_WRITE_REQUEST;
+ request.reference = (charHandle | (descriptorHandle << 16));
+ request.reference2 = newValue;
+ openRequests.enqueue(request);
+
+ sendNextPendingRequest();
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_p.cpp b/src/bluetooth/qlowenergycontroller_p.cpp
new file mode 100644
index 00000000..a903494d
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_p.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergycontroller_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
+ : QObject(),
+ state(QLowEnergyController::UnconnectedState),
+ error(QLowEnergyController::NoError)
+{
+}
+
+QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
+{
+}
+
+void QLowEnergyControllerPrivate::connectToDevice()
+{
+ setError(QLowEnergyController::UnknownError);
+}
+
+void QLowEnergyControllerPrivate::disconnectFromDevice()
+{
+
+}
+
+void QLowEnergyControllerPrivate::discoverServices()
+{
+
+}
+
+void QLowEnergyControllerPrivate::discoverServiceDetails(const QBluetoothUuid &/*service*/)
+{
+
+}
+
+void QLowEnergyControllerPrivate::writeCharacteristic(
+ const QSharedPointer<QLowEnergyServicePrivate> /*service*/,
+ const QLowEnergyHandle /*charHandle*/,
+ const QByteArray &/*newValue*/)
+{
+
+}
+
+void QLowEnergyControllerPrivate::writeDescriptor(
+ const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue)
+{
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergycontroller_p.h b/src/bluetooth/qlowenergycontroller_p.h
new file mode 100644
index 00000000..ef21fbff
--- /dev/null
+++ b/src/bluetooth/qlowenergycontroller_p.h
@@ -0,0 +1,166 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYCONTROLLERPRIVATE_P_H
+#define QLOWENERGYCONTROLLERPRIVATE_P_H
+
+#include <qglobal.h>
+#include <QtCore/QQueue>
+#include <QtBluetooth/qbluetooth.h>
+#include "qlowenergycontroller.h"
+#include "qlowenergyserviceprivate_p.h"
+
+#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+#include <QtBluetooth/QBluetoothSocket>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+typedef QMap<QBluetoothUuid, QSharedPointer<QLowEnergyServicePrivate> > ServiceDataMap;
+
+class QLowEnergyControllerPrivate : public QObject
+{
+ Q_OBJECT
+ Q_DECLARE_PUBLIC(QLowEnergyController)
+public:
+ QLowEnergyControllerPrivate();
+ ~QLowEnergyControllerPrivate();
+
+ void setError(QLowEnergyController::Error newError);
+ bool isValidLocalAdapter();
+
+ void setState(QLowEnergyController::ControllerState newState);
+
+ void connectToDevice();
+ void disconnectFromDevice();
+
+ void discoverServices();
+ void invalidateServices();
+
+ void discoverServiceDetails(const QBluetoothUuid &service);
+
+ // misc helpers
+ QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(
+ QLowEnergyHandle handle);
+ QLowEnergyCharacteristic characteristicForHandle(
+ QLowEnergyHandle handle);
+ QLowEnergyDescriptor descriptorForHandle(
+ QLowEnergyHandle handle);
+
+ quint16 updateValueOfCharacteristic(QLowEnergyHandle charHandle,
+ const QByteArray &value,
+ bool appendValue);
+ quint16 updateValueOfDescriptor(QLowEnergyHandle charHandle,
+ QLowEnergyHandle descriptorHandle,
+ const QByteArray &value,
+ bool appendValue);
+
+
+ // write data
+ void writeCharacteristic(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QByteArray &newValue);
+ void writeDescriptor(const QSharedPointer<QLowEnergyServicePrivate> service,
+ const QLowEnergyHandle charHandle,
+ const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue);
+
+
+ QBluetoothAddress remoteDevice;
+ QBluetoothAddress localAdapter;
+
+ QLowEnergyController::ControllerState state;
+ QLowEnergyController::Error error;
+ QString errorString;
+
+ // list of all found service uuids
+ ServiceDataMap serviceList;
+
+private:
+#if defined(QT_BLUEZ_BLUETOOTH) && !defined(QT_BLUEZ_NO_BTLE)
+ QBluetoothSocket *l2cpSocket;
+ struct Request {
+ quint8 command;
+ QByteArray payload;
+ // TODO reference below is ugly but until we know all commands and their
+ // requirements this is WIP
+ QVariant reference;
+ QVariant reference2;
+ };
+ QQueue<Request> openRequests;
+ bool requestPending;
+ quint16 mtuSize;
+
+ void sendCommand(const QByteArray &packet);
+ void sendNextPendingRequest();
+ void processReply(const Request &request, const QByteArray &reply);
+
+ void sendReadByGroupRequest(QLowEnergyHandle start, QLowEnergyHandle end,
+ quint16 type);
+ void sendReadByTypeRequest(QSharedPointer<QLowEnergyServicePrivate> serviceData,
+ QLowEnergyHandle nextHandle, quint16 attributeType);
+ void sendReadValueRequest(QLowEnergyHandle attributeHandle, bool isDescriptor);
+ void readServiceValues(const QBluetoothUuid &service,
+ bool readCharacteristics);
+ void readServiceValuesByOffset(quint16 handleData, quint16 offset,
+ bool isLastValue);
+
+ void discoverServiceDescriptors(const QBluetoothUuid &serviceUuid);
+ void discoverNextDescriptor(QSharedPointer<QLowEnergyServicePrivate> serviceData,
+ const QList<QLowEnergyHandle> pendingCharHandles,
+ QLowEnergyHandle startingHandle);
+ void processUnsolicitedReply(const QByteArray &msg);
+ void exchangeMTU();
+
+
+private slots:
+ void l2cpConnected();
+ void l2cpDisconnected();
+ void l2cpErrorChanged(QBluetoothSocket::SocketError);
+ void l2cpReadyRead();
+#endif
+private:
+ QLowEnergyController *q_ptr;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYCONTROLLERPRIVATE_P_H
diff --git a/src/bluetooth/qlowenergydescriptor.cpp b/src/bluetooth/qlowenergydescriptor.cpp
new file mode 100644
index 00000000..6817d20e
--- /dev/null
+++ b/src/bluetooth/qlowenergydescriptor.cpp
@@ -0,0 +1,293 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtBluetooth/QLowEnergyService>
+#include "qlowenergyserviceprivate_p.h"
+#include "qlowenergydescriptor.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QLowEnergyDescriptor
+ \inmodule QtBluetooth
+ \brief The QLowEnergyDescriptor class stores information about the Bluetooth
+ Low Energy descriptor.
+ \since 5.4
+
+ QLowEnergyDescriptor provides information about a Bluetooth Low Energy
+ descriptor's name, UUID, value and handle. Descriptors are contained in the
+ Bluetooth Low Energy characteristic and they provide additional information
+ about the characteristic (data format, notification activation, etc).
+*/
+
+struct QLowEnergyDescriptorPrivate
+{
+ QLowEnergyHandle charHandle;
+ QLowEnergyHandle descHandle;
+};
+
+/*!
+ Construct a new QLowEnergyDescriptor.
+*/
+QLowEnergyDescriptor::QLowEnergyDescriptor():
+ d_ptr(0), data(0)
+{
+}
+
+/*!
+ Construct a new QLowEnergyDesxcriptor that is a copy of \a other.
+
+ The two copies continue to share the same underlying data which does not detach
+ upon write.
+*/
+QLowEnergyDescriptor::QLowEnergyDescriptor(const QLowEnergyDescriptor &other):
+ d_ptr(other.d_ptr), data(0)
+{
+ if (other.data) {
+ data = new QLowEnergyDescriptorPrivate();
+ data->charHandle = other.data->charHandle;
+ data->descHandle = other.data->descHandle;
+ }
+}
+
+/*!
+ \internal
+
+*/
+QLowEnergyDescriptor::QLowEnergyDescriptor(QSharedPointer<QLowEnergyServicePrivate> p,
+ QLowEnergyHandle charHandle,
+ QLowEnergyHandle descHandle):
+ d_ptr(p)
+{
+ data = new QLowEnergyDescriptorPrivate();
+ data->charHandle = charHandle;
+ data->descHandle = descHandle;
+
+}
+
+/*!
+ Destroys the QLowEnergyDescriptor object.
+*/
+QLowEnergyDescriptor::~QLowEnergyDescriptor()
+{
+ delete data;
+}
+
+/*!
+ Makes a copy of \a other and assigns it to this QLowEnergyDescriptor object.
+ The two copies continue to share the same service and registration details.
+*/
+QLowEnergyDescriptor &QLowEnergyDescriptor::operator=(const QLowEnergyDescriptor &other)
+{
+ d_ptr = other.d_ptr;
+
+ if (!other.data) {
+ if (data) {
+ delete data;
+ data = 0;
+ }
+ } else {
+ if (!data)
+ data = new QLowEnergyDescriptorPrivate();
+
+ data->charHandle = other.data->charHandle;
+ data->descHandle = other.data->descHandle;
+ }
+
+ return *this;
+}
+
+/*!
+ Returns \c true if \a other is equal to this QLowEnergyCharacteristic; otherwise \c false.
+
+ Two QLowEnergyDescriptor instances are considered to be equal if they refer to
+ the same descriptor on the same remote Bluetooth Low Energy device.
+ */
+bool QLowEnergyDescriptor::operator==(const QLowEnergyDescriptor &other) const
+{
+ if (d_ptr != other.d_ptr)
+ return false;
+
+ if ((data && !other.data) || (!data && other.data))
+ return false;
+
+ if (!data)
+ return true;
+
+ if (data->charHandle != other.data->charHandle
+ || data->descHandle != other.data->descHandle) {
+ return false;
+ }
+
+ return true;
+}
+
+/*!
+ Returns \c true if \a other is not equal to this QLowEnergyCharacteristic; otherwise \c false.
+
+ Two QLowEnergyDescriptor instances are considered to be equal if they refer to
+ the same descriptor on the same remote Bluetooth Low Energy device.
+ */
+bool QLowEnergyDescriptor::operator!=(const QLowEnergyDescriptor &other) const
+{
+ return !(*this == other);
+}
+
+/*!
+ Returns \c true if the QLowEnergyDescriptor object is valid, otherwise returns \c false.
+
+ An invalid descriptor instance is not associated to any service
+ or the associated service is no longer valid due to for example a disconnect from
+ the underlying Bluetooth Low Energy device. Once the object is invalid
+ it cannot become valid anymore.
+
+ \note If a QLowEnergyDescriptor instance turns invalid due to a disconnect
+ from the underlying device, the information encapsulated by the current
+ instance remains as it was at the time of the disconnect.
+*/
+bool QLowEnergyDescriptor::isValid() const
+{
+ if (d_ptr.isNull() || !data)
+ return false;
+
+ if (d_ptr->state == QLowEnergyService::InvalidService)
+ return false;
+
+ return true;
+}
+
+/*!
+ Returns the UUID of this descriptor.
+*/
+QBluetoothUuid QLowEnergyDescriptor::uuid() const
+{
+ if (d_ptr.isNull() || !data
+ || !d_ptr->characteristicList.contains(data->charHandle)
+ || !d_ptr->characteristicList[data->charHandle].
+ descriptorList.contains(data->descHandle)) {
+ return QBluetoothUuid();
+ }
+
+ return d_ptr->characteristicList[data->charHandle].descriptorList[data->descHandle].uuid;
+}
+
+/*!
+ Returns the handle of the descriptor.
+*/
+QLowEnergyHandle QLowEnergyDescriptor::handle() const
+{
+ if (!data)
+ return 0;
+
+ return data->descHandle;
+}
+
+/*!
+ Returns the value of the descriptor.
+*/
+QByteArray QLowEnergyDescriptor::value() const
+{
+ if (d_ptr.isNull() || !data
+ || !d_ptr->characteristicList.contains(data->charHandle)
+ || !d_ptr->characteristicList[data->charHandle].
+ descriptorList.contains(data->descHandle)) {
+ return QByteArray();
+ }
+
+ return d_ptr->characteristicList[data->charHandle].descriptorList[data->descHandle].value;
+}
+
+/*!
+ Returns the name of the descriptor type.
+
+ \sa type()
+*/
+
+QString QLowEnergyDescriptor::name() const
+{
+ return QBluetoothUuid::descriptorToString(type());
+}
+
+/*!
+ Returns the type of descriptor.
+ */
+QBluetoothUuid::DescriptorType QLowEnergyDescriptor::type() const
+{
+ const QBluetoothUuid u = uuid();
+ bool ok = false;
+ quint16 shortUuid = u.toUInt16(&ok);
+
+ if (!ok)
+ return QBluetoothUuid::UnknownDescriptorType;
+
+ switch (shortUuid) {
+ case QBluetoothUuid::CharacteristicExtendedProperties:
+ case QBluetoothUuid::CharacteristicUserDescription:
+ case QBluetoothUuid::ClientCharacteristicConfiguration:
+ case QBluetoothUuid::ServerCharacteristicConfiguration:
+ case QBluetoothUuid::CharacteristicPresentationFormat:
+ case QBluetoothUuid::CharacteristicAggregateFormat:
+ case QBluetoothUuid::ValidRange:
+ case QBluetoothUuid::ExternalReportReference:
+ case QBluetoothUuid::ReportReference:
+ return (QBluetoothUuid::DescriptorType) shortUuid;
+ default:
+ break;
+ }
+
+ return QBluetoothUuid::UnknownDescriptorType;
+}
+
+/*!
+ \internal
+
+ Returns the handle of the characteristic to which this descriptor belongs
+ */
+QLowEnergyHandle QLowEnergyDescriptor::characteristicHandle() const
+{
+ if (d_ptr.isNull() || !data)
+ return 0;
+
+ return data->charHandle;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergydescriptor.h b/src/bluetooth/qlowenergydescriptor.h
new file mode 100644
index 00000000..2c0a3296
--- /dev/null
+++ b/src/bluetooth/qlowenergydescriptor.h
@@ -0,0 +1,93 @@
+/***************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYDESCRIPTOR_H
+#define QLOWENERGYDESCRIPTOR_H
+
+#include <QtCore/QSharedPointer>
+#include <QtCore/QVariantMap>
+#include <QtBluetooth/qbluetooth.h>
+#include <QtBluetooth/QBluetoothUuid>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyDescriptorPrivate;
+class QLowEnergyServicePrivate;
+
+class Q_BLUETOOTH_EXPORT QLowEnergyDescriptor
+{
+public:
+ QLowEnergyDescriptor();
+ QLowEnergyDescriptor(const QLowEnergyDescriptor &other);
+ ~QLowEnergyDescriptor();
+
+ QLowEnergyDescriptor &operator=(const QLowEnergyDescriptor &other);
+ bool operator==(const QLowEnergyDescriptor &other) const;
+ bool operator!=(const QLowEnergyDescriptor &other) const;
+
+ bool isValid() const;
+
+ QByteArray value() const;
+
+ QBluetoothUuid uuid() const;
+ QLowEnergyHandle handle() const;
+ QString name() const;
+
+ QBluetoothUuid::DescriptorType type() const;
+
+protected:
+ QLowEnergyHandle characteristicHandle() const;
+ QSharedPointer<QLowEnergyServicePrivate> d_ptr;
+
+ friend class QLowEnergyCharacteristic;
+ friend class QLowEnergyService;
+ friend class QLowEnergyControllerPrivate;
+ QLowEnergyDescriptorPrivate *data;
+
+ QLowEnergyDescriptor(QSharedPointer<QLowEnergyServicePrivate> p,
+ QLowEnergyHandle charHandle,
+ QLowEnergyHandle descHandle);
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYDESCRIPTOR_H
diff --git a/src/bluetooth/qlowenergyservice.cpp b/src/bluetooth/qlowenergyservice.cpp
new file mode 100644
index 00000000..725c82e9
--- /dev/null
+++ b/src/bluetooth/qlowenergyservice.cpp
@@ -0,0 +1,322 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QPointer>
+#include <QtBluetooth/QLowEnergyService>
+
+#include <algorithm>
+
+#include "qlowenergycontroller_p.h"
+#include "qlowenergyserviceprivate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \enum QBluetoothDeviceInfo::ServiceType
+
+ This enum describes the type of the service.
+
+ \value PrimaryService The service is a top-level/primary service.
+ If this type flag is not set the service is considered
+ to be a secondary service. Each service may be included
+ by another service which is indicated by \l IncludedService.
+ \value IncludedService The service is included by another service.
+*/
+
+/*!
+ \internal
+
+ QLowEnergyControllerPrivate creates instances of this class.
+ The user gets access to class instances via
+ \l QLowEnergyController::services().
+ */
+QLowEnergyService::QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> p,
+ QObject *parent)
+ : QObject(parent),
+ d_ptr(p)
+{
+ qRegisterMetaType<QLowEnergyService::ServiceState>("QLowEnergyService::ServiceState");
+ qRegisterMetaType<QLowEnergyService::ServiceError>("QLowEnergyService::ServiceError");
+
+ connect(p.data(), SIGNAL(error(QLowEnergyService::ServiceError)),
+ this, SIGNAL(error(QLowEnergyService::ServiceError)));
+ connect(p.data(), SIGNAL(stateChanged(QLowEnergyService::ServiceState)),
+ this, SIGNAL(stateChanged(QLowEnergyService::ServiceState)));
+ connect(p.data(), SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)),
+ this, SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
+ connect(p.data(), SIGNAL(descriptorChanged(QLowEnergyDescriptor,QByteArray)),
+ this, SIGNAL(descriptorChanged(QLowEnergyDescriptor,QByteArray)));
+}
+
+
+QLowEnergyService::~QLowEnergyService()
+{
+}
+
+/*!
+ Returns the uuids of all services which are included by the
+ current service.
+
+ It is possible that an included service contains yet another service. Such
+ second level includes have to be obtained via their relevant first level
+ QLowEnergyService instance. Technically it is possible that this can create
+ a circular dependency.
+
+ \l {QLowEnergyController::createServiceObject} should be used to obtain
+ service instances for each of the uuids.
+
+ \sa createServiceObject()
+ */
+QList<QBluetoothUuid> QLowEnergyService::includedServices() const
+{
+ return d_ptr->includedServices;
+}
+
+QLowEnergyService::ServiceState QLowEnergyService::state() const
+{
+ return d_ptr->state;
+}
+
+QLowEnergyService::ServiceTypes QLowEnergyService::type() const
+{
+ return d_ptr->type;
+}
+
+/*!
+ Returns the matching characteristic for \a uuid; otherwise an invalid
+ characteristic.
+
+ \sa characteristics()
+*/
+QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const
+{
+ foreach (const QLowEnergyHandle handle, d_ptr->characteristicList.keys()) {
+ if (d_ptr->characteristicList[handle].uuid == uuid)
+ return QLowEnergyCharacteristic(d_ptr, handle);
+ }
+
+ return QLowEnergyCharacteristic();
+}
+
+/*!
+ Returns all characteristics associated with this \c QLowEnergyService instance.
+
+ The returned list will be empty if this service instance is invalid,
+ \l discoverDetails() was not yet called or there are no known characteristics.
+
+ \sa characteristic(), state(), discoverDetails
+*/
+
+QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const
+{
+ QList<QLowEnergyCharacteristic> results;
+ QList<QLowEnergyHandle> handles = d_ptr->characteristicList.keys();
+ std::sort(handles.begin(), handles.end());
+
+ foreach (const QLowEnergyHandle &handle, handles) {
+ QLowEnergyCharacteristic characteristic(d_ptr, handle);
+ results.append(characteristic);
+ }
+ return results;
+}
+
+
+QBluetoothUuid QLowEnergyService::serviceUuid() const
+{
+ return d_ptr->uuid;
+}
+
+
+QString QLowEnergyService::serviceName() const
+{
+ bool ok = false;
+ quint16 clsId = d_ptr->uuid.toUInt16(&ok);
+ if (ok) {
+ QBluetoothUuid::ServiceClassUuid id
+ = static_cast<QBluetoothUuid::ServiceClassUuid>(clsId);
+ const QString name = QBluetoothUuid::serviceClassToString(id);
+ if (!name.isEmpty())
+ return name;
+ }
+ return qApp ?
+ qApp->translate("QBluetoothServiceDiscoveryAgent", "Unknown Service") :
+ QStringLiteral("Unknown Service");
+}
+
+
+void QLowEnergyService::discoverDetails()
+{
+ Q_D(QLowEnergyService);
+
+ if (!d->controller || d->state == QLowEnergyService::InvalidService) {
+ d->setError(QLowEnergyService::ServiceNotValidError);
+ return;
+ }
+
+ if (d->state != QLowEnergyService::DiscoveryRequired)
+ return;
+
+ d->setState(QLowEnergyService::DiscoveringServices);
+
+ d->controller->discoverServiceDetails(d->uuid);
+}
+
+QLowEnergyService::ServiceError QLowEnergyService::error() const
+{
+ return d_ptr->lastError;
+}
+
+
+/*!
+ Returns \c true if \a characteristic belongs to this service; otherwise \c false.
+
+ A characteristic belongs to a service if \l {QLowEnergyService::characteristics()}
+ contains the \a characteristic.
+ */
+bool QLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const
+{
+ if (characteristic.d_ptr.isNull() || !characteristic.data)
+ return false;
+
+ if (d_ptr == characteristic.d_ptr
+ && d_ptr->characteristicList.contains(characteristic.attributeHandle())) {
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Writes \a newValue as value for the \a characteristic. If the operation is successful
+ the \l characteristicChanged() signal will be emitted. \a newValue must contain the
+ hexadecimal representation of new value.
+
+ A characteristic can only be written if this service is in the \l ServiceDiscovered state
+ and \a characteristic is writable.
+ */
+void QLowEnergyService::writeCharacteristic(
+ const QLowEnergyCharacteristic &characteristic, const QByteArray &newValue)
+{
+ //TODO check behavior when writing to WriteNoResponse characteristic
+ //TODO check behavior when writing to WriteSigned characteristic
+ //TODO add support for write long characteristic value (newValue.size() > MTU - 3)
+ Q_D(QLowEnergyService);
+
+ // not a characteristic of this service
+ if (!contains(characteristic))
+ return;
+
+ // don't write if we don't have to
+ if (characteristic.value() == newValue)
+ return;
+
+ // don't write write-protected or undiscovered characteristic
+ if (!(characteristic.properties() & QLowEnergyCharacteristic::Write)
+ || state() != ServiceDiscovered) {
+ d->setError(QLowEnergyService::OperationError);
+ return;
+ }
+
+ if (!d->controller)
+ return;
+
+ d->controller->writeCharacteristic(characteristic.d_ptr,
+ characteristic.attributeHandle(),
+ newValue);
+}
+
+/*!
+ Returns \c true if \a descriptor belongs to this service; otherwise \c false.
+ */
+bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const
+{
+ if (descriptor.d_ptr.isNull() || !descriptor.data)
+ return false;
+
+ const QLowEnergyHandle charHandle = descriptor.characteristicHandle();
+ if (!charHandle)
+ return false;
+
+ if (d_ptr == descriptor.d_ptr
+ && d_ptr->characteristicList.contains(charHandle)
+ && d_ptr->characteristicList[charHandle].descriptorList.contains(descriptor.handle()))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ Writes \a newValue as value for \a descriptor. If the operation is successful
+ the \l descriptorChanged() signal is emitted. \a newValue must contain the
+ hexadecimal representation of new value.
+
+ A descriptor can only be written if this service is in the \l ServiceDiscovered state
+ and \a characteristic is writable.
+ */
+void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor,
+ const QByteArray &newValue)
+{
+ //TODO not all descriptors are writable (how to deal with write errors)
+ Q_D(QLowEnergyService);
+
+ if (!contains(descriptor))
+ return;
+
+ if (descriptor.value() == newValue)
+ return;
+
+ if (state() != ServiceDiscovered || !d->controller) {
+ d->setError(QLowEnergyService::OperationError);
+ return;
+ }
+
+ d->controller->writeDescriptor(descriptor.d_ptr,
+ descriptor.characteristicHandle(),
+ descriptor.handle(),
+ newValue);
+}
+
+
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergyservice.h b/src/bluetooth/qlowenergyservice.h
new file mode 100644
index 00000000..d7033224
--- /dev/null
+++ b/src/bluetooth/qlowenergyservice.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QLOWENERGYSERVICE_H
+#define QLOWENERGYSERVICE_H
+
+#include <QtBluetooth/QBluetoothAddress>
+#include <QtBluetooth/QBluetoothUuid>
+#include <QtBluetooth/QLowEnergyCharacteristic>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyServicePrivate;
+class QLowEnergyControllerPrivate;
+class Q_BLUETOOTH_EXPORT QLowEnergyService : public QObject
+{
+ Q_OBJECT
+public:
+ enum ServiceType {
+ PrimaryService = 0x0001,
+ IncludedService = 0x0002
+ };
+ Q_DECLARE_FLAGS(ServiceTypes, ServiceType)
+
+ enum ServiceError {
+ NoError = 0,
+ ServiceNotValidError,
+ OperationError,
+ CharacteristicWriteError, // emitted when writeCharacteristic() failed
+ DescriptorWriteError // emitted when writeDescriptor() failed
+ };
+
+ enum ServiceState {
+ InvalidService = 0, // when underlying controller disconnects
+ DiscoveryRequired, // we know start/end handle but nothing more
+ DiscoveringServices,// discoverDetails() called and running
+ ServiceDiscovered, // all details have been synchronized
+ };
+
+ ~QLowEnergyService();
+
+ QList<QBluetoothUuid> includedServices() const;
+
+ QLowEnergyService::ServiceTypes type() const;
+ QLowEnergyService::ServiceState state() const;
+
+ QLowEnergyCharacteristic characteristic(const QBluetoothUuid &uuid) const;
+ QList<QLowEnergyCharacteristic> characteristics() const;
+ QBluetoothUuid serviceUuid() const;
+ QString serviceName() const;
+
+ void discoverDetails();
+
+ ServiceError error() const;
+
+ bool contains(const QLowEnergyCharacteristic &characteristic) const;
+ void writeCharacteristic(const QLowEnergyCharacteristic &characteristic,
+ const QByteArray &newValue);
+
+ bool contains(const QLowEnergyDescriptor &descriptor) const;
+ void writeDescriptor(const QLowEnergyDescriptor &descriptor,
+ const QByteArray &newValue);
+
+Q_SIGNALS:
+ void stateChanged(QLowEnergyService::ServiceState newState);
+ void characteristicChanged(const QLowEnergyCharacteristic &info,
+ const QByteArray &value);
+ void descriptorChanged(const QLowEnergyDescriptor &info,
+ const QByteArray &value);
+ void error(QLowEnergyService::ServiceError error);
+
+private:
+ Q_DECLARE_PRIVATE(QLowEnergyService)
+ QSharedPointer<QLowEnergyServicePrivate> d_ptr;
+
+ // QLowEnergyController is the factory for this class
+ friend class QLowEnergyController;
+ QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> p,
+ QObject *parent = 0);
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYSERVICE_H
diff --git a/src/bluetooth/qlowenergyserviceinfo.cpp b/src/bluetooth/qlowenergyserviceinfo.cpp
new file mode 100644
index 00000000..572e71d7
--- /dev/null
+++ b/src/bluetooth/qlowenergyserviceinfo.cpp
@@ -0,0 +1,180 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergyserviceinfo.h"
+#include "qlowenergyserviceinfo_p.h"
+#include <QtCore/QCoreApplication>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QLowEnergyServiceInfo
+ \inmodule QtBluetooth
+ \brief The QLowEnergyServiceInfo class stores information about the Bluetooth
+ Low Energy service.
+ \since 5.4
+
+ QLowEnergyServiceInfo provides information about a Bluetooth Low Energy
+ service's name, device, UUID, connection status, service type, handle
+ and characteristics. A Bluetooth Low Energy device can have one or more
+ low energy services. Each low energy service contains one or more
+ characteristics. The class is used with the QLowEnergyController
+ class. It is necessary to connect to the service first in order
+ to get the all service information and characteristics.
+*/
+
+/*!
+ \enum QLowEnergyServiceInfo::ServiceType
+
+ This enum describes the type of the service. One LE device can have one or more primary services.
+
+ \value PrimaryService The primary service. The primary service can have one or
+ more included services.
+ \value IncludedService The included service by primary services.
+*/
+
+/*!
+ Construct a new QLowEnergyServiceInfo.
+*/
+QLowEnergyServiceInfo::QLowEnergyServiceInfo():
+ d_ptr(QSharedPointer<QLowEnergyServiceInfoPrivate>(new QLowEnergyServiceInfoPrivate))
+{
+
+}
+
+/*!
+ Construct a new QLowEnergyServiceInfo object with the given \a uuid.
+
+ Based on uuid, corresponsing service name is given.
+*/
+QLowEnergyServiceInfo::QLowEnergyServiceInfo(const QBluetoothUuid &uuid):
+ d_ptr(QSharedPointer<QLowEnergyServiceInfoPrivate>(new QLowEnergyServiceInfoPrivate))
+{
+ d_ptr->uuid = QBluetoothUuid(uuid);
+}
+
+/*!
+ Construct a new QLowEnergyServiceInfo that is a copy of \a other.
+
+ The two copies continue to share the same underlying data which does not detach
+ upon write.
+*/
+QLowEnergyServiceInfo::QLowEnergyServiceInfo(const QLowEnergyServiceInfo &other):
+ d_ptr(other.d_ptr)
+{
+
+}
+
+/*!
+ Destroys the QLowEnergyServiceInfo object.
+*/
+QLowEnergyServiceInfo::~QLowEnergyServiceInfo()
+{
+
+}
+
+/*!
+ Returns the gatt service uuid.
+*/
+QBluetoothUuid QLowEnergyServiceInfo::serviceUuid() const
+{
+ return d_ptr->uuid;
+}
+
+/*!
+ Returns the service name.
+*/
+QString QLowEnergyServiceInfo::serviceName() const
+{
+ bool ok = false;
+ quint16 clsId = d_ptr->uuid.toUInt16(&ok);
+ if (ok) {
+ QBluetoothUuid::ServiceClassUuid id
+ = static_cast<QBluetoothUuid::ServiceClassUuid>(clsId);
+ return QBluetoothUuid::serviceClassToString(id);
+ }
+ return qApp ?
+ qApp->translate("QBluetoothServiceDiscoveryAgent", "Unknown Service") :
+ QStringLiteral("Unknown Service");
+}
+
+/*!
+ Returns a copy of \a other and assigns it to this QLowEnergyServiceInfo object.
+ The two copies continue to share the same service and registration details.
+*/
+QLowEnergyServiceInfo &QLowEnergyServiceInfo::operator=(const QLowEnergyServiceInfo &other)
+{
+ d_ptr = other.d_ptr;
+ return *this;
+}
+
+/*!
+ Returns the address of the Bluetooth device that provides this service.
+*/
+QBluetoothDeviceInfo QLowEnergyServiceInfo::device() const
+{
+ return d_ptr->deviceInfo;
+}
+
+/*!
+ Sets the Bluetooth device that provides this service to \a device.
+*/
+void QLowEnergyServiceInfo::setDevice(const QBluetoothDeviceInfo &device)
+{
+ d_ptr->deviceInfo = device;
+}
+
+/*!
+ Returns true if the QLowEnergyServiceInfo object is valid, otherwise returns false.
+*/
+bool QLowEnergyServiceInfo::isValid() const
+{
+ if (d_ptr->uuid == QBluetoothUuid())
+ return false;
+ if (!d_ptr->deviceInfo.isValid())
+ return false;
+ return true;
+}
+
+QT_END_NAMESPACE
+
+
diff --git a/src/bluetooth/qlowenergyserviceinfo.h b/src/bluetooth/qlowenergyserviceinfo.h
new file mode 100644
index 00000000..43ddff30
--- /dev/null
+++ b/src/bluetooth/qlowenergyserviceinfo.h
@@ -0,0 +1,81 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYSERVICEINFO_H
+#define QLOWENERGYSERVICEINFO_H
+#include <QtCore/QSharedPointer>
+#include <QtBluetooth/QBluetoothAddress>
+#include <QtBluetooth/QBluetoothDeviceInfo>
+#include <QtBluetooth/QBluetoothUuid>
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyServiceInfoPrivate;
+
+class Q_BLUETOOTH_EXPORT QLowEnergyServiceInfo
+{
+public:
+ QLowEnergyServiceInfo();
+ QLowEnergyServiceInfo(const QBluetoothUuid &uuid);
+ QLowEnergyServiceInfo(const QLowEnergyServiceInfo &other);
+
+ ~QLowEnergyServiceInfo();
+
+ QLowEnergyServiceInfo &operator=(const QLowEnergyServiceInfo &other);
+
+ void setDevice(const QBluetoothDeviceInfo &info);
+ QBluetoothDeviceInfo device() const;
+
+ QBluetoothUuid serviceUuid() const;
+
+ QString serviceName() const;
+
+ bool isValid() const;
+
+protected:
+ QSharedPointer<QLowEnergyServiceInfoPrivate> d_ptr;
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYSERVICEINFO_H
diff --git a/src/bluetooth/qlowenergyserviceinfo_p.h b/src/bluetooth/qlowenergyserviceinfo_p.h
new file mode 100644
index 00000000..6d1d3c73
--- /dev/null
+++ b/src/bluetooth/qlowenergyserviceinfo_p.h
@@ -0,0 +1,58 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYSERVICEINFO_P_H
+#define QLOWENERGYSERVICEINFO_P_H
+#include "qlowenergyserviceinfo.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyServiceInfoPrivate
+{
+public:
+ QBluetoothUuid uuid;
+ QBluetoothDeviceInfo deviceInfo;
+};
+
+QT_END_NAMESPACE
+
+#endif // QLOWENERGYSERVICEINFO_P_H
diff --git a/src/bluetooth/qlowenergyserviceprivate.cpp b/src/bluetooth/qlowenergyserviceprivate.cpp
new file mode 100644
index 00000000..4768582d
--- /dev/null
+++ b/src/bluetooth/qlowenergyserviceprivate.cpp
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qlowenergyserviceprivate_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QLowEnergyServicePrivate::QLowEnergyServicePrivate(QObject *parent) :
+ QObject(parent),
+ type(QLowEnergyService::PrimaryService),
+ state(QLowEnergyService::InvalidService),
+ lastError(QLowEnergyService::NoError)
+{
+}
+
+QLowEnergyServicePrivate::~QLowEnergyServicePrivate()
+{
+}
+
+void QLowEnergyServicePrivate::setController(QLowEnergyControllerPrivate *control)
+{
+ controller = control;
+
+ if (control)
+ setState(QLowEnergyService::DiscoveryRequired);
+ else
+ setState(QLowEnergyService::InvalidService);
+}
+
+void QLowEnergyServicePrivate::setError(QLowEnergyService::ServiceError newError)
+{
+ lastError = newError;
+ emit error(newError);
+}
+
+void QLowEnergyServicePrivate::setState(QLowEnergyService::ServiceState newState)
+{
+ state = newState;
+ emit stateChanged(newState);
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qlowenergyserviceprivate_p.h b/src/bluetooth/qlowenergyserviceprivate_p.h
new file mode 100644
index 00000000..cc0fafee
--- /dev/null
+++ b/src/bluetooth/qlowenergyserviceprivate_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLOWENERGYSERVICEPRIVATE_P_H
+#define QLOWENERGYSERVICEPRIVATE_P_H
+
+#include <QtCore/QObject>
+#include <QtCore/QPointer>
+#include <QtBluetooth/qbluetooth.h>
+#include <QtBluetooth/QLowEnergyService>
+#include <QtBluetooth/QLowEnergyCharacteristic>
+
+#include "qlowenergycontroller_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QLowEnergyServicePrivate : public QObject
+{
+ Q_OBJECT
+public:
+ explicit QLowEnergyServicePrivate(QObject *parent = 0);
+ ~QLowEnergyServicePrivate();
+
+ struct DescData {
+ QByteArray value;
+ QBluetoothUuid uuid;
+ };
+
+ struct CharData {
+ QLowEnergyHandle valueHandle;
+ QBluetoothUuid uuid;
+ QLowEnergyCharacteristic::PropertyTypes properties;
+ QByteArray value;
+ QHash<QLowEnergyHandle, DescData> descriptorList;
+ };
+
+ enum GattAttributeTypes {
+ PrimaryService = 0x2800,
+ SecondaryService = 0x2801,
+ IncludeAttribute = 0x2802,
+ Characteristic = 0x2803
+ };
+
+ void setController(QLowEnergyControllerPrivate* control);
+ void setError(QLowEnergyService::ServiceError newError);
+ void setState(QLowEnergyService::ServiceState newState);
+
+signals:
+ void stateChanged(QLowEnergyService::ServiceState newState);
+ void error(QLowEnergyService::ServiceError error);
+ void characteristicChanged(const QLowEnergyCharacteristic &characteristic,
+ const QByteArray &newValue);
+ void descriptorChanged(const QLowEnergyDescriptor &descriptor,
+ const QByteArray &newValue);
+
+public:
+ QLowEnergyHandle startHandle;
+ QLowEnergyHandle endHandle;
+
+ QBluetoothUuid uuid;
+ QList<QBluetoothUuid> includedServices;
+ QLowEnergyService::ServiceTypes type;
+ QLowEnergyService::ServiceState state;
+ QLowEnergyService::ServiceError lastError;
+
+ QHash<QLowEnergyHandle, CharData> characteristicList;
+
+ QPointer<QLowEnergyControllerPrivate> controller;
+};
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QSharedPointer<QLowEnergyServicePrivate>)
+
+#endif // QLOWENERGYSERVICEPRIVATE_P_H
diff --git a/src/nfc/qnx/qnxnfcmanager.cpp b/src/nfc/qnx/qnxnfcmanager.cpp
index edc2d2be..2ba1029b 100644
--- a/src/nfc/qnx/qnxnfcmanager.cpp
+++ b/src/nfc/qnx/qnxnfcmanager.cpp
@@ -44,6 +44,7 @@
#include <QMetaObject>
#include "../qllcpsocket_qnx_p.h"
#include <QCoreApplication>
+#include <QStringList>
QT_BEGIN_NAMESPACE
diff --git a/sync.profile b/sync.profile
index 1204b458..0ceddf44 100644
--- a/sync.profile
+++ b/sync.profile
@@ -17,3 +17,6 @@
"qtxmlpatterns" => "",
"qtandroidextras" => "",
);
+
+my @gato_headers = ("gatoattclient.h", "gatoperipheral.h");
+@ignore_for_master_contents = ( @gato_headers );
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index 5a703944..8966e5d9 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -16,7 +16,11 @@ qtHaveModule(bluetooth) {
qbluetoothtransfermanager \
qbluetoothtransferrequest \
qbluetoothuuid \
- qbluetoothserver
+ qbluetoothserver \
+ qlowenergycharacteristic \
+ qlowenergydescriptor \
+ qlowenergyserviceinfo \
+ qlowenergycontroller
}
qtHaveModule(nfc) {
diff --git a/tests/auto/qbluetoothdeviceinfo/qbluetoothdeviceinfo.pro b/tests/auto/qbluetoothdeviceinfo/qbluetoothdeviceinfo.pro
index 43ca52ed..e33125c2 100644
--- a/tests/auto/qbluetoothdeviceinfo/qbluetoothdeviceinfo.pro
+++ b/tests/auto/qbluetoothdeviceinfo/qbluetoothdeviceinfo.pro
@@ -4,3 +4,6 @@ CONFIG += testcase
QT = core concurrent bluetooth testlib
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+blackberry {
+ LIBS += -lbtapi
+}
diff --git a/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp b/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp
index adc61199..023c1390 100644
--- a/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp
+++ b/tests/auto/qbluetoothdeviceinfo/tst_qbluetoothdeviceinfo.cpp
@@ -52,6 +52,7 @@ QT_USE_NAMESPACE
Q_DECLARE_METATYPE(QBluetoothDeviceInfo::ServiceClasses)
Q_DECLARE_METATYPE(QBluetoothDeviceInfo::MajorDeviceClass)
+Q_DECLARE_METATYPE(QBluetoothDeviceInfo::CoreConfiguration)
class tst_QBluetoothDeviceInfo : public QObject
{
@@ -101,6 +102,7 @@ void tst_QBluetoothDeviceInfo::tst_construction_data()
QTest::addColumn<QBluetoothDeviceInfo::ServiceClasses>("serviceClasses");
QTest::addColumn<QBluetoothDeviceInfo::MajorDeviceClass>("majorDeviceClass");
QTest::addColumn<quint8>("minorDeviceClass");
+ QTest::addColumn<QBluetoothDeviceInfo::CoreConfiguration>("coreConfiguration");
// bits 12-8 Major
// bits 7-2 Minor
@@ -110,120 +112,144 @@ void tst_QBluetoothDeviceInfo::tst_construction_data()
<< quint32(0x000000)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::MiscellaneousDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous);
+ << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous)
+ << QBluetoothDeviceInfo::BaseRateCoreConfiguration;
QTest::newRow("0x000100 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000100)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::ComputerDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedComputer);
+ << quint8(QBluetoothDeviceInfo::UncategorizedComputer)
+ << QBluetoothDeviceInfo::BaseRateCoreConfiguration;
QTest::newRow("0x000104 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000104)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::ComputerDevice
- << quint8(QBluetoothDeviceInfo::DesktopComputer);
+ << quint8(QBluetoothDeviceInfo::DesktopComputer)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000118 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000118)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::ComputerDevice
- << quint8(QBluetoothDeviceInfo::WearableComputer);
+ << quint8(QBluetoothDeviceInfo::WearableComputer)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000200 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device" << quint32(0x000200)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::PhoneDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedPhone);
+ << quint8(QBluetoothDeviceInfo::UncategorizedPhone)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000204 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000204)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::PhoneDevice
- << quint8(QBluetoothDeviceInfo::CellularPhone);
+ << quint8(QBluetoothDeviceInfo::CellularPhone)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000214 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device" << quint32(0x000214)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::PhoneDevice
- << quint8(QBluetoothDeviceInfo::CommonIsdnAccessPhone);
+ << quint8(QBluetoothDeviceInfo::CommonIsdnAccessPhone)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000300 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000300)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::LANAccessDevice
- << quint8(QBluetoothDeviceInfo::NetworkFullService);
+ << quint8(QBluetoothDeviceInfo::NetworkFullService)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000320 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000320)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::LANAccessDevice
- << quint8(QBluetoothDeviceInfo::NetworkLoadFactorOne);
+ << quint8(QBluetoothDeviceInfo::NetworkLoadFactorOne)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x0003E0 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x0003E0)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::LANAccessDevice
- << quint8(QBluetoothDeviceInfo::NetworkNoService);
+ << quint8(QBluetoothDeviceInfo::NetworkNoService)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000400 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000400)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::AudioVideoDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedAudioVideoDevice);
+ << quint8(QBluetoothDeviceInfo::UncategorizedAudioVideoDevice)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000448 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000448)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::AudioVideoDevice
- << quint8(QBluetoothDeviceInfo::GamingDevice);
+ << quint8(QBluetoothDeviceInfo::GamingDevice)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000500 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000500)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::PeripheralDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedPeripheral);
+ << quint8(QBluetoothDeviceInfo::UncategorizedPeripheral)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x0005D8 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x0005D8)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::PeripheralDevice
- << quint8(QBluetoothDeviceInfo::KeyboardWithPointingDevicePeripheral | QBluetoothDeviceInfo::CardReaderPeripheral);
+ << quint8(QBluetoothDeviceInfo::KeyboardWithPointingDevicePeripheral | QBluetoothDeviceInfo::CardReaderPeripheral)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000600 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000600)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::ImagingDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedImagingDevice);
+ << quint8(QBluetoothDeviceInfo::UncategorizedImagingDevice)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
QTest::newRow("0x000680 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000680)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::ImagingDevice
- << quint8(QBluetoothDeviceInfo::ImagePrinter);
+ << quint8(QBluetoothDeviceInfo::ImagePrinter)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0x000700 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000700)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::WearableDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedWearableDevice);
+ << quint8(QBluetoothDeviceInfo::UncategorizedWearableDevice)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0x000714 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000714)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::WearableDevice
- << quint8(QBluetoothDeviceInfo::WearableGlasses);
+ << quint8(QBluetoothDeviceInfo::WearableGlasses)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0x000800 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000800)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::ToyDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedToy);
+ << quint8(QBluetoothDeviceInfo::UncategorizedToy)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0x000814 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x000814)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::ToyDevice
- << quint8(QBluetoothDeviceInfo::ToyGame);
+ << quint8(QBluetoothDeviceInfo::ToyGame)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0x001f00 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x001f00)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::NoService)
<< QBluetoothDeviceInfo::UncategorizedDevice
- << quint8(0);
+ << quint8(0)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0x002000 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x002000)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::PositioningService)
<< QBluetoothDeviceInfo::MiscellaneousDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous);
+ << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0x100000 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0x100000)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::InformationService)
<< QBluetoothDeviceInfo::MiscellaneousDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous);
+ << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
QTest::newRow("0xFFE000 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
<< quint32(0xFFE000)
<< QBluetoothDeviceInfo::ServiceClasses(QBluetoothDeviceInfo::AllServices)
<< QBluetoothDeviceInfo::MiscellaneousDevice
- << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous);
+ << quint8(QBluetoothDeviceInfo::UncategorizedMiscellaneous)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
}
void tst_QBluetoothDeviceInfo::tst_construction()
@@ -241,6 +267,7 @@ void tst_QBluetoothDeviceInfo::tst_construction()
QFETCH(QBluetoothDeviceInfo::ServiceClasses, serviceClasses);
QFETCH(QBluetoothDeviceInfo::MajorDeviceClass, majorDeviceClass);
QFETCH(quint8, minorDeviceClass);
+ QFETCH(QBluetoothDeviceInfo::CoreConfiguration, coreConfiguration);
QBluetoothDeviceInfo deviceInfo(address, name, classOfDevice);
@@ -251,9 +278,12 @@ void tst_QBluetoothDeviceInfo::tst_construction()
QCOMPARE(deviceInfo.serviceClasses(), serviceClasses);
QCOMPARE(deviceInfo.majorDeviceClass(), majorDeviceClass);
QCOMPARE(deviceInfo.minorDeviceClass(), minorDeviceClass);
+ QCOMPARE(deviceInfo.coreConfigurations(), QBluetoothDeviceInfo::BaseRateCoreConfiguration);
- QBluetoothDeviceInfo copyInfo(deviceInfo);
+ deviceInfo.setCoreConfigurations(coreConfiguration);
+ QCOMPARE(deviceInfo.coreConfigurations(), coreConfiguration);
+ QBluetoothDeviceInfo copyInfo(deviceInfo);
QVERIFY(copyInfo.isValid());
QCOMPARE(copyInfo.address(), address);
@@ -261,6 +291,7 @@ void tst_QBluetoothDeviceInfo::tst_construction()
QCOMPARE(copyInfo.serviceClasses(), serviceClasses);
QCOMPARE(copyInfo.majorDeviceClass(), majorDeviceClass);
QCOMPARE(copyInfo.minorDeviceClass(), minorDeviceClass);
+ QCOMPARE(copyInfo.coreConfigurations(), coreConfiguration);
}
}
@@ -277,8 +308,10 @@ void tst_QBluetoothDeviceInfo::tst_assignment()
QFETCH(QBluetoothDeviceInfo::ServiceClasses, serviceClasses);
QFETCH(QBluetoothDeviceInfo::MajorDeviceClass, majorDeviceClass);
QFETCH(quint8, minorDeviceClass);
+ QFETCH(QBluetoothDeviceInfo::CoreConfiguration, coreConfiguration);
QBluetoothDeviceInfo deviceInfo(address, name, classOfDevice);
+ deviceInfo.setCoreConfigurations(coreConfiguration);
QVERIFY(deviceInfo.isValid());
@@ -292,6 +325,7 @@ void tst_QBluetoothDeviceInfo::tst_assignment()
QCOMPARE(copyInfo.serviceClasses(), serviceClasses);
QCOMPARE(copyInfo.majorDeviceClass(), majorDeviceClass);
QCOMPARE(copyInfo.minorDeviceClass(), minorDeviceClass);
+ QCOMPARE(copyInfo.coreConfigurations(), coreConfiguration);
}
{
@@ -308,6 +342,7 @@ void tst_QBluetoothDeviceInfo::tst_assignment()
QCOMPARE(copyInfo.serviceClasses(), serviceClasses);
QCOMPARE(copyInfo.majorDeviceClass(), majorDeviceClass);
QCOMPARE(copyInfo.minorDeviceClass(), minorDeviceClass);
+ QCOMPARE(copyInfo.coreConfigurations(), coreConfiguration);
}
{
@@ -333,6 +368,8 @@ void tst_QBluetoothDeviceInfo::tst_assignment()
QCOMPARE(copyInfo2.majorDeviceClass(), majorDeviceClass);
QCOMPARE(copyInfo1.minorDeviceClass(), minorDeviceClass);
QCOMPARE(copyInfo2.minorDeviceClass(), minorDeviceClass);
+ QCOMPARE(copyInfo1.coreConfigurations(), coreConfiguration);
+ QCOMPARE(copyInfo2.coreConfigurations(), coreConfiguration);
}
{
diff --git a/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro b/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro
index 7b8ee74a..cdf8a78b 100644
--- a/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro
+++ b/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro
@@ -3,5 +3,8 @@ TARGET = tst_qbluetoothservicediscoveryagent
CONFIG += testcase
QT = core concurrent bluetooth testlib
+blackberry {
+ LIBS += -lbtapi
+}
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
diff --git a/tests/auto/qbluetoothservicediscoveryagent/tst_qbluetoothservicediscoveryagent.cpp b/tests/auto/qbluetoothservicediscoveryagent/tst_qbluetoothservicediscoveryagent.cpp
index 6aded142..398fb1ad 100644
--- a/tests/auto/qbluetoothservicediscoveryagent/tst_qbluetoothservicediscoveryagent.cpp
+++ b/tests/auto/qbluetoothservicediscoveryagent/tst_qbluetoothservicediscoveryagent.cpp
@@ -51,11 +51,13 @@
#include <qbluetoothlocaldevice.h>
#include <qbluetoothserver.h>
#include <qbluetoothserviceinfo.h>
+#include <qlowenergyserviceinfo.h>
QT_USE_NAMESPACE
Q_DECLARE_METATYPE(QBluetoothDeviceInfo)
Q_DECLARE_METATYPE(QBluetoothServiceDiscoveryAgent::Error)
+Q_DECLARE_METATYPE(QLowEnergyServiceInfo)
// Maximum time to for bluetooth device scan
const int MaxScanTime = 5 * 60 * 1000; // 5 minutes in ms
@@ -71,6 +73,7 @@ public:
public slots:
void deviceDiscoveryDebug(const QBluetoothDeviceInfo &info);
void serviceDiscoveryDebug(const QBluetoothServiceInfo &info);
+ void leServiceDiscoveryDebug(const QLowEnergyServiceInfo &info);
void serviceError(const QBluetoothServiceDiscoveryAgent::Error err);
private slots:
@@ -100,6 +103,7 @@ tst_QBluetoothServiceDiscoveryAgent::tst_QBluetoothServiceDiscoveryAgent()
qRegisterMetaType<QBluetoothDeviceInfo>("QBluetoothDeviceInfo");
qRegisterMetaType<QBluetoothServiceInfo>("QBluetoothServiceInfo");
+ qRegisterMetaType<QLowEnergyServiceInfo>("QLowEnergyServiceInfo");
qRegisterMetaType<QList<QBluetoothUuid> >("QList<QBluetoothUuid>");
qRegisterMetaType<QBluetoothServiceDiscoveryAgent::Error>("QBluetoothServiceDiscoveryAgent::Error");
qRegisterMetaType<QBluetoothDeviceDiscoveryAgent::Error>("QBluetoothDeviceDiscoveryAgent::Error");
@@ -180,6 +184,14 @@ void tst_QBluetoothServiceDiscoveryAgent::serviceDiscoveryDebug(const QBluetooth
qDebug() << "\tRFCOMM server channel:" << info.serverChannel();
}
+void tst_QBluetoothServiceDiscoveryAgent::leServiceDiscoveryDebug(const QLowEnergyServiceInfo &info)
+{
+ qDebug() << "Discovered LE service on"
+ << info.device().name() << info.device().address().toString();
+ qDebug() << "\tService name:" << info.serviceName();
+ qDebug() << "\tUUID:" << info.serviceUuid();
+}
+
static void dumpAttributeVariant(const QVariant &var, const QString indent)
{
if (!var.isValid()) {
@@ -316,7 +328,7 @@ void tst_QBluetoothServiceDiscoveryAgent::tst_serviceDiscoveryAdapters()
QVERIFY(serviceInfo.registerService());
QVERIFY(server.isListening());
- qDebug() << "Scanning address" << addresses[0].toString();
+ qDebug() << "Scanning address " << addresses[0].toString();
QBluetoothServiceDiscoveryAgent discoveryAgent(addresses[1]);
bool setAddress = discoveryAgent.setRemoteAddress(addresses[0]);
@@ -364,7 +376,6 @@ void tst_QBluetoothServiceDiscoveryAgent::tst_serviceDiscovery()
QFETCH(QBluetoothServiceDiscoveryAgent::Error, serviceDiscoveryError);
QBluetoothLocalDevice localDevice;
-
qDebug() << "Scanning address" << deviceInfo.address().toString();
QBluetoothServiceDiscoveryAgent discoveryAgent(localDevice.address());
bool setAddress = discoveryAgent.setRemoteAddress(deviceInfo.address());
@@ -384,8 +395,11 @@ void tst_QBluetoothServiceDiscoveryAgent::tst_serviceDiscovery()
QSignalSpy finishedSpy(&discoveryAgent, SIGNAL(finished()));
QSignalSpy errorSpy(&discoveryAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)));
QSignalSpy discoveredSpy(&discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)));
+ QSignalSpy leDiscoveredSpy(&discoveryAgent, SIGNAL(serviceDiscovered(QLowEnergyServiceInfo)));
// connect(&discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)),
// this, SLOT(serviceDiscoveryDebug(QBluetoothServiceInfo)));
+// connect(&discoveryAgent, SIGNAL(serviceDiscovered(QLowEnergyServiceInfo)),
+// this, SLOT(leServiceDiscoveryDebug(QLowEnergyServiceInfo)));
connect(&discoveryAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)),
this, SLOT(serviceError(QBluetoothServiceDiscoveryAgent::Error)));
@@ -451,6 +465,21 @@ void tst_QBluetoothServiceDiscoveryAgent::tst_serviceDiscovery()
}
+ while (!leDiscoveredSpy.isEmpty()) {
+ const QVariant v = leDiscoveredSpy.takeFirst().at(0);
+ if (v.userType() == qMetaTypeId<QLowEnergyServiceInfo>())
+ {
+ const QLowEnergyServiceInfo info =
+ *reinterpret_cast<const QLowEnergyServiceInfo*>(v.constData());
+
+ QVERIFY(info.isValid());
+ QVERIFY(info.device().coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ } else {
+ QFAIL("Unknown type returned by service discovery");
+ }
+
+ }
+
QVERIFY(discoveryAgent.discoveredServices().count() != 0);
discoveryAgent.clear();
QVERIFY(discoveryAgent.discoveredServices().count() == 0);
diff --git a/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro b/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro
new file mode 100644
index 00000000..33302d60
--- /dev/null
+++ b/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro
@@ -0,0 +1,9 @@
+SOURCES += tst_qlowenergycharacteristic.cpp
+TARGET = tst_qlowenergycharacteristic
+CONFIG += testcase
+
+QT = core bluetooth testlib
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+blackberry {
+ LIBS += -lbtapi
+}
diff --git a/tests/auto/qlowenergycharacteristic/tst_qlowenergycharacteristic.cpp b/tests/auto/qlowenergycharacteristic/tst_qlowenergycharacteristic.cpp
new file mode 100644
index 00000000..9ecddbd5
--- /dev/null
+++ b/tests/auto/qlowenergycharacteristic/tst_qlowenergycharacteristic.cpp
@@ -0,0 +1,358 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QUuid>
+
+#include <QDebug>
+
+#include <QBluetoothDeviceDiscoveryAgent>
+#include <QLowEnergyCharacteristic>
+#include <QLowEnergyController>
+#include <QBluetoothLocalDevice>
+
+QT_USE_NAMESPACE
+
+class tst_QLowEnergyCharacteristic : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QLowEnergyCharacteristic();
+ ~tst_QLowEnergyCharacteristic();
+
+protected slots:
+ void deviceDiscovered(const QBluetoothDeviceInfo &info);
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void tst_constructionDefault();
+ void tst_assignCompare();
+
+private:
+ QSet<QString> remoteLeDevices;
+ QLowEnergyController *globalControl;
+ QLowEnergyService *globalService;
+};
+
+tst_QLowEnergyCharacteristic::tst_QLowEnergyCharacteristic() :
+ globalControl(0), globalService(0)
+{
+ QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+}
+
+tst_QLowEnergyCharacteristic::~tst_QLowEnergyCharacteristic()
+{
+}
+
+void tst_QLowEnergyCharacteristic::initTestCase()
+{
+ if (QBluetoothLocalDevice::allDevices().isEmpty()) {
+ qWarning("No remote device discovered.");
+ return;
+ }
+
+ // start Bluetooth if not started
+ QBluetoothLocalDevice device;
+ device.powerOn();
+
+ // find an arbitrary low energy device in vincinity
+ // find an arbitrary service with characteristic
+ QBluetoothDeviceDiscoveryAgent *devAgent = new QBluetoothDeviceDiscoveryAgent(this);
+ connect(devAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
+ this, SLOT(deviceDiscovered(QBluetoothDeviceInfo)));
+
+ QSignalSpy errorSpy(devAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)));
+ QVERIFY(errorSpy.isValid());
+ QVERIFY(errorSpy.isEmpty());
+
+ QSignalSpy spy(devAgent, SIGNAL(finished()));
+ QVERIFY(spy.isValid());
+ QVERIFY(spy.isEmpty());
+
+ devAgent->start();
+ QTRY_VERIFY_WITH_TIMEOUT(spy.count() > 0, 50000);
+
+ // find first service with descriptor
+ QLowEnergyController *controller = 0;
+ foreach (const QString &remoteDevice, remoteLeDevices.toList()) {
+ controller = new QLowEnergyController(QBluetoothAddress(remoteDevice), this);
+ qDebug() << "Connecting to" << remoteDevice;
+ controller->connectToDevice();
+ QTRY_IMPL(controller->state() != QLowEnergyController::ConnectingState,
+ 10000);
+ if (controller->state() != QLowEnergyController::ConnectedState) {
+ // any error and we skip
+ delete controller;
+ qDebug() << "Skipping device";
+ continue;
+ }
+
+ QSignalSpy discoveryFinishedSpy(controller, SIGNAL(discoveryFinished()));
+ controller->discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+ foreach (const QBluetoothUuid &leServiceUuid, controller->services()) {
+ QLowEnergyService *leService = controller->createServiceObject(leServiceUuid, this);
+ if (!leService)
+ continue;
+
+ leService->discoverDetails();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ leService->state() == QLowEnergyService::ServiceDiscovered, 10000);
+
+ QList<QLowEnergyCharacteristic> chars = leService->characteristics();
+ foreach (const QLowEnergyCharacteristic &ch, chars) {
+ if (!ch.descriptors().isEmpty()) {
+ globalService = leService;
+ globalControl = controller;
+ qWarning() << "Found service with descriptor" << remoteDevice
+ << globalService->serviceName() << globalService->serviceUuid();
+ break;
+ }
+ }
+
+ if (globalControl)
+ break;
+ else
+ delete leService;
+ }
+
+ if (globalControl)
+ break;
+
+ delete controller;
+ }
+
+ if (!globalControl) {
+ qWarning() << "Test limited due to missing remote QLowEnergyDescriptor."
+ << "Please ensure the Bluetooth Low Energy device is advertising its services.";
+ }
+}
+
+void tst_QLowEnergyCharacteristic::cleanupTestCase()
+{
+ if (globalControl)
+ globalControl->disconnectFromDevice();
+}
+
+void tst_QLowEnergyCharacteristic::deviceDiscovered(const QBluetoothDeviceInfo &info)
+{
+ if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
+ remoteLeDevices.insert(info.address().toString());
+}
+
+void tst_QLowEnergyCharacteristic::tst_constructionDefault()
+{
+ QLowEnergyCharacteristic characteristic;
+ QVERIFY(!characteristic.isValid());
+ QCOMPARE(characteristic.value(), QByteArray());
+ QVERIFY(characteristic.uuid().isNull());
+ QVERIFY(characteristic.handle() == 0);
+ QCOMPARE(characteristic.name(), QString());
+ QCOMPARE(characteristic.descriptors().count(), 0);
+ QCOMPARE(characteristic.descriptor(QBluetoothUuid()),
+ QLowEnergyDescriptor());
+ QCOMPARE(characteristic.descriptor(QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration)),
+ QLowEnergyDescriptor());
+ QCOMPARE(characteristic.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration),
+ QLowEnergyDescriptor());
+ QCOMPARE(characteristic.properties(), QLowEnergyCharacteristic::Unknown);
+
+ QLowEnergyCharacteristic copyConstructed(characteristic);
+ QVERIFY(!copyConstructed.isValid());
+ QCOMPARE(copyConstructed.value(), QByteArray());
+ QVERIFY(copyConstructed.uuid().isNull());
+ QVERIFY(copyConstructed.handle() == 0);
+ QCOMPARE(copyConstructed.name(), QString());
+ QCOMPARE(copyConstructed.descriptors().count(), 0);
+ QCOMPARE(copyConstructed.properties(), QLowEnergyCharacteristic::Unknown);
+
+ QVERIFY(copyConstructed == characteristic);
+ QVERIFY(characteristic == copyConstructed);
+ QVERIFY(!(copyConstructed != characteristic));
+ QVERIFY(!(characteristic != copyConstructed));
+
+ QLowEnergyCharacteristic assigned;
+
+ QVERIFY(assigned == characteristic);
+ QVERIFY(characteristic == assigned);
+ QVERIFY(!(assigned != characteristic));
+ QVERIFY(!(characteristic != assigned));
+
+ assigned = characteristic;
+ QVERIFY(!assigned.isValid());
+ QCOMPARE(assigned.value(), QByteArray());
+ QVERIFY(assigned.uuid().isNull());
+ QVERIFY(assigned.handle() == 0);
+ QCOMPARE(assigned.name(), QString());
+ QCOMPARE(assigned.descriptors().count(), 0);
+ QCOMPARE(assigned.properties(), QLowEnergyCharacteristic::Unknown);
+
+ QVERIFY(assigned == characteristic);
+ QVERIFY(characteristic == assigned);
+ QVERIFY(!(assigned != characteristic));
+ QVERIFY(!(characteristic != assigned));
+}
+
+void tst_QLowEnergyCharacteristic::tst_assignCompare()
+{
+ if (!globalService)
+ QSKIP("No characteristic found.");
+
+ QLowEnergyCharacteristic target;
+ QVERIFY(!target.isValid());
+ QCOMPARE(target.value(), QByteArray());
+ QVERIFY(target.uuid().isNull());
+ QVERIFY(target.handle() == 0);
+ QCOMPARE(target.name(), QString());
+ QCOMPARE(target.descriptors().count(), 0);
+ QCOMPARE(target.properties(), QLowEnergyCharacteristic::Unknown);
+
+ int indexWithDescriptor = -1;
+ const QList<QLowEnergyCharacteristic> chars = globalService->characteristics();
+ QVERIFY(!chars.isEmpty());
+ for (int i = 0; i < chars.count(); i++) {
+ const QLowEnergyCharacteristic specific =
+ globalService->characteristic(chars[i].uuid());
+ QVERIFY(specific.isValid());
+ QCOMPARE(specific, chars[i]);
+ if (chars[i].descriptors().count() > 0) {
+ indexWithDescriptor = i;
+ break;
+ }
+ }
+
+ if (chars.isEmpty())
+ QSKIP("No suitable characteristic found despite prior indication.");
+
+ bool noDescriptors = (indexWithDescriptor == -1);
+ if (noDescriptors)
+ indexWithDescriptor = 0; // just choose one
+
+ // test assignment operator
+ target = chars[indexWithDescriptor];
+ QVERIFY(target.isValid());
+ QVERIFY(!target.name().isEmpty());
+ QVERIFY(target.handle() > 0);
+ QVERIFY(!target.uuid().isNull());
+ QVERIFY(target.properties() != QLowEnergyCharacteristic::Unknown);
+ if (target.properties() & QLowEnergyCharacteristic::Read)
+ QVERIFY(!target.value().isEmpty());
+ if (!noDescriptors)
+ QVERIFY(target.descriptors().count() > 0);
+
+ QVERIFY(target == chars[indexWithDescriptor]);
+ QVERIFY(chars[indexWithDescriptor] == target);
+ QVERIFY(!(target != chars[indexWithDescriptor]));
+ QVERIFY(!(chars[indexWithDescriptor] != target));
+
+ QCOMPARE(target.isValid(), chars[indexWithDescriptor].isValid());
+ QCOMPARE(target.name(), chars[indexWithDescriptor].name());
+ QCOMPARE(target.handle(), chars[indexWithDescriptor].handle());
+ QCOMPARE(target.uuid(), chars[indexWithDescriptor].uuid());
+ QCOMPARE(target.value(), chars[indexWithDescriptor].value());
+ QCOMPARE(target.properties(), chars[indexWithDescriptor].properties());
+ QCOMPARE(target.descriptors().count(),
+ chars[indexWithDescriptor].descriptors().count());
+ for (int i = 0; i < target.descriptors().count(); i++) {
+ const QLowEnergyDescriptor ref = chars[indexWithDescriptor].descriptors()[i];
+ QCOMPARE(target.descriptors()[i].name(), ref.name());
+ QCOMPARE(target.descriptors()[i].isValid(), ref.isValid());
+ QCOMPARE(target.descriptors()[i].type(), ref.type());
+ QCOMPARE(target.descriptors()[i].handle(), ref.handle());
+ QCOMPARE(target.descriptors()[i].uuid(), ref.uuid());
+ QCOMPARE(target.descriptors()[i].value(), ref.value());
+
+ const QLowEnergyDescriptor ref2 = chars[indexWithDescriptor].descriptor(ref.uuid());
+ QCOMPARE(ref, ref2);
+ }
+
+ // test copy constructor
+ QLowEnergyCharacteristic copyConstructed(target);
+ QCOMPARE(copyConstructed.isValid(), chars[indexWithDescriptor].isValid());
+ QCOMPARE(copyConstructed.name(), chars[indexWithDescriptor].name());
+ QCOMPARE(copyConstructed.handle(), chars[indexWithDescriptor].handle());
+ QCOMPARE(copyConstructed.uuid(), chars[indexWithDescriptor].uuid());
+ QCOMPARE(copyConstructed.value(), chars[indexWithDescriptor].value());
+ QCOMPARE(copyConstructed.properties(), chars[indexWithDescriptor].properties());
+ QCOMPARE(copyConstructed.descriptors().count(),
+ chars[indexWithDescriptor].descriptors().count());
+
+ QVERIFY(copyConstructed == target);
+ QVERIFY(target == copyConstructed);
+ QVERIFY(!(copyConstructed != target));
+ QVERIFY(!(target != copyConstructed));
+
+ // test invalidation
+ QLowEnergyCharacteristic invalid;
+ target = invalid;
+ QVERIFY(!target.isValid());
+ QCOMPARE(target.value(), QByteArray());
+ QVERIFY(target.uuid().isNull());
+ QVERIFY(target.handle() == 0);
+ QCOMPARE(target.name(), QString());
+ QCOMPARE(target.descriptors().count(), 0);
+ QCOMPARE(target.properties(), QLowEnergyCharacteristic::Unknown);
+
+ QVERIFY(invalid == target);
+ QVERIFY(target == invalid);
+ QVERIFY(!(invalid != target));
+ QVERIFY(!(target != invalid));
+
+ QVERIFY(!(chars[indexWithDescriptor] == target));
+ QVERIFY(!(target == chars[indexWithDescriptor]));
+ QVERIFY(chars[indexWithDescriptor] != target);
+ QVERIFY(target != chars[indexWithDescriptor]);
+
+ if (chars.count() >= 2) {
+ // at least two characteristics
+ QVERIFY(!(chars[0] == chars[1]));
+ QVERIFY(!(chars[1] == chars[0]));
+ QVERIFY(chars[0] != chars[1]);
+ QVERIFY(chars[1] != chars[0]);
+ }
+}
+
+QTEST_MAIN(tst_QLowEnergyCharacteristic)
+
+#include "tst_qlowenergycharacteristic.moc"
diff --git a/tests/auto/qlowenergycontroller/qlowenergycontroller.pro b/tests/auto/qlowenergycontroller/qlowenergycontroller.pro
new file mode 100644
index 00000000..159f27bf
--- /dev/null
+++ b/tests/auto/qlowenergycontroller/qlowenergycontroller.pro
@@ -0,0 +1,6 @@
+QT = core bluetooth testlib
+TARGET = tst_qlowenergycontroller
+CONFIG += testcase
+
+SOURCES += tst_qlowenergycontroller.cpp
+
diff --git a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
new file mode 100644
index 00000000..7b12e8bc
--- /dev/null
+++ b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
@@ -0,0 +1,1807 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QBluetoothLocalDevice>
+#include <QBluetoothDeviceDiscoveryAgent>
+#include <QBluetoothUuid>
+#include <QLowEnergyController>
+#include <QLowEnergyCharacteristic>
+
+#include <QDebug>
+
+/*!
+ This test requires a TI sensor tag with Firmware version: 1.5 (Oct 23 2013).
+ Since revision updates change user strings and even shift handles around
+ other versions than the above are unlikely to succeed. Please update the
+ sensor tag before continuing.
+
+ The TI sensor can be updated using the related iOS app. The Android version
+ doesn't seem to update at this point in time.
+ */
+
+QT_USE_NAMESPACE
+
+class tst_QLowEnergyController : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QLowEnergyController();
+ ~tst_QLowEnergyController();
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void tst_connect();
+ void tst_concurrentDiscovery();
+ void tst_defaultBehavior();
+ void tst_writeCharacteristic();
+ void tst_writeDescriptor();
+
+private:
+ void verifyServiceProperties(const QLowEnergyService *info);
+
+ QBluetoothDeviceDiscoveryAgent *devAgent;
+ QBluetoothAddress remoteDevice;
+ QList<QBluetoothUuid> foundServices;
+};
+
+Q_DECLARE_METATYPE(QLowEnergyCharacteristic)
+Q_DECLARE_METATYPE(QLowEnergyDescriptor)
+Q_DECLARE_METATYPE(QLowEnergyService::ServiceError)
+
+tst_QLowEnergyController::tst_QLowEnergyController()
+{
+ qRegisterMetaType<QLowEnergyCharacteristic>();
+ qRegisterMetaType<QLowEnergyDescriptor>();
+
+ //QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+ const QString remote = qgetenv("BT_TEST_DEVICE");
+ if (!remote.isEmpty()) {
+ remoteDevice = QBluetoothAddress(remote);
+ qWarning() << "Using remote device " << remote << " for testing. Ensure that the device is discoverable for pairing requests";
+ } else {
+ qWarning() << "Not using any remote device for testing. Set BT_TEST_DEVICE env to run manual tests involving a remote device";
+ }
+}
+
+tst_QLowEnergyController::~tst_QLowEnergyController()
+{
+
+}
+
+void tst_QLowEnergyController::initTestCase()
+{
+ if (remoteDevice.isNull()
+ || QBluetoothLocalDevice::allDevices().isEmpty()) {
+ qWarning("No remote device or local adapter found.");
+ return;
+ }
+
+ devAgent = new QBluetoothDeviceDiscoveryAgent(this);
+
+ QSignalSpy finishedSpy(devAgent, SIGNAL(finished()));
+ // there should be no changes yet
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(finishedSpy.isEmpty());
+
+ bool deviceFound = false;
+ devAgent->start();
+ QTRY_VERIFY_WITH_TIMEOUT(finishedSpy.count() > 0, 30000);
+ foreach (const QBluetoothDeviceInfo &info, devAgent->discoveredDevices()) {
+ if (info.address() == remoteDevice) {
+ deviceFound = true;
+ break;
+ }
+ }
+
+ QVERIFY2(deviceFound, "Cannot find remote device.");
+
+ // These are the services exported by the TI SensorTag
+ foundServices << QBluetoothUuid(QString("00001800-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("00001801-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("0000180a-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("0000ffe0-0000-1000-8000-00805f9b34fb"));
+ foundServices << QBluetoothUuid(QString("f000aa00-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa10-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa20-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa30-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa40-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa50-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000aa60-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000ccc0-0451-4000-b000-000000000000"));
+ foundServices << QBluetoothUuid(QString("f000ffc0-0451-4000-b000-000000000000"));
+}
+
+void tst_QLowEnergyController::cleanupTestCase()
+{
+
+}
+
+void tst_QLowEnergyController::tst_connect()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ const QBluetoothAddress localAdapter = localAdapters.at(0).address();
+ QLowEnergyController control(remoteDevice);
+ QSignalSpy connectedSpy(&control, SIGNAL(connected()));
+ QSignalSpy disconnectedSpy(&control, SIGNAL(disconnected()));
+
+ QCOMPARE(control.localAddress(), localAdapter);
+ QVERIFY(!control.localAddress().isNull());
+ QCOMPARE(control.remoteAddress(), remoteDevice);
+ QCOMPARE(control.state(), QLowEnergyController::UnconnectedState);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+ QVERIFY(control.errorString().isEmpty());
+ QCOMPARE(disconnectedSpy.count(), 0);
+ QCOMPARE(connectedSpy.count(), 0);
+ QVERIFY(control.services().isEmpty());
+
+ bool wasError = false;
+ control.connectToDevice();
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 10000);
+
+ QCOMPARE(disconnectedSpy.count(), 0);
+ if (control.error() != QLowEnergyController::NoError) {
+ //error during connect
+ QCOMPARE(connectedSpy.count(), 0);
+ QCOMPARE(control.state(), QLowEnergyController::UnconnectedState);
+ wasError = true;
+ } else if (control.state() == QLowEnergyController::ConnectingState) {
+ //timeout
+ QCOMPARE(connectedSpy.count(), 0);
+ QVERIFY(control.errorString().isEmpty());
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+ QVERIFY(control.services().isEmpty());
+ QSKIP("Connection to LE device cannot be established. Skipping test.");
+ return;
+ } else {
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ QCOMPARE(connectedSpy.count(), 1);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+ QVERIFY(control.errorString().isEmpty());
+ }
+
+ QVERIFY(control.services().isEmpty());
+
+ QList<QLowEnergyService *> savedReferences;
+
+ if (!wasError) {
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ QSignalSpy serviceFoundSpy(&control, SIGNAL(serviceDiscovered(QBluetoothUuid)));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ QVERIFY(!serviceFoundSpy.isEmpty());
+ QVERIFY(serviceFoundSpy.count() >= foundServices.count());
+ QVERIFY(!serviceFoundSpy.isEmpty());
+ QList<QBluetoothUuid> listing;
+ for (int i = 0; i < serviceFoundSpy.count(); i++) {
+ const QVariant v = serviceFoundSpy[i].at(0);
+ listing.append(v.value<QBluetoothUuid>());
+ }
+
+ foreach (const QBluetoothUuid &uuid, foundServices) {
+ QVERIFY2(listing.contains(uuid),
+ uuid.toString().toLatin1());
+
+ QLowEnergyService *service = control.createServiceObject(uuid);
+ QVERIFY2(service, uuid.toString().toLatin1());
+ savedReferences.append(service);
+ QCOMPARE(service->type(), QLowEnergyService::PrimaryService);
+ QCOMPARE(service->state(), QLowEnergyService::DiscoveryRequired);
+ }
+
+ // unrelated uuids don't return valid service object
+ // invalid service uuid
+ QVERIFY(!control.createServiceObject(QBluetoothUuid()));
+ // some random uuid
+ QVERIFY(!control.createServiceObject(QBluetoothUuid(QBluetoothUuid::DeviceName)));
+
+ // initiate characteristic discovery
+ foreach (QLowEnergyService *service, savedReferences) {
+ qDebug() << "Discoverying" << service->serviceUuid();
+ QSignalSpy stateSpy(service,
+ SIGNAL(stateChanged(QLowEnergyService::ServiceState)));
+ QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError)));
+ service->discoverDetails();
+
+ QTRY_VERIFY_WITH_TIMEOUT(
+ service->state() == QLowEnergyService::ServiceDiscovered, 10000);
+
+ QCOMPARE(errorSpy.count(), 0); //no error
+ QCOMPARE(stateSpy.count(), 2); //
+
+ verifyServiceProperties(service);
+ }
+
+ // ensure that related service objects share same state
+ foreach (QLowEnergyService* originalService, savedReferences) {
+ QLowEnergyService *newService = control.createServiceObject(
+ originalService->serviceUuid());
+ QVERIFY(newService);
+ QCOMPARE(newService->state(), QLowEnergyService::ServiceDiscovered);
+ delete newService;
+ }
+ }
+
+ // Finish off
+ control.disconnectFromDevice();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ control.state() == QLowEnergyController::UnconnectedState,
+ 10000);
+
+ if (wasError) {
+ QCOMPARE(disconnectedSpy.count(), 0);
+ } else {
+ QCOMPARE(disconnectedSpy.count(), 1);
+ // after disconnect all service references must be invalid
+ foreach (const QLowEnergyService *entry, savedReferences) {
+ const QBluetoothUuid &uuid = entry->serviceUuid();
+ QVERIFY2(entry->state() == QLowEnergyService::InvalidService,
+ uuid.toString().toLatin1());
+
+ //after disconnect all related characteristics and descriptors are invalid
+ QList<QLowEnergyCharacteristic> chars = entry->characteristics();
+ for (int i = 0; i < chars.count(); i++) {
+ QCOMPARE(chars.at(i).isValid(), false);
+ QList<QLowEnergyDescriptor> descriptors = chars[i].descriptors();
+ for (int j = 0; j < descriptors.count(); j++)
+ QCOMPARE(descriptors[j].isValid(), false);
+ }
+ }
+ }
+
+ qDeleteAll(savedReferences);
+ savedReferences.clear();
+}
+
+void tst_QLowEnergyController::tst_concurrentDiscovery()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ // quick setup - more elaborate test is done by connectNew()
+ QLowEnergyController control(remoteDevice);
+ QCOMPARE(control.state(), QLowEnergyController::UnconnectedState);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ if (control.state() == QLowEnergyController::ConnectingState
+ || control.error() != QLowEnergyController::NoError) {
+ // default BTLE backend forever hangs in ConnectingState
+ QSKIP("Cannot connect to remote device");
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+
+ // 2. new controller to same device fails
+ {
+ QLowEnergyController control2(remoteDevice);
+ control2.connectToDevice();
+ {
+ QTRY_IMPL(control2.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ QVERIFY(control2.error() != QLowEnergyController::NoError);
+ }
+
+ /* We are testing that we can run service discovery on the same device
+ * for multiple services at the same time.
+ * */
+
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ // pick MAX_SERVICES_SAME_TIME_ACCESS services
+ // and discover them at the same time
+#define MAX_SERVICES_SAME_TIME_ACCESS 3
+ QLowEnergyService *services[MAX_SERVICES_SAME_TIME_ACCESS];
+
+ QVERIFY(control.services().count() >= MAX_SERVICES_SAME_TIME_ACCESS);
+
+ QList<QBluetoothUuid> uuids = control.services();
+
+ // initialize services
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ services[i] = control.createServiceObject(uuids.at(i), this);
+ QVERIFY(services[i]);
+ }
+
+ // start complete discovery
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++)
+ services[i]->discoverDetails();
+
+ // wait until discovery done
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ qWarning() << "Waiting for" << i << services[i]->serviceUuid();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ services[i]->state() == QLowEnergyService::ServiceDiscovered,
+ 30000);
+ }
+
+ // verify discovered services
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ verifyServiceProperties(services[i]);
+
+ QVERIFY(!services[i]->contains(QLowEnergyCharacteristic()));
+ QVERIFY(!services[i]->contains(QLowEnergyDescriptor()));
+ }
+
+ control.disconnectFromDevice();
+ QTRY_VERIFY_WITH_TIMEOUT(control.state() == QLowEnergyController::UnconnectedState,
+ 30000);
+ discoveryFinishedSpy.clear();
+
+ // redo the discovery with same controller
+ QLowEnergyService *services_second[MAX_SERVICES_SAME_TIME_ACCESS];
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ // get all details
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ services_second[i] = control.createServiceObject(uuids.at(i), this);
+ QVERIFY(services_second[i]->parent() == this);
+ QVERIFY(services[i]);
+ QVERIFY(services_second[i]->state() == QLowEnergyService::DiscoveryRequired);
+ services_second[i]->discoverDetails();
+ }
+
+ // wait until discovery done
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ qWarning() << "Waiting for" << i << services_second[i]->serviceUuid();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ services_second[i]->state() == QLowEnergyService::ServiceDiscovered,
+ 30000);
+ QCOMPARE(services_second[i]->serviceName(), services[i]->serviceName());
+ QCOMPARE(services_second[i]->serviceUuid(), services[i]->serviceUuid());
+ }
+
+ // verify discovered services (1st and 2nd round)
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ verifyServiceProperties(services_second[i]);
+ //after disconnect all related characteristics and descriptors are invalid
+ const QList<QLowEnergyCharacteristic> chars = services[i]->characteristics();
+ for (int j = 0; j < chars.count(); j++) {
+ QCOMPARE(chars.at(j).isValid(), false);
+ QVERIFY(services[i]->contains(chars[j]));
+ QVERIFY(!services_second[i]->contains(chars[j]));
+ const QList<QLowEnergyDescriptor> descriptors = chars[j].descriptors();
+ for (int k = 0; k < descriptors.count(); k++) {
+ QCOMPARE(descriptors[k].isValid(), false);
+ services[i]->contains(descriptors[k]);
+ QVERIFY(!services_second[i]->contains(chars[j]));
+ }
+ }
+
+ QCOMPARE(services[i]->serviceUuid(), services_second[i]->serviceUuid());
+ QCOMPARE(services[i]->serviceName(), services_second[i]->serviceName());
+ QCOMPARE(services[i]->type(), services_second[i]->type());
+ QVERIFY(services[i]->state() == QLowEnergyService::InvalidService);
+ QVERIFY(services_second[i]->state() == QLowEnergyService::ServiceDiscovered);
+ }
+
+ // cleanup
+ for (int i = 0; i<MAX_SERVICES_SAME_TIME_ACCESS; i++) {
+ delete services[i];
+ delete services_second[i];
+ }
+
+ control.disconnectFromDevice();
+}
+
+void tst_QLowEnergyController::verifyServiceProperties(
+ const QLowEnergyService *info)
+{
+ if (info->serviceUuid() ==
+ QBluetoothUuid(QString("00001800-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying GAP Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 5);
+
+ // Device Name
+ QString temp("00002a00-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x3));
+ QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("544920424c452053656e736f7220546167"));
+ QVERIFY(chars[0].isValid());
+ QCOMPARE(chars[0].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[0]));
+
+ // Appearance
+ temp = QString("00002a01-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x5));
+ QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("0000"));
+ QVERIFY(chars[1].isValid());
+ QCOMPARE(chars[1].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[1]));
+
+ // Peripheral Privacy Flag
+ temp = QString("00002a02-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x7));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[2].isValid());
+ QCOMPARE(chars[2].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[2]));
+
+ // Reconnection Address
+ temp = QString("00002a03-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[3].handle(), QLowEnergyHandle(0x9));
+ //Early firmware version had this characteristic as Read|Write and may fail
+ QCOMPARE(chars[3].properties(), QLowEnergyCharacteristic::Write);
+ if (chars[3].properties() & QLowEnergyCharacteristic::Read)
+ QCOMPARE(chars[3].value(), QByteArray::fromHex("000000000000"));
+ else
+ QCOMPARE(chars[3].value(), QByteArray());
+ QVERIFY(chars[3].isValid());
+ QCOMPARE(chars[3].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[3]));
+
+ // Peripheral Preferred Connection Parameters
+ temp = QString("00002a04-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[4].handle(), QLowEnergyHandle(0xb));
+ QCOMPARE(chars[4].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[4].value(), QByteArray::fromHex("5000a0000000e803"));
+ QVERIFY(chars[4].isValid());
+ QCOMPARE(chars[4].descriptors().count(), 0);
+ QVERIFY(info->contains(chars[4]));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("00001801-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying GATT Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 1);
+
+ // Service Changed
+ QString temp("00002a05-0000-1000-8000-00805f9b34fb");
+ //this should really be readable according to GATT Service spec
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0xe));
+ QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Indicate);
+ QCOMPARE(chars[0].value(), QByteArray());
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 1);
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0xf));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("0000180a-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying Device Information";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 9);
+
+ // System ID
+ QString temp("00002a23-0000-1000-8000-00805f9b34fb");
+ //this should really be readable according to GATT Service spec
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x12));
+ QCOMPARE(chars[0].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("6e41ab0000296abc"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+ QCOMPARE(chars[0].descriptors().count(), 0);
+
+ // Model Number
+ temp = QString("00002a24-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x14));
+ QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Read);
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+ QCOMPARE(chars[1].descriptors().count(), 0);
+
+ // Serial Number
+ temp = QString("00002a25-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x16));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+ QCOMPARE(chars[2].descriptors().count(), 0);
+
+ // Firmware Revision
+ temp = QString("00002a26-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[3].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[3].handle(), QLowEnergyHandle(0x18));
+ QCOMPARE(chars[3].properties(),
+ (QLowEnergyCharacteristic::Read));
+ //FW rev. : 1.5 (Oct 23 2013)
+ // Other revisions will fail here
+ QCOMPARE(chars[3].value(), QByteArray::fromHex("312e3520284f637420323320323031332900"));
+ QVERIFY(chars[3].isValid());
+ QVERIFY(info->contains(chars[3]));
+ QCOMPARE(chars[3].descriptors().count(), 0);
+
+ // Hardware Revision
+ temp = QString("00002a27-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[4].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[4].handle(), QLowEnergyHandle(0x1a));
+ QCOMPARE(chars[4].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[4].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[4].isValid());
+ QVERIFY(info->contains(chars[4]));
+ QCOMPARE(chars[4].descriptors().count(), 0);
+
+ // Software Revision
+ temp = QString("00002a28-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[5].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[5].handle(), QLowEnergyHandle(0x1c));
+ QCOMPARE(chars[5].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[5].value(), QByteArray::fromHex("4e2e412e00"));
+ QVERIFY(chars[5].isValid());
+ QVERIFY(info->contains(chars[5]));
+ QCOMPARE(chars[5].descriptors().count(), 0);
+
+ // Manufacturer Name
+ temp = QString("00002a29-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[6].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[6].handle(), QLowEnergyHandle(0x1e));
+ QCOMPARE(chars[6].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[6].value(), QByteArray::fromHex("546578617320496e737472756d656e747300"));
+ QVERIFY(chars[6].isValid());
+ QVERIFY(info->contains(chars[6]));
+ QCOMPARE(chars[6].descriptors().count(), 0);
+
+ // IEEE
+ temp = QString("00002a2a-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[7].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[7].handle(), QLowEnergyHandle(0x20));
+ QCOMPARE(chars[7].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[7].value(), QByteArray::fromHex("fe006578706572696d656e74616c"));
+ QVERIFY(chars[7].isValid());
+ QVERIFY(info->contains(chars[7]));
+ QCOMPARE(chars[7].descriptors().count(), 0);
+
+ // PnP ID
+ temp = QString("00002a50-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[8].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[8].handle(), QLowEnergyHandle(0x22));
+ QCOMPARE(chars[8].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[8].value(), QByteArray::fromHex("010d0000001001"));
+ QVERIFY(chars[8].isValid());
+ QVERIFY(info->contains(chars[8]));
+ QCOMPARE(chars[8].descriptors().count(), 0);
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa00-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Temperature";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 2);
+
+ // Temp Data
+ QString temp("f000aa01-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x25));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x26));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x27));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("54656d702e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Temp Config
+ temp = QString("f000aa02-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x29));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x2a));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("54656d702e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+
+ //Temp Period (introduced by later firmware versions)
+ if (chars.count() > 2) {
+ temp = QString("f000aa03-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x2c));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x2d));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("54656d702e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ }
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("0000ffe0-0000-1000-8000-00805f9b34fb"))) {
+ qDebug() << "Verifying Simple Keys";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 1);
+
+ // Temp Data
+ QString temp("0000ffe1-0000-1000-8000-00805f9b34fb");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x6b));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray());
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x6c));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x6d));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4b6579205072657373205374617465"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa10-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Accelerometer";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 3);
+
+ // Accel Data
+ QString temp("f000aa11-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x30));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x31));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x32));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("416363656c2e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Accel Config
+ temp = QString("f000aa12-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x34));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+ QCOMPARE(chars[1].descriptors().count(), 1);
+
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x35));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("416363656c2e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ // Accel Period
+ temp = QString("f000aa13-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x37));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64")); // don't change it or set it to 0x64
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x38));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("416363656c2e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa20-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Humidity";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 2); //new firmware has more chars
+
+ // Humidity Data
+ QString temp("f000aa21-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x3b));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x3c));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x3d));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("48756d69642e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Humidity Config
+ temp = QString("f000aa22-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x3f));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x40));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("48756d69642e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ if (chars.count() >= 3) {
+ // New firmware new characteristic
+ // Humidity Period
+ temp = QString("f000aa23-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x42));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x43));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("48756d69642e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ }
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa30-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Magnetometer";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 3);
+
+ // Magnetometer Data
+ QString temp("f000aa31-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x46));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x47));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x48));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4d61676e2e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Magnetometer Config
+ temp = QString("f000aa32-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x4a));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x4b));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("4d61676e2e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ // Magnetometer Period
+ temp = QString("f000aa33-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x4d));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("c8")); // don't change it or set it to 0xc8
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x4e));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("4d61676e2e20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa40-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Pressure";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 3);
+
+ // Pressure Data
+ QString temp("f000aa41-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x51));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("00000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x52));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x53));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4261726f6d2e2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Pressure Config
+ temp = QString("f000aa42-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x55));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x56));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("4261726f6d2e20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ //calibration and period characteristic are swapped, ensure we don't depend on their order
+ QLowEnergyCharacteristic calibration, period;
+ foreach (const QLowEnergyCharacteristic &ch, chars) {
+ //find calibration characteristic
+ if (ch.uuid() == QBluetoothUuid(QString("f000aa43-0451-4000-b000-000000000000")))
+ calibration = ch;
+ else if (ch.uuid() == QBluetoothUuid(QString("f000aa44-0451-4000-b000-000000000000")))
+ period = ch;
+ }
+
+ if (calibration.isValid()) {
+ // Pressure Calibration
+ temp = QString("f000aa43-0451-4000-b000-000000000000");
+ QCOMPARE(calibration.uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(calibration.handle(), QLowEnergyHandle(0x5b));
+ QCOMPARE(calibration.properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(calibration.value(), QByteArray::fromHex("00000000000000000000000000000000")); // don't change it
+ QVERIFY(calibration.isValid());
+ QVERIFY(info->contains(calibration));
+
+ QCOMPARE(calibration.descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(calibration.descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(calibration.descriptors().at(0).handle(), QLowEnergyHandle(0x5c));
+ QCOMPARE(calibration.descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(calibration.descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(calibration.descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(calibration.descriptors().at(0)));
+
+ QCOMPARE(calibration.descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(calibration.descriptors().at(1).handle(), QLowEnergyHandle(0x5d));
+ QCOMPARE(calibration.descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(calibration.descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(calibration.descriptors().at(1).value(),
+ QByteArray::fromHex("4261726f6d2e2043616c6962722e"));
+ QVERIFY(info->contains(calibration.descriptors().at(1)));
+ }
+
+ if (period.isValid()) {
+ // Period Calibration
+ temp = QString("f000aa44-0451-4000-b000-000000000000");
+ QCOMPARE(period.uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(period.handle(), QLowEnergyHandle(0x58));
+ QCOMPARE(period.properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(period.value(), QByteArray::fromHex("64"));
+ QVERIFY(period.isValid());
+ QVERIFY(info->contains(period));
+
+ QCOMPARE(period.descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(period.descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(period.descriptors().at(0).handle(), QLowEnergyHandle(0x59));
+ QCOMPARE(period.descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(period.descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(period.descriptors().at(0).value(),
+ QByteArray::fromHex("4261726f6d2e20506572696f64"));
+ QVERIFY(info->contains(period.descriptors().at(0)));
+ }
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa50-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Gyroscope";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QVERIFY(chars.count() >= 2);
+
+ // Gyroscope Data
+ QString temp("f000aa51-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x60));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Notify));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x61));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0000"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x62));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("4779726f2044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // Gyroscope Config
+ temp = QString("f000aa52-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x64));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x65));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("4779726f20436f6e662e"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ // Gyroscope Period
+ temp = QString("f000aa53-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x67));
+ QCOMPARE(chars[2].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[2].value(), QByteArray::fromHex("64"));
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x68));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("4779726f20506572696f64"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000aa60-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying Test Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 2);
+
+ // Test Data
+ QString temp("f000aa61-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x70));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("3f00"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 1);
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x71));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(0).value(),
+ QByteArray::fromHex("546573742044617461"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ // Test Config
+ temp = QString("f000aa62-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x73));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Read|QLowEnergyCharacteristic::Write));
+ QCOMPARE(chars[1].value(), QByteArray::fromHex("00"));
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x74));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("5465737420436f6e666967"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000ccc0-0451-4000-b000-000000000000"))) {
+ qDebug() << "Connection Control Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 3);
+
+ //first characteristic
+ QString temp("f000ccc1-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x77));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Read));
+ QCOMPARE(chars[0].value(), QByteArray::fromHex("000000000000"));
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x78));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0100"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x79));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("436f6e6e2e20506172616d73"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ //second characteristic
+ temp = QString("f000ccc2-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x7b));
+ QCOMPARE(chars[1].properties(), QLowEnergyCharacteristic::Write);
+ QCOMPARE(chars[1].value(), QByteArray());
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 1);
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x7c));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(0).value(),
+ QByteArray::fromHex("436f6e6e2e20506172616d7320526571"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ //third characteristic
+ temp = QString("f000ccc3-0451-4000-b000-000000000000");
+ QCOMPARE(chars[2].uuid(), QBluetoothUuid(temp));
+ QCOMPARE(chars[2].handle(), QLowEnergyHandle(0x7e));
+ QCOMPARE(chars[2].properties(), QLowEnergyCharacteristic::Write);
+ QCOMPARE(chars[2].value(), QByteArray());
+ QVERIFY(chars[2].isValid());
+ QVERIFY(info->contains(chars[2]));
+
+ QCOMPARE(chars[2].descriptors().count(), 1);
+ QCOMPARE(chars[2].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[2].descriptors().at(0).handle(), QLowEnergyHandle(0x7f));
+ QCOMPARE(chars[2].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[2].descriptors().at(0).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[2].descriptors().at(0).value(),
+ QByteArray::fromHex("446973636f6e6e65637420526571"));
+ QVERIFY(info->contains(chars[2].descriptors().at(0)));
+ } else if (info->serviceUuid() ==
+ QBluetoothUuid(QString("f000ffc0-0451-4000-b000-000000000000"))) {
+ qDebug() << "Verifying OID Service";
+ QList<QLowEnergyCharacteristic> chars = info->characteristics();
+ QCOMPARE(chars.count(), 2);
+
+ // first characteristic
+ QString temp("f000ffc1-0451-4000-b000-000000000000");
+ QCOMPARE(chars[0].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].handle(), QLowEnergyHandle(0x82));
+ QCOMPARE(chars[0].properties(),
+ (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse));
+ QCOMPARE(chars[0].value(), QByteArray());
+ QVERIFY(chars[0].isValid());
+ QVERIFY(info->contains(chars[0]));
+
+ QCOMPARE(chars[0].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[0].descriptors().at(0).isValid(), true);
+ QCOMPARE(chars[0].descriptors().at(0).handle(), QLowEnergyHandle(0x83));
+ QCOMPARE(chars[0].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[0].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(0).value(), QByteArray::fromHex("0100"));
+ QVERIFY(info->contains(chars[0].descriptors().at(0)));
+
+ QCOMPARE(chars[0].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[0].descriptors().at(1).handle(), QLowEnergyHandle(0x84));
+ QCOMPARE(chars[0].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[0].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[0].descriptors().at(1).value(),
+ QByteArray::fromHex("496d67204964656e74696679"));
+ QVERIFY(info->contains(chars[0].descriptors().at(1)));
+
+ // second characteristic
+ temp = QString("f000ffc2-0451-4000-b000-000000000000");
+ QCOMPARE(chars[1].uuid(), QBluetoothUuid(temp));
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].handle(), QLowEnergyHandle(0x86));
+ QCOMPARE(chars[1].properties(),
+ (QLowEnergyCharacteristic::Notify|QLowEnergyCharacteristic::Write|QLowEnergyCharacteristic::WriteNoResponse));
+ QCOMPARE(chars[1].value(), QByteArray());
+ QVERIFY(chars[1].isValid());
+ QVERIFY(info->contains(chars[1]));
+
+ QCOMPARE(chars[1].descriptors().count(), 2);
+ //descriptor checks
+ QCOMPARE(chars[1].descriptors().at(0).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).handle(), QLowEnergyHandle(0x87));
+ QCOMPARE(chars[1].descriptors().at(0).uuid(),
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+ QCOMPARE(chars[1].descriptors().at(0).type(),
+ QBluetoothUuid::ClientCharacteristicConfiguration);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(0).value(), QByteArray::fromHex("0100"));
+ QVERIFY(info->contains(chars[1].descriptors().at(0)));
+
+ QCOMPARE(chars[1].descriptors().at(1).isValid(), true);
+ // value different in other revisions and test may fail
+ QCOMPARE(chars[1].descriptors().at(1).handle(), QLowEnergyHandle(0x88));
+ QCOMPARE(chars[1].descriptors().at(1).uuid(),
+ QBluetoothUuid(QBluetoothUuid::CharacteristicUserDescription));
+ QCOMPARE(chars[1].descriptors().at(1).type(),
+ QBluetoothUuid::CharacteristicUserDescription);
+ QCOMPARE(chars[1].descriptors().at(1).value(),
+ QByteArray::fromHex("496d6720426c6f636b"));
+ QVERIFY(info->contains(chars[1].descriptors().at(1)));
+ } else {
+ QFAIL(QString("Service not found" + info->serviceUuid().toString()).toUtf8().constData());
+ }
+}
+
+void tst_QLowEnergyController::tst_defaultBehavior()
+{
+ QList<QBluetoothAddress> foundAddresses;
+ foreach (const QBluetoothHostInfo &info, QBluetoothLocalDevice::allDevices())
+ foundAddresses.append(info.address());
+ const QBluetoothAddress randomAddress("11:22:33:44:55:66");
+
+ // Test automatic detection of local adapter
+ QLowEnergyController controlDefaultAdapter(randomAddress);
+ QCOMPARE(controlDefaultAdapter.remoteAddress(), randomAddress);
+ QCOMPARE(controlDefaultAdapter.state(), QLowEnergyController::UnconnectedState);
+ if (foundAddresses.isEmpty()) {
+ QVERIFY(controlDefaultAdapter.localAddress().isNull());
+ } else {
+ QCOMPARE(controlDefaultAdapter.error(), QLowEnergyController::NoError);
+ QVERIFY(controlDefaultAdapter.errorString().isEmpty());
+ QVERIFY(foundAddresses.contains(controlDefaultAdapter.localAddress()));
+
+ // unrelated uuids don't return valid service object
+ // invalid service uuid
+ QVERIFY(!controlDefaultAdapter.createServiceObject(
+ QBluetoothUuid()));
+ // some random uuid
+ QVERIFY(!controlDefaultAdapter.createServiceObject(
+ QBluetoothUuid(QBluetoothUuid::DeviceName)));
+ }
+
+ QCOMPARE(controlDefaultAdapter.services().count(), 0);
+
+ // Test explicit local adapter
+ if (!foundAddresses.isEmpty()) {
+ QLowEnergyController controlExplicitAdapter(randomAddress,
+ foundAddresses[0]);
+ QCOMPARE(controlExplicitAdapter.remoteAddress(), randomAddress);
+ QCOMPARE(controlExplicitAdapter.localAddress(), foundAddresses[0]);
+ QCOMPARE(controlExplicitAdapter.state(),
+ QLowEnergyController::UnconnectedState);
+ QCOMPARE(controlExplicitAdapter.services().count(), 0);
+
+ // unrelated uuids don't return valid service object
+ // invalid service uuid
+ QVERIFY(!controlExplicitAdapter.createServiceObject(
+ QBluetoothUuid()));
+ // some random uuid
+ QVERIFY(!controlExplicitAdapter.createServiceObject(
+ QBluetoothUuid(QBluetoothUuid::DeviceName)));
+ }
+}
+
+void tst_QLowEnergyController::tst_writeCharacteristic()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ // quick setup - more elaborate test is done by connect()
+ QLowEnergyController control(remoteDevice);
+ QCOMPARE(control.error(), QLowEnergyController::NoError);
+
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ if (control.state() == QLowEnergyController::ConnectingState
+ || control.error() != QLowEnergyController::NoError) {
+ // default BTLE backend forever hangs in ConnectingState
+ QSKIP("Cannot connect to remote device");
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ const QBluetoothUuid testService(QString("f000aa60-0451-4000-b000-000000000000"));
+ QList<QBluetoothUuid> uuids = control.services();
+ QVERIFY(uuids.contains(testService));
+
+ QLowEnergyService *service = control.createServiceObject(testService, this);
+ QVERIFY(service);
+ service->discoverDetails();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ service->state() == QLowEnergyService::ServiceDiscovered, 30000);
+
+ //test service described by http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
+ const QList<QLowEnergyCharacteristic> chars = service->characteristics();
+
+ QLowEnergyCharacteristic dataChar;
+ QLowEnergyCharacteristic configChar;
+ for (int i = 0; i < chars.count(); i++) {
+ if (chars[i].uuid() == QBluetoothUuid(QString("f000aa61-0451-4000-b000-000000000000")))
+ dataChar = chars[i];
+ else if (chars[i].uuid() == QBluetoothUuid(QString("f000aa62-0451-4000-b000-000000000000")))
+ configChar = chars[i];
+ }
+
+ QVERIFY(dataChar.isValid());
+ QVERIFY(!(dataChar.properties() & ~QLowEnergyCharacteristic::Read)); // only a read char
+ QVERIFY(service->contains(dataChar));
+ QVERIFY(configChar.isValid());
+ QVERIFY(configChar.properties() & QLowEnergyCharacteristic::Write);
+ QVERIFY(service->contains(configChar));
+
+ QCOMPARE(dataChar.value(), QByteArray::fromHex("3f00"));
+ QVERIFY(configChar.value() == QByteArray::fromHex("00")
+ || configChar.value() == QByteArray::fromHex("81"));
+
+ QSignalSpy writeSpy(service,
+ SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
+
+ // *******************************************
+ // test writing of characteristic
+ // enable Blinking LED if not already enabled
+ if (configChar.value() != QByteArray("81")) {
+ service->writeCharacteristic(configChar, QByteArray::fromHex("81")); //0x81 blink LED D1
+ QTRY_VERIFY_WITH_TIMEOUT(!writeSpy.isEmpty(), 10000);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("81"));
+ QList<QVariant> firstSignalData = writeSpy.first();
+ QLowEnergyCharacteristic signalChar = firstSignalData[0].value<QLowEnergyCharacteristic>();
+ QByteArray signalValue = firstSignalData[1].toByteArray();
+
+ QCOMPARE(signalValue, QByteArray::fromHex("81"));
+ QVERIFY(signalChar == configChar);
+
+ writeSpy.clear();
+ }
+
+ service->writeCharacteristic(configChar, QByteArray::fromHex("00")); //0x81 blink LED D1
+ QTRY_VERIFY_WITH_TIMEOUT(!writeSpy.isEmpty(), 10000);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("00"));
+ QList<QVariant> firstSignalData = writeSpy.first();
+ QLowEnergyCharacteristic signalChar = firstSignalData[0].value<QLowEnergyCharacteristic>();
+ QByteArray signalValue = firstSignalData[1].toByteArray();
+
+ QCOMPARE(signalValue, QByteArray::fromHex("00"));
+ QVERIFY(signalChar == configChar);
+
+ // *******************************************
+ // write wrong value -> error response required
+ QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError)));
+ writeSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(writeSpy.count(), 0);
+
+ // write 2 byte value to 1 byte characteristic
+ service->writeCharacteristic(configChar, QByteArray::fromHex("1111"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::CharacteristicWriteError);
+ QCOMPARE(service->error(), QLowEnergyService::CharacteristicWriteError);
+ QCOMPARE(writeSpy.count(), 0);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("00"));
+
+ // *******************************************
+ // write to read-only characteristic -> error
+ errorSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ service->writeCharacteristic(dataChar, QByteArray::fromHex("ffff"));
+
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 10000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::OperationError);
+ QCOMPARE(service->error(), QLowEnergyService::OperationError);
+ QCOMPARE(writeSpy.count(), 0);
+ QCOMPARE(dataChar.value(), QByteArray::fromHex("3f00"));
+
+
+ control.disconnectFromDevice();
+
+ // *******************************************
+ // write value while disconnected -> error
+ errorSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ service->writeCharacteristic(configChar, QByteArray::fromHex("ffff"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 2000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::OperationError);
+ QCOMPARE(service->error(), QLowEnergyService::OperationError);
+ QCOMPARE(writeSpy.count(), 0);
+ QCOMPARE(configChar.value(), QByteArray::fromHex("00"));
+
+ // invalid characteristics still belong to their respective service
+ QVERIFY(service->contains(configChar));
+ QVERIFY(service->contains(dataChar));
+
+ QVERIFY(!service->contains(QLowEnergyCharacteristic()));
+
+ delete service;
+}
+
+void tst_QLowEnergyController::tst_writeDescriptor()
+{
+ QList<QBluetoothHostInfo> localAdapters = QBluetoothLocalDevice::allDevices();
+ if (localAdapters.isEmpty() || remoteDevice.isNull())
+ QSKIP("No local Bluetooth or remote BTLE device found. Skipping test.");
+
+ // quick setup - more elaborate test is done by connect()
+ QLowEnergyController control(remoteDevice);
+ control.connectToDevice();
+ {
+ QTRY_IMPL(control.state() != QLowEnergyController::ConnectingState,
+ 30000);
+ }
+
+ if (control.state() == QLowEnergyController::ConnectingState
+ || control.error() != QLowEnergyController::NoError) {
+ // default BTLE backend forever hangs in ConnectingState
+ QSKIP("Cannot connect to remote device");
+ }
+
+ QCOMPARE(control.state(), QLowEnergyController::ConnectedState);
+ QSignalSpy discoveryFinishedSpy(&control, SIGNAL(discoveryFinished()));
+ control.discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+
+ const QBluetoothUuid testService(QString("f000aa00-0451-4000-b000-000000000000"));
+ QList<QBluetoothUuid> uuids = control.services();
+ QVERIFY(uuids.contains(testService));
+
+ QLowEnergyService *service = control.createServiceObject(testService, this);
+ QVERIFY(service);
+ service->discoverDetails();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ service->state() == QLowEnergyService::ServiceDiscovered, 30000);
+
+ // Temperature service described by
+ // http://processors.wiki.ti.com/index.php/SensorTag_User_Guide
+
+ // 1. Find temperature data characteristic
+ const QLowEnergyCharacteristic tempData = service->characteristic(
+ QBluetoothUuid(QStringLiteral("f000aa01-0451-4000-b000-000000000000")));
+ const QLowEnergyCharacteristic tempConfig = service->characteristic(
+ QBluetoothUuid(QStringLiteral("f000aa02-0451-4000-b000-000000000000")));
+
+ if (!tempData.isValid()) {
+ delete service;
+ control.disconnectFromDevice();
+ QSKIP("Cannot find temperature data characteristic of TI Sensor");
+ }
+
+ // 2. Find temperature data notification descriptor
+ const QLowEnergyDescriptor notification = tempData.descriptor(
+ QBluetoothUuid(QBluetoothUuid::ClientCharacteristicConfiguration));
+
+ if (!notification.isValid()) {
+ delete service;
+ control.disconnectFromDevice();
+ QSKIP("Cannot find temperature data notification of TI Sensor");
+ }
+
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+ service->contains(notification);
+ service->contains(tempData);
+ if (tempConfig.isValid()) {
+ service->contains(tempConfig);
+ QCOMPARE(tempConfig.value(), QByteArray::fromHex("00"));
+ }
+
+ // 3. Test writing to descriptor -> activate notifications
+ QSignalSpy descChangedSpy(service,
+ SIGNAL(descriptorChanged(QLowEnergyDescriptor,QByteArray)));
+ QSignalSpy charChangedSpy(service,
+ SIGNAL(characteristicChanged(QLowEnergyCharacteristic,QByteArray)));
+ service->writeDescriptor(notification, QByteArray::fromHex("0100"));
+ // verify
+ QTRY_VERIFY_WITH_TIMEOUT(!descChangedSpy.isEmpty(), 3000);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0100"));
+ QList<QVariant> firstSignalData = descChangedSpy.first();
+ QLowEnergyDescriptor signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>();
+ QByteArray signalValue = firstSignalData[1].toByteArray();
+ QCOMPARE(signalValue, QByteArray::fromHex("0100"));
+ QVERIFY(notification == signalDesc);
+ descChangedSpy.clear();
+
+ // 4. Test reception of notifications
+ // activate the temperature sensor if available
+ if (tempConfig.isValid()) {
+ service->writeCharacteristic(tempConfig, QByteArray::fromHex("01"));
+
+ // first signal is confirmation of tempConfig write
+ // subsequent signals are temp data updates
+ QTRY_VERIFY_WITH_TIMEOUT(charChangedSpy.count() >= 5, 10000);
+ QList<QVariant> entry;
+ for (int i = 0; i < charChangedSpy.count(); i++) {
+ entry = charChangedSpy[i];
+ const QLowEnergyCharacteristic ch = entry[0].value<QLowEnergyCharacteristic>();
+ const QByteArray val = entry[1].toByteArray();
+
+ if (i == 0) {
+ QCOMPARE(tempConfig, ch);
+ } else {
+ qDebug() << "Temp update: " << hex << ch.handle() << val.toHex();
+ QCOMPARE(tempData, ch);
+ }
+ }
+
+ service->writeCharacteristic(tempConfig, QByteArray::fromHex("00"));
+ }
+
+ // 5. Test writing to descriptor -> deactivate notifications
+ service->writeDescriptor(notification, QByteArray::fromHex("0000"));
+ // verify
+ QTRY_VERIFY_WITH_TIMEOUT(!descChangedSpy.isEmpty(), 3000);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+ firstSignalData = descChangedSpy.first();
+ signalDesc = firstSignalData[0].value<QLowEnergyDescriptor>();
+ signalValue = firstSignalData[1].toByteArray();
+ QCOMPARE(signalValue, QByteArray::fromHex("0000"));
+ QVERIFY(notification == signalDesc);
+ descChangedSpy.clear();
+
+ // *******************************************
+ // write wrong value -> error response required
+ QSignalSpy errorSpy(service, SIGNAL(error(QLowEnergyService::ServiceError)));
+ descChangedSpy.clear();
+ QCOMPARE(errorSpy.count(), 0);
+ QCOMPARE(descChangedSpy.count(), 0);
+
+ // write 4 byte value to 2 byte characteristic
+ service->writeDescriptor(notification, QByteArray::fromHex("11112222"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 30000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::DescriptorWriteError);
+ QCOMPARE(service->error(), QLowEnergyService::DescriptorWriteError);
+ QCOMPARE(descChangedSpy.count(), 0);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+
+ control.disconnectFromDevice();
+
+ // *******************************************
+ // write value while disconnected -> error
+ errorSpy.clear();
+ service->writeDescriptor(notification, QByteArray::fromHex("0100"));
+ QTRY_VERIFY_WITH_TIMEOUT(!errorSpy.isEmpty(), 2000);
+ QCOMPARE(errorSpy[0].at(0).value<QLowEnergyService::ServiceError>(),
+ QLowEnergyService::OperationError);
+ QCOMPARE(service->error(), QLowEnergyService::OperationError);
+ QCOMPARE(descChangedSpy.count(), 0);
+ QCOMPARE(notification.value(), QByteArray::fromHex("0000"));
+
+ delete service;
+}
+
+QTEST_MAIN(tst_QLowEnergyController)
+
+#include "tst_qlowenergycontroller.moc"
diff --git a/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro b/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro
new file mode 100644
index 00000000..60b5a740
--- /dev/null
+++ b/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro
@@ -0,0 +1,8 @@
+SOURCES += tst_qlowenergydescriptor.cpp
+TARGET = tst_qlowenergydescriptor
+CONFIG += testcase
+
+QT = core bluetooth testlib
+blackberry {
+ LIBS += -lbtapi
+}
diff --git a/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
new file mode 100644
index 00000000..6de47458
--- /dev/null
+++ b/tests/auto/qlowenergydescriptor/tst_qlowenergydescriptor.cpp
@@ -0,0 +1,326 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QUuid>
+
+#include <QDebug>
+
+#include <QBluetoothDeviceDiscoveryAgent>
+#include <QLowEnergyDescriptor>
+#include <QLowEnergyController>
+#include <QBluetoothLocalDevice>
+
+QT_USE_NAMESPACE
+
+class tst_QLowEnergyDescriptor : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QLowEnergyDescriptor();
+ ~tst_QLowEnergyDescriptor();
+
+protected slots:
+ void deviceDiscovered(const QBluetoothDeviceInfo &info);
+
+private slots:
+ void initTestCase();
+ void cleanupTestCase();
+ void tst_constructionDefault();
+ void tst_assignCompare();
+
+private:
+ QSet<QString> remoteLeDevices;
+ QLowEnergyController *globalControl;
+ QLowEnergyService *globalService;
+};
+
+tst_QLowEnergyDescriptor::tst_QLowEnergyDescriptor() :
+ globalControl(0), globalService(0)
+{
+ QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true"));
+}
+
+tst_QLowEnergyDescriptor::~tst_QLowEnergyDescriptor()
+{
+}
+
+void tst_QLowEnergyDescriptor::initTestCase()
+{
+ if (QBluetoothLocalDevice::allDevices().isEmpty()) {
+ qWarning("No remote device discovered.");
+
+ return;
+ }
+
+ // start Bluetooth if not started
+ QBluetoothLocalDevice device;
+ device.powerOn();
+
+ // find an arbitrary low energy device in vincinity
+ // find an arbitrary service with descriptor
+
+ QBluetoothDeviceDiscoveryAgent *devAgent = new QBluetoothDeviceDiscoveryAgent(this);
+ connect(devAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
+ this, SLOT(deviceDiscovered(QBluetoothDeviceInfo)));
+
+ QSignalSpy errorSpy(devAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)));
+ QVERIFY(errorSpy.isValid());
+ QVERIFY(errorSpy.isEmpty());
+
+ QSignalSpy spy(devAgent, SIGNAL(finished()));
+ // there should be no changes yet
+ QVERIFY(spy.isValid());
+ QVERIFY(spy.isEmpty());
+
+ devAgent->start();
+ QTRY_VERIFY_WITH_TIMEOUT(spy.count() > 0, 50000);
+
+ // find first service with descriptor
+ QLowEnergyController *controller = 0;
+ foreach (const QString &remoteDevice, remoteLeDevices.toList()) {
+ controller = new QLowEnergyController(QBluetoothAddress(remoteDevice), this);
+ qDebug() << "Connecting to" << remoteDevice;
+ controller->connectToDevice();
+ QTRY_IMPL(controller->state() != QLowEnergyController::ConnectingState,
+ 10000);
+ if (controller->state() != QLowEnergyController::ConnectedState) {
+ // any error and we skip
+ delete controller;
+ qDebug() << "Skipping device";
+ continue;
+ }
+
+ QSignalSpy discoveryFinishedSpy(controller, SIGNAL(discoveryFinished()));
+ controller->discoverServices();
+ QTRY_VERIFY_WITH_TIMEOUT(discoveryFinishedSpy.count() == 1, 10000);
+ foreach (const QBluetoothUuid &leServiceUuid, controller->services()) {
+ QLowEnergyService *leService = controller->createServiceObject(leServiceUuid, this);
+ if (!leService)
+ continue;
+
+ leService->discoverDetails();
+ QTRY_VERIFY_WITH_TIMEOUT(
+ leService->state() == QLowEnergyService::ServiceDiscovered, 10000);
+
+ QList<QLowEnergyCharacteristic> chars = leService->characteristics();
+ foreach (const QLowEnergyCharacteristic &ch, chars) {
+ if (!ch.descriptors().isEmpty()) {
+ globalService = leService;
+ globalControl = controller;
+ qWarning() << "Found service with descriptor" << remoteDevice
+ << globalService->serviceName() << globalService->serviceUuid();
+ break;
+ }
+ }
+
+ if (globalControl)
+ break;
+ else
+ delete leService;
+ }
+
+ if (globalControl)
+ break;
+
+ delete controller;
+ }
+
+ if (!globalControl) {
+ qWarning() << "Test limited due to missing remote QLowEnergyDescriptor."
+ << "Please ensure the Bluetooth Low Energy device is advertising its services.";
+ }
+}
+
+void tst_QLowEnergyDescriptor::cleanupTestCase()
+{
+ if (globalControl)
+ globalControl->disconnectFromDevice();
+}
+
+void tst_QLowEnergyDescriptor::deviceDiscovered(const QBluetoothDeviceInfo &info)
+{
+ if (info.coreConfigurations() & QBluetoothDeviceInfo::LowEnergyCoreConfiguration)
+ remoteLeDevices.insert(info.address().toString());
+}
+
+void tst_QLowEnergyDescriptor::tst_constructionDefault()
+{
+ QLowEnergyDescriptor descriptor;
+ QVERIFY(!descriptor.isValid());
+ QCOMPARE(descriptor.value(), QByteArray());
+ QVERIFY(descriptor.uuid().isNull());
+ QVERIFY(descriptor.handle() == 0);
+ QCOMPARE(descriptor.name(), QString());
+ QCOMPARE(descriptor.type(), QBluetoothUuid::UnknownDescriptorType);
+
+ QLowEnergyDescriptor copyConstructed(descriptor);
+ QVERIFY(!copyConstructed.isValid());
+ QCOMPARE(copyConstructed.value(), QByteArray());
+ QVERIFY(copyConstructed.uuid().isNull());
+ QVERIFY(copyConstructed.handle() == 0);
+ QCOMPARE(copyConstructed.name(), QString());
+ QCOMPARE(copyConstructed.type(), QBluetoothUuid::UnknownDescriptorType);
+
+ QVERIFY(copyConstructed == descriptor);
+ QVERIFY(descriptor == copyConstructed);
+ QVERIFY(!(copyConstructed != descriptor));
+ QVERIFY(!(descriptor != copyConstructed));
+
+ QLowEnergyDescriptor assigned;
+
+ QVERIFY(assigned == descriptor);
+ QVERIFY(descriptor == assigned);
+ QVERIFY(!(assigned != descriptor));
+ QVERIFY(!(descriptor != assigned));
+
+ assigned = descriptor;
+ QVERIFY(!assigned.isValid());
+ QCOMPARE(assigned.value(), QByteArray());
+ QVERIFY(assigned.uuid().isNull());
+ QVERIFY(assigned.handle() == 0);
+ QCOMPARE(assigned.name(), QString());
+ QCOMPARE(assigned.type(), QBluetoothUuid::UnknownDescriptorType);
+
+ QVERIFY(assigned == descriptor);
+ QVERIFY(descriptor == assigned);
+ QVERIFY(!(assigned != descriptor));
+ QVERIFY(!(descriptor != assigned));
+}
+
+
+void tst_QLowEnergyDescriptor::tst_assignCompare()
+{
+ //find the descriptor
+ if (!globalService)
+ QSKIP("No descriptor found.");
+
+ QLowEnergyDescriptor target;
+ QVERIFY(!target.isValid());
+ QCOMPARE(target.type(), QBluetoothUuid::UnknownDescriptorType);
+ QCOMPARE(target.name(), QString());
+ QCOMPARE(target.handle(), QLowEnergyHandle(0));
+ QCOMPARE(target.uuid(), QBluetoothUuid());
+ QCOMPARE(target.value(), QByteArray());
+
+ QList<QLowEnergyDescriptor> targets;
+ const QList<QLowEnergyCharacteristic> chars = globalService->characteristics();
+ foreach (const QLowEnergyCharacteristic &ch, chars) {
+ if (!ch.descriptors().isEmpty()) {
+ targets = ch.descriptors();
+ break;
+ }
+ }
+
+ if (targets.isEmpty())
+ QSKIP("No descriptor found despite prior indication.");
+
+ // test assignment operator
+ target = targets.first();
+ QVERIFY(target.isValid());
+ QVERIFY(target.type() != QBluetoothUuid::UnknownDescriptorType);
+ QVERIFY(!target.name().isEmpty());
+ QVERIFY(target.handle() > 0);
+ QVERIFY(!target.uuid().isNull());
+ QVERIFY(!target.value().isEmpty());
+
+ QVERIFY(target == targets.first());
+ QVERIFY(targets.first() == target);
+ QVERIFY(!(target != targets.first()));
+ QVERIFY(!(targets.first() != target));
+
+ QCOMPARE(target.isValid(), targets.first().isValid());
+ QCOMPARE(target.type(), targets.first().type());
+ QCOMPARE(target.name(), targets.first().name());
+ QCOMPARE(target.handle(), targets.first().handle());
+ QCOMPARE(target.uuid(), targets.first().uuid());
+ QCOMPARE(target.value(), targets.first().value());
+
+ // test copy constructor
+ QLowEnergyDescriptor copyConstructed(target);
+ QCOMPARE(copyConstructed.isValid(), targets.first().isValid());
+ QCOMPARE(copyConstructed.type(), targets.first().type());
+ QCOMPARE(copyConstructed.name(), targets.first().name());
+ QCOMPARE(copyConstructed.handle(), targets.first().handle());
+ QCOMPARE(copyConstructed.uuid(), targets.first().uuid());
+ QCOMPARE(copyConstructed.value(), targets.first().value());
+
+ QVERIFY(copyConstructed == target);
+ QVERIFY(target == copyConstructed);
+ QVERIFY(!(copyConstructed != target));
+ QVERIFY(!(target != copyConstructed));
+
+ // test invalidation
+ QLowEnergyDescriptor invalid;
+ target = invalid;
+ QVERIFY(!target.isValid());
+ QCOMPARE(target.value(), QByteArray());
+ QVERIFY(target.uuid().isNull());
+ QVERIFY(target.handle() == 0);
+ QCOMPARE(target.name(), QString());
+ QCOMPARE(target.type(), QBluetoothUuid::UnknownDescriptorType);
+
+ QVERIFY(invalid == target);
+ QVERIFY(target == invalid);
+ QVERIFY(!(invalid != target));
+ QVERIFY(!(target != invalid));
+
+ QVERIFY(!(targets.first() == target));
+ QVERIFY(!(target == targets.first()));
+ QVERIFY(targets.first() != target);
+ QVERIFY(target != targets.first());
+
+ if (targets.count() >= 2) {
+ QLowEnergyDescriptor second = targets[1];
+ // at least two descriptors
+ QVERIFY(!(targets.first() == second));
+ QVERIFY(!(second == targets.first()));
+ QVERIFY(targets.first() != second);
+ QVERIFY(second != targets.first());
+ }
+}
+
+QTEST_MAIN(tst_QLowEnergyDescriptor)
+
+#include "tst_qlowenergydescriptor.moc"
+
diff --git a/tests/auto/qlowenergyserviceinfo/qlowenergyserviceinfo.pro b/tests/auto/qlowenergyserviceinfo/qlowenergyserviceinfo.pro
new file mode 100644
index 00000000..1dd24a97
--- /dev/null
+++ b/tests/auto/qlowenergyserviceinfo/qlowenergyserviceinfo.pro
@@ -0,0 +1,9 @@
+SOURCES += tst_qlowenergyserviceinfo.cpp
+TARGET = tst_qlowenergyserviceinfo
+CONFIG += testcase
+
+QT = core bluetooth testlib
+DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+blackberry {
+ LIBS += -lbtapi
+}
diff --git a/tests/auto/qlowenergyserviceinfo/tst_qlowenergyserviceinfo.cpp b/tests/auto/qlowenergyserviceinfo/tst_qlowenergyserviceinfo.cpp
new file mode 100644
index 00000000..ae9b3bd8
--- /dev/null
+++ b/tests/auto/qlowenergyserviceinfo/tst_qlowenergyserviceinfo.cpp
@@ -0,0 +1,251 @@
+/***************************************************************************
+**
+** Copyright (C) 2013 BlackBerry Limited all rights reserved
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QUuid>
+
+#include <QDebug>
+
+#include <qbluetoothdeviceinfo.h>
+#include <qlowenergyserviceinfo.h>
+#include <qbluetoothaddress.h>
+#include <qbluetoothlocaldevice.h>
+#include <qbluetoothuuid.h>
+
+QT_USE_NAMESPACE
+
+Q_DECLARE_METATYPE(QUuid)
+Q_DECLARE_METATYPE(QBluetoothDeviceInfo::CoreConfiguration)
+Q_DECLARE_METATYPE(QLowEnergyServiceInfo)
+Q_DECLARE_METATYPE(QBluetoothUuid::ServiceClassUuid)
+
+class tst_QLowEnergyServiceInfo : public QObject
+{
+ Q_OBJECT
+
+public:
+ tst_QLowEnergyServiceInfo();
+ ~tst_QLowEnergyServiceInfo();
+
+private slots:
+ void initTestCase();
+ void tst_construction();
+ void tst_assignment_data();
+ void tst_assignment();
+};
+
+tst_QLowEnergyServiceInfo::tst_QLowEnergyServiceInfo()
+{
+}
+
+tst_QLowEnergyServiceInfo::~tst_QLowEnergyServiceInfo()
+{
+}
+
+void tst_QLowEnergyServiceInfo::initTestCase()
+{
+ // start Bluetooth if not started
+ QBluetoothLocalDevice *device = new QBluetoothLocalDevice();
+ device->powerOn();
+ delete device;
+}
+
+void tst_QLowEnergyServiceInfo::tst_construction()
+{
+ const QBluetoothUuid serviceUuid(QBluetoothUuid::HeartRate);
+ const QBluetoothUuid alternateServiceUuid(QBluetoothUuid::BatteryService);
+ const QBluetoothDeviceInfo deviceInfo(QBluetoothAddress("001122334455"), "Test Device", 0);
+ const QBluetoothDeviceInfo alternatedeviceInfo(QBluetoothAddress("554433221100"), "Test Device2", 0);
+
+ {
+ QLowEnergyServiceInfo serviceInfo;
+
+ QVERIFY(!serviceInfo.isValid());
+ QCOMPARE(serviceInfo.serviceName(), QStringLiteral("Unknown Service"));
+ QCOMPARE(serviceInfo.serviceUuid().toString(), QBluetoothUuid().toString());
+ QCOMPARE(serviceInfo.device(), QBluetoothDeviceInfo());
+ }
+
+ {
+ QLowEnergyServiceInfo serviceInfo(serviceUuid);
+ serviceInfo.setDevice(deviceInfo);
+
+ QVERIFY(serviceInfo.isValid());
+
+ QCOMPARE(serviceInfo.serviceUuid().toString(), serviceUuid.toString());
+ QCOMPARE(serviceInfo.device().address(), deviceInfo.address());
+
+ QLowEnergyServiceInfo copyInfo(serviceInfo);
+
+ QVERIFY(copyInfo.isValid());
+
+ QCOMPARE(copyInfo.serviceUuid().toString(), serviceUuid.toString());
+ QCOMPARE(copyInfo.device().address(), deviceInfo.address());
+
+
+ copyInfo = QLowEnergyServiceInfo(alternateServiceUuid);
+ copyInfo.setDevice(alternatedeviceInfo);
+ QCOMPARE(copyInfo.serviceUuid(), alternateServiceUuid);
+
+ QCOMPARE(copyInfo.device().address(), alternatedeviceInfo.address());
+
+ }
+}
+
+void tst_QLowEnergyServiceInfo::tst_assignment_data()
+{
+ QTest::addColumn<QBluetoothAddress>("address");
+ QTest::addColumn<QString>("name");
+ QTest::addColumn<quint32>("classOfDevice");
+ QTest::addColumn<QBluetoothUuid>("serviceClassUuid");
+ QTest::addColumn<QBluetoothDeviceInfo::CoreConfiguration>("coreConfiguration");
+
+ // bits 12-8 Major
+ // bits 7-2 Minor
+ // bits 1-0 0
+
+ QTest::newRow("0x000000 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000000)
+ << QBluetoothUuid(QBluetoothUuid::GenericAccess)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
+ QTest::newRow("0x000100 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000100)
+ << QBluetoothUuid(QBluetoothUuid::GenericAttribute)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
+ QTest::newRow("0x000104 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000104)
+ << QBluetoothUuid(QBluetoothUuid::HeartRate)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
+ QTest::newRow("0x000118 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000118)
+ << QBluetoothUuid(QBluetoothUuid::CyclingSpeedAndCadence)
+ << QBluetoothDeviceInfo::BaseRateAndLowEnergyCoreConfiguration;
+ QTest::newRow("0x000200 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000200)
+ << QBluetoothUuid(QBluetoothUuid::CyclingPower)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
+ QTest::newRow("0x000204 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000204)
+ << QBluetoothUuid(QBluetoothUuid::ScanParameters)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
+ QTest::newRow("0x000214 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000214)
+ << QBluetoothUuid(QBluetoothUuid::DeviceInformation)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
+ QTest::newRow("0x000300 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000300)
+ << QBluetoothUuid(QBluetoothUuid::CurrentTimeService)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
+ QTest::newRow("0x000320 COD") << QBluetoothAddress("000000000000") << "My Bluetooth Device"
+ << quint32(0x000320)
+ << QBluetoothUuid(QBluetoothUuid::LocationAndNavigation)
+ << QBluetoothDeviceInfo::LowEnergyCoreConfiguration;
+}
+
+void tst_QLowEnergyServiceInfo::tst_assignment()
+{
+ QFETCH(QBluetoothAddress, address);
+ QFETCH(QString, name);
+ QFETCH(quint32, classOfDevice);
+ QFETCH(QBluetoothUuid, serviceClassUuid);
+ QFETCH(QBluetoothDeviceInfo::CoreConfiguration, coreConfiguration);
+
+ QBluetoothDeviceInfo deviceInfo(address, name, classOfDevice);
+ deviceInfo.setCoreConfigurations(coreConfiguration);
+ QCOMPARE(deviceInfo.coreConfigurations(), coreConfiguration);
+
+ QLowEnergyServiceInfo serviceInfo(serviceClassUuid);
+ serviceInfo.setDevice(deviceInfo);
+ QCOMPARE(serviceInfo.device(), deviceInfo);
+
+ QVERIFY(serviceInfo.isValid());
+
+ {
+ QLowEnergyServiceInfo copyInfo = serviceInfo;
+
+ QVERIFY(copyInfo.isValid());
+
+ QCOMPARE(copyInfo.device().address(), address);
+ QCOMPARE(copyInfo.serviceUuid(), serviceClassUuid);
+ QCOMPARE(copyInfo.device().coreConfigurations(), coreConfiguration);
+ QCOMPARE(copyInfo.device(), deviceInfo);
+ }
+
+ {
+ QLowEnergyServiceInfo copyInfo;
+
+ QVERIFY(!copyInfo.isValid());
+
+ copyInfo = serviceInfo;
+
+ QVERIFY(copyInfo.isValid());
+
+ QCOMPARE(copyInfo.device().address(), address);
+ QCOMPARE(copyInfo.serviceUuid(), serviceClassUuid);
+ QCOMPARE(copyInfo.device().coreConfigurations(), coreConfiguration);
+ }
+
+ {
+ QLowEnergyServiceInfo copyInfo1;
+ QLowEnergyServiceInfo copyInfo2;
+
+ QVERIFY(!copyInfo1.isValid());
+ QVERIFY(!copyInfo2.isValid());
+
+ copyInfo1 = copyInfo2 = serviceInfo;
+
+ QVERIFY(copyInfo1.isValid());
+ QVERIFY(copyInfo2.isValid());
+
+ QCOMPARE(copyInfo1.device().address(), address);
+ QCOMPARE(copyInfo2.device().address(), address);
+ QCOMPARE(copyInfo1.serviceUuid(), serviceClassUuid);
+ QCOMPARE(copyInfo2.serviceUuid(), serviceClassUuid);
+ QCOMPARE(copyInfo1.device().coreConfigurations(), coreConfiguration);
+ QCOMPARE(copyInfo2.device().coreConfigurations(), coreConfiguration);
+ QCOMPARE(copyInfo1.device(), deviceInfo);
+ QCOMPARE(copyInfo2.device(), deviceInfo);
+ }
+}
+
+QTEST_MAIN(tst_QLowEnergyServiceInfo)
+
+#include "tst_qlowenergyserviceinfo.moc"
diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp
index 3447de1a..6b7027d0 100644
--- a/tests/bttestui/btlocaldevice.cpp
+++ b/tests/bttestui/btlocaldevice.cpp
@@ -80,6 +80,8 @@ BtLocalDevice::BtLocalDevice(QObject *parent) :
serviceAgent = new QBluetoothServiceDiscoveryAgent(this);
connect(serviceAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)),
this, SLOT(serviceDiscovered(QBluetoothServiceInfo)));
+ connect(serviceAgent, SIGNAL(serviceDiscovered(QLowEnergyServiceInfo)),
+ this, SLOT(leServiceDiscovered(QLowEnergyServiceInfo)));
connect(serviceAgent, SIGNAL(finished()),
this, SLOT(serviceDiscoveryFinished()));
connect(serviceAgent, SIGNAL(canceled()),
@@ -303,11 +305,11 @@ void BtLocalDevice::stopServiceDiscovery()
void BtLocalDevice::serviceDiscovered(const QBluetoothServiceInfo &info)
{
- QString classIds;
- foreach (const QBluetoothUuid uuid, info.serviceClassUuids())
- classIds += uuid.toString() + QLatin1Char(' ');
+ QStringList classIds;
+ foreach (const QBluetoothUuid &uuid, info.serviceClassUuids())
+ classIds.append(uuid.toString());
qDebug() << "$$ Found new service" << info.device().address().toString()
- << info.serviceUuid() << info.serviceName() << classIds;
+ << info.serviceUuid() << info.serviceName() << info.serviceDescription() << classIds;
if (info.serviceUuid() == QBluetoothUuid(QString(TEST_SERVICE_UUID))
|| info.serviceClassUuids().contains(QBluetoothUuid(QString(TEST_SERVICE_UUID))))
@@ -328,6 +330,12 @@ void BtLocalDevice::serviceDiscovered(const QBluetoothServiceInfo &info)
}
}
+void BtLocalDevice::leServiceDiscovered(const QLowEnergyServiceInfo &info)
+{
+ qDebug() << "$$ Found new BTLE service" << info.device().address().toString()
+ << info.serviceUuid() << info.serviceName();
+}
+
void BtLocalDevice::serviceDiscoveryFinished()
{
qDebug() << "###### Service Discovery Finished";
diff --git a/tests/bttestui/btlocaldevice.h b/tests/bttestui/btlocaldevice.h
index 01d9b0df..a0c020cc 100644
--- a/tests/bttestui/btlocaldevice.h
+++ b/tests/bttestui/btlocaldevice.h
@@ -90,6 +90,7 @@ public slots:
void startTargettedServiceDiscovery();
void stopServiceDiscovery();
void serviceDiscovered(const QBluetoothServiceInfo &info);
+ void leServiceDiscovered(const QLowEnergyServiceInfo &leInfo);
void serviceDiscoveryFinished();
void serviceDiscoveryCanceled();
void serviceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error error);