From b4bde05d8459cc57c25bac3f7be19fcf03908606 Mon Sep 17 00:00:00 2001 From: Alex Blasche Date: Wed, 12 Feb 2014 16:31:07 +0100 Subject: Add extensive test application for QtBluetooth This application can be used to manually test most Bluetooth features. Change-Id: Ib7101ff2f5334026c67c2e00a852d6b10d4674e8 Reviewed-by: Fabian Bumberger --- tests/bttestui/Button.qml | 66 ++++ tests/bttestui/README | 21 ++ tests/bttestui/btlocaldevice.cpp | 710 +++++++++++++++++++++++++++++++++++++++ tests/bttestui/btlocaldevice.h | 135 ++++++++ tests/bttestui/bttest.qrc | 6 + tests/bttestui/bttestui.pro | 16 + tests/bttestui/main.cpp | 67 ++++ tests/bttestui/main.qml | 262 +++++++++++++++ 8 files changed, 1283 insertions(+) create mode 100644 tests/bttestui/Button.qml create mode 100644 tests/bttestui/README create mode 100644 tests/bttestui/btlocaldevice.cpp create mode 100644 tests/bttestui/btlocaldevice.h create mode 100644 tests/bttestui/bttest.qrc create mode 100644 tests/bttestui/bttestui.pro create mode 100644 tests/bttestui/main.cpp create mode 100644 tests/bttestui/main.qml (limited to 'tests/bttestui') diff --git a/tests/bttestui/Button.qml b/tests/bttestui/Button.qml new file mode 100644 index 00000000..25cffb43 --- /dev/null +++ b/tests/bttestui/Button.qml @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle +{ + id: button + signal clicked() + property string buttonText + width: powerOn.width + 10 + height: powerOn.height + 10 + border.color: "black" + border.width: 2 + radius: 4 + + + Text { + id: powerOn + text: buttonText + font.pointSize: 16 + anchors.centerIn: parent + } + MouseArea { + anchors.fill: parent + onClicked: button.clicked() + } +} diff --git a/tests/bttestui/README b/tests/bttestui/README new file mode 100644 index 00000000..e76ea90e --- /dev/null +++ b/tests/bttestui/README @@ -0,0 +1,21 @@ +This application is a graphical test application for Bluetooth. +It is able to invoke close to all API features offered by the QtBluetooth API. +It includes features like: + +1.) Turning Bluetooth on/off +2.) Pairing with remote devices +3.) Discovery of services and devices +4.) Connecting to a remote SPP profile using RFCOMM +5.) Start of a SPP Bluetooth server + +Some features require interaction with remote devices. In particular this involves +pairing and the direct connecting to remote SPP servers. The remote test device address +is hardcoded via BTCHAT_DEVICE_ADDR. + +Also, the test application reuses the service uuid employed by the btchat example +(see examples/bluetooth/btchat). It can either directly connect to the remote +btchat app server or create its own btchat server. + +Note that the application is purposely very liberal with regards to the amount of its +debug output. This is done to be able to confirm proper working of the tested +APIs. diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp new file mode 100644 index 00000000..b73a2a73 --- /dev/null +++ b/tests/bttestui/btlocaldevice.cpp @@ -0,0 +1,710 @@ +/**************************************************************************** +** +** 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 "btlocaldevice.h" +#include +#include +#include + +#define BTCHAT_DEVICE_ADDR "00:15:83:38:17:C3" + +//same uuid as examples/bluetooth/btchat +#define TEST_SERVICE_UUID "e8e10f95-1a70-4b27-9ccf-02010264e9c8" + +BtLocalDevice::BtLocalDevice(QObject *parent) : + QObject(parent) +{ + localDevice = new QBluetoothLocalDevice(this); + connect(localDevice, SIGNAL(error(QBluetoothLocalDevice::Error)), + this, SIGNAL(error(QBluetoothLocalDevice::Error))); + connect(localDevice, SIGNAL(hostModeStateChanged(QBluetoothLocalDevice::HostMode)), + this, SIGNAL(hostModeStateChanged())); + connect(localDevice, SIGNAL(pairingFinished(QBluetoothAddress,QBluetoothLocalDevice::Pairing)), + this, SLOT(pairingFinished(QBluetoothAddress,QBluetoothLocalDevice::Pairing))); + connect(localDevice, SIGNAL(deviceConnected(QBluetoothAddress)), + this, SLOT(connected(QBluetoothAddress))); + connect(localDevice, SIGNAL(deviceDisconnected(QBluetoothAddress)), + this, SLOT(disconnected(QBluetoothAddress))); + connect(localDevice, SIGNAL(pairingDisplayConfirmation(QBluetoothAddress,QString)), + this, SLOT(pairingDisplayConfirmation(QBluetoothAddress,QString))); + + if (localDevice->isValid()) { + deviceAgent = new QBluetoothDeviceDiscoveryAgent(this); + connect(deviceAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), + this, SLOT(deviceDiscovered(QBluetoothDeviceInfo))); + connect(deviceAgent, SIGNAL(finished()), + this, SLOT(discoveryFinished())); + connect(deviceAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), + this, SLOT(discoveryError(QBluetoothDeviceDiscoveryAgent::Error))); + connect(deviceAgent, SIGNAL(canceled()), + this, SLOT(discoveryCanceled())); + + serviceAgent = new QBluetoothServiceDiscoveryAgent(this); + connect(serviceAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), + this, SLOT(serviceDiscovered(QBluetoothServiceInfo))); + connect(serviceAgent, SIGNAL(finished()), + this, SLOT(serviceDiscoveryFinished())); + connect(serviceAgent, SIGNAL(canceled()), + this, SLOT(serviceDiscoveryCanceled())); + connect(serviceAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)), + this, SLOT(serviceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error))); + + socket = new QBluetoothSocket(this); + connect(socket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), + this, SLOT(socketStateChanged(QBluetoothSocket::SocketState))); + connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), + this, SLOT(socketError(QBluetoothSocket::SocketError))); + connect(socket, SIGNAL(connected()), this, SLOT(socketConnected())); + connect(socket, SIGNAL(disconnected()), this, SLOT(socketDisconnected())); + connect(socket, SIGNAL(readyRead()), this, SLOT(readData())); + + server = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); + server->setSecurityFlags(QBluetooth::NoSecurity); + connect(server, SIGNAL(newConnection()), this, SLOT(serverNewConnection())); + connect(server, SIGNAL(error(QBluetoothServer::Error)), + this, SLOT(serverError(QBluetoothServer::Error))); + } else { + deviceAgent = 0; + serviceAgent = 0; + } +} + +BtLocalDevice::~BtLocalDevice() +{ + while (!serverSockets.isEmpty()) + { + QBluetoothSocket* s = serverSockets.takeFirst(); + s->abort(); + s->deleteLater(); + } +} + +QString BtLocalDevice::hostMode() const +{ + switch (localDevice->hostMode()) { + case QBluetoothLocalDevice::HostDiscoverable: + return QStringLiteral("HostMode: Discoverable"); + case QBluetoothLocalDevice::HostConnectable: + return QStringLiteral("HostMode: Connectable"); + case QBluetoothLocalDevice::HostDiscoverableLimitedInquiry: + return QStringLiteral("HostMode: DiscoverableLimit"); + case QBluetoothLocalDevice::HostPoweredOff: + return QStringLiteral("HostMode: Powered Off"); + } + + return QStringLiteral("HostMode: "); +} + +void BtLocalDevice::setHostMode(int newMode) +{ + localDevice->setHostMode((QBluetoothLocalDevice::HostMode)newMode); +} + +void BtLocalDevice::requestPairingUpdate(bool isPairing) +{ + QBluetoothAddress baddr(BTCHAT_DEVICE_ADDR); + if (baddr.isNull()) + return; + + if (isPairing) + localDevice->requestPairing(baddr, QBluetoothLocalDevice::Paired); + else + localDevice->requestPairing(baddr, QBluetoothLocalDevice::Unpaired); + + for (int i = 0; i < foundTestServers.count(); i++) { + if (isPairing) + localDevice->requestPairing(foundTestServers.at(i).device().address(), + QBluetoothLocalDevice::Paired); + else + localDevice->requestPairing(foundTestServers.at(i).device().address(), + QBluetoothLocalDevice::Unpaired); + } +} + +void BtLocalDevice::pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing) +{ + qDebug() << "(Un)Pairing finished" << address.toString() << pairing; +} + +void BtLocalDevice::connected(const QBluetoothAddress &addr) +{ + qDebug() << "Newly connected device" << addr.toString(); +} + +void BtLocalDevice::disconnected(const QBluetoothAddress &addr) +{ + qDebug() << "Newly disconnected device" << addr.toString(); +} + +void BtLocalDevice::pairingDisplayConfirmation(const QBluetoothAddress &address, const QString &pin) +{ + Q_UNUSED(pin); + Q_UNUSED(address); + QTimer::singleShot(3000, this, SLOT(confirmPairing())); +} + +void BtLocalDevice::confirmPairing() +{ + static bool confirm = false; + confirm = !confirm; //toggle + qDebug() << "######" << "Sending pairing confirmation: " << confirm; + localDevice->pairingConfirmation(confirm); +} + +void BtLocalDevice::deviceDiscovered(const QBluetoothDeviceInfo &info) +{ + QString services; + if (info.serviceClasses() & QBluetoothDeviceInfo::PositioningService) + services += "Position|"; + if (info.serviceClasses() & QBluetoothDeviceInfo::NetworkingService) + services += "Network|"; + if (info.serviceClasses() & QBluetoothDeviceInfo::RenderingService) + services += "Rendering|"; + if (info.serviceClasses() & QBluetoothDeviceInfo::CapturingService) + services += "Capturing|"; + if (info.serviceClasses() & QBluetoothDeviceInfo::ObjectTransferService) + services += "ObjectTra|"; + if (info.serviceClasses() & QBluetoothDeviceInfo::AudioService) + services += "Audio|"; + if (info.serviceClasses() & QBluetoothDeviceInfo::TelephonyService) + services += "Telephony|"; + if (info.serviceClasses() & QBluetoothDeviceInfo::InformationService) + services += "Information|"; + + services.truncate(services.length()-1); //cut last '/' + + qDebug() << "Found new device: " << info.name() << info.isValid() << info.address().toString() + << info.rssi() << info.majorDeviceClass() + << info.minorDeviceClass() << services; + +} + +void BtLocalDevice::discoveryFinished() +{ + qDebug() << "###### Device Discovery Finished"; +} + +void BtLocalDevice::discoveryCanceled() +{ + qDebug() << "###### Device Discovery Canceled"; +} + +void BtLocalDevice::discoveryError(QBluetoothDeviceDiscoveryAgent::Error error) +{ + QBluetoothDeviceDiscoveryAgent *client = qobject_cast(sender()); + if (!client) + return; + qDebug() << "###### Device Discovery Error:" << error << (client ? client->errorString() : QString()); +} + +void BtLocalDevice::startDiscovery() +{ + if (deviceAgent) { + qDebug() << "###### Starting device discovery process"; + deviceAgent->start(); + } +} + +void BtLocalDevice::stopDiscovery() +{ + if (deviceAgent) { + qDebug() << "Stopping device discovery process"; + deviceAgent->stop(); + } +} + +void BtLocalDevice::startServiceDiscovery(bool isMinimalDiscovery) +{ + if (serviceAgent) { + qDebug() << "###### Starting service discovery process"; + serviceAgent->start(isMinimalDiscovery + ? QBluetoothServiceDiscoveryAgent::MinimalDiscovery + : QBluetoothServiceDiscoveryAgent::FullDiscovery); + } +} + +void BtLocalDevice::stopServiceDiscovery() +{ + if (serviceAgent) { + qDebug() << "Stopping service discovery process"; + serviceAgent->stop(); + } +} + +void BtLocalDevice::serviceDiscovered(const QBluetoothServiceInfo &info) +{ + + qDebug() << "$$ Found new service" << info.device().address().toString() + << info.serviceUuid() << info.serviceName() << info.serviceDescription(); + + if (info.serviceUuid() == QBluetoothUuid(QString(TEST_SERVICE_UUID)) + || info.serviceClassUuids().contains(QBluetoothUuid(QString(TEST_SERVICE_UUID)))) + { + //This is here to detect the test server for SPP testing later on + bool alreadyKnown = false; + foreach (const QBluetoothServiceInfo& found, foundTestServers) { + if (found.device().address() == info.device().address()) { + alreadyKnown = true; + break; + } + } + + if (!alreadyKnown) { + foundTestServers.append(info); + qDebug() << "@@@@@@@@ Adding:" << info.device().address().toString(); + } + } +} + +void BtLocalDevice::serviceDiscoveryFinished() +{ + qDebug() << "###### Service Discovery Finished"; +} + +void BtLocalDevice::serviceDiscoveryCanceled() +{ + qDebug() << "###### Service Discovery Canceled"; +} + +void BtLocalDevice::serviceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error error) +{ + QBluetoothServiceDiscoveryAgent *client = qobject_cast(sender()); + if (!client) + return; + qDebug() << "###### Service Discovery Error:" << error << (client ? client->errorString() : QString()); +} + +void BtLocalDevice::dumpServiceDiscovery() +{ + if (serviceAgent) { + qDebug() << "Service Discovery active:" << serviceAgent->isActive(); + qDebug() << "Error:" << serviceAgent->error() << serviceAgent->errorString(); + QList list = serviceAgent->discoveredServices(); + qDebug() << "Discovered Services:" << list.count(); + + foreach (const QBluetoothServiceInfo &i, list) { + qDebug() << i.device().address().toString() << i.device().name() << i.serviceName(); + } + + qDebug() << "###### TestServer offered by:"; + foreach (const QBluetoothServiceInfo& found, foundTestServers) { + qDebug() << found.device().name() << found.device().address().toString(); + } + } +} + +void BtLocalDevice::connectToService() +{ + if (socket) + socket->connectToService(QBluetoothAddress(BTCHAT_DEVICE_ADDR),QBluetoothUuid(QString(TEST_SERVICE_UUID))); +} + +void BtLocalDevice::connectToServiceViaSearch() +{ + if (socket) { + qDebug() << "###### Connecting to service socket"; + if (!foundTestServers.isEmpty()) { + QBluetoothServiceInfo info = foundTestServers.at(0); + socket->connectToService(info.device().address(), QBluetoothUuid(QString(TEST_SERVICE_UUID))); + } else { + qWarning() << "Perform search for test service before triggering this function"; + } + } +} + +void BtLocalDevice::disconnectToService() +{ + if (socket) { + qDebug() << "###### Disconnecting socket"; + socket->disconnectFromService(); + } +} + +void BtLocalDevice::closeSocket() +{ + if (socket) { + qDebug() << "###### Closing socket"; + socket->close(); + } + + if (!serverSockets.isEmpty()) { + qDebug() << "###### Closing server sockets"; + foreach (QBluetoothSocket *s, serverSockets) + s->close(); + } +} + +void BtLocalDevice::abortSocket() +{ + if (socket) { + qDebug() << "###### Disconnecting socket"; + socket->abort(); + } +} + +void BtLocalDevice::socketConnected() +{ + qDebug() << "###### Socket connected"; +} + +void BtLocalDevice::socketDisconnected() +{ + qDebug() << "###### Socket disconnected"; +} + +void BtLocalDevice::socketError(QBluetoothSocket::SocketError error) +{ + QBluetoothSocket *client = qobject_cast(sender()); + + qDebug() << "###### Socket error" << error << (client ? client->errorString() : QString()); +} + +void BtLocalDevice::socketStateChanged(QBluetoothSocket::SocketState state) +{ + qDebug() << "###### Socket state" << state; + emit socketStateUpdate((int) state); +} + +void BtLocalDevice::dumpSocketInformation() +{ + if (socket) { + qDebug() << "*******************************"; + qDebug() << "Local info (addr, name, port):" << socket->localAddress().toString() + << socket->localName() << socket->localPort(); + qDebug() << "Peer Info (adr, name, port):" << socket->peerAddress().toString() + << socket->peerName() << socket->peerPort(); + qDebug() << "socket type:" << socket->socketType(); + qDebug() << "socket state:" << socket->state(); + QString tmp; + switch (socket->error()) { + case QBluetoothSocket::NoSocketError: tmp += "NoSocketError"; break; + case QBluetoothSocket::UnknownSocketError: tmp += "UnknownSocketError"; break; + case QBluetoothSocket::HostNotFoundError: tmp += "HostNotFoundError"; break; + case QBluetoothSocket::ServiceNotFoundError: tmp += "ServiceNotFound"; break; + case QBluetoothSocket::NetworkError: tmp += "NetworkError"; break; + //case QBluetoothSocket::OperationError: tmp+= "OperationError"; break; + case QBluetoothSocket::UnsupportedProtocolError: tmp += "UnsupportedProtocolError"; break; + default: tmp+= "Undefined"; break; + } + + qDebug() << "socket error:" << tmp << socket->errorString(); + } else { + qDebug() << "No valid socket existing"; + } +} + +void BtLocalDevice::writeData() +{ + const char * testData = "ABCABC\n"; + if (socket && socket->state() == QBluetoothSocket::ConnectedState) { + socket->write(testData); + } + foreach (QBluetoothSocket* client, serverSockets) { + client->write(testData); + } +} + +void BtLocalDevice::readData() +{ + if (socket) { + while (socket->canReadLine()) { + QByteArray line = socket->readLine().trimmed(); + qDebug() << ">>>>" << QString::fromUtf8(line.constData(), line.length()); + } + } +} + +void BtLocalDevice::serverError(QBluetoothServer::Error error) +{ + qDebug() << "###### Server socket error" << error; +} + +void BtLocalDevice::serverListenPort() +{ + if (server && localDevice) { + if (server->isListening() || serviceInfo.isRegistered()) { + qDebug() << "###### Already listening" << serviceInfo.isRegistered(); + return; + } + qDebug() << "###### Start listening via port"; + bool ret = server->listen(localDevice->address()); + qDebug() << "###### Listening(Expecting TRUE):" << ret; + + if (!ret) + return; + + QBluetoothServiceInfo::Sequence classId; + classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort)); + serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList, + classId); + + classId.prepend(QVariant::fromValue(QBluetoothUuid(QString(TEST_SERVICE_UUID)))); + serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId); + + // Service name, description and provider + serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceName, tr("Bt Chat Server")); + serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceDescription, + tr("Example bluetooth chat server")); + serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceProvider, tr("qt-project.org")); + + // Service UUID set + serviceInfo.setServiceUuid(QBluetoothUuid(QString(TEST_SERVICE_UUID))); + + + // Service Discoverability + serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, + QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup)); + + // Protocol descriptor list + QBluetoothServiceInfo::Sequence protocolDescriptorList; + QBluetoothServiceInfo::Sequence protocol; + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap)); + protocolDescriptorList.append(QVariant::fromValue(protocol)); + protocol.clear(); + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) + << QVariant::fromValue(quint8(server->serverPort())); + protocolDescriptorList.append(QVariant::fromValue(protocol)); + serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, + protocolDescriptorList); + + //Register service + qDebug() << "###### Registering service on" << localDevice->address().toString(); + bool result = serviceInfo.registerService(localDevice->address()); + if (!result) { + server->close(); + qDebug() << "###### Reverting registration due to SDP failure."; + } + } + +} + +void BtLocalDevice::serverListenUuid() +{ + if (server) { + if (server->isListening() || serviceInfo.isRegistered()) { + qDebug() << "###### Already listening" << serviceInfo.isRegistered(); + return; + } + qDebug() << "###### Start listening via UUID"; + serviceInfo = server->listen(QBluetoothUuid(QString(TEST_SERVICE_UUID)), tr("Bt Chat Server")); + qDebug() << "###### Listening(Expecting TRUE, TRUE):" << serviceInfo.isRegistered() << serviceInfo.isValid(); + } +} + +void BtLocalDevice::serverClose() +{ + if (server) { + qDebug() << "###### Closing Server socket"; + if (serviceInfo.isRegistered()) + serviceInfo.unregisterService(); + server->close(); + } +} + +void BtLocalDevice::serverNewConnection() +{ + qDebug() << "###### New incoming server connection, pending:" << server->hasPendingConnections(); + if (!server->hasPendingConnections()) { + qDebug() << "FAIL: expected pending server connection"; + return; + } + QBluetoothSocket *client = server->nextPendingConnection(); + if (!client) { + qDebug() << "FAIL: Cannot obtain pending server connection"; + return; + } + + client->setParent(this); + connect(client, SIGNAL(disconnected()), this, SLOT(clientSocketDisconnected())); + connect(client, SIGNAL(readyRead()), this, SLOT(clientSocketReadyRead())); + connect(client, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), + this, SLOT(socketStateChanged(QBluetoothSocket::SocketState))); + connect(client, SIGNAL(error(QBluetoothSocket::SocketError)), + this, SLOT(socketError(QBluetoothSocket::SocketError))); + connect(client, SIGNAL(connected()), this, SLOT(socketConnected())); + serverSockets.append(client); +} + +void BtLocalDevice::clientSocketDisconnected() +{ + QBluetoothSocket *client = qobject_cast(sender()); + if (!client) + return; + + qDebug() << "######" << "Removing server socket connection"; + + serverSockets.removeOne(client); + client->deleteLater(); +} + + +void BtLocalDevice::clientSocketReadyRead() +{ + QBluetoothSocket *socket = qobject_cast(sender()); + if (!socket) + return; + + while (socket->canReadLine()) { + QByteArray line = socket->readLine().trimmed(); + qDebug() << ">>(" << socket->peerName() << ")>>" + << QString::fromUtf8(line.constData(), line.length()); + } +} + + +void BtLocalDevice::dumpServerInformation() +{ + static QBluetooth::SecurityFlags secFlag = QBluetooth::Authentication; + if (server) { + qDebug() << "*******************************"; + qDebug() << "server port:" <serverPort() + << "type:" << server->serverType() + << "address:" << server->serverAddress().toString(); + qDebug() << "error:" << server->error(); + qDebug() << "listening:" << server->isListening() + << "hasPending:" << server->hasPendingConnections() + << "maxPending:" << server->maxPendingConnections(); + qDebug() << "security:" << server->securityFlags() << "Togling security flag"; + if (secFlag == QBluetooth::Authentication) + secFlag = QBluetooth::Encryption; + else + secFlag = QBluetooth::Authentication; + + //server->setSecurityFlags(secFlag); + + foreach (const QBluetoothSocket *client, serverSockets) { + qDebug() << "##" << client->localAddress().toString() + << client->localName() << client->localPort(); + qDebug() << "##" << client->peerAddress().toString() + << client->peerName() << client->peerPort(); + qDebug() << client->socketType() << client->state(); + QString tmp; + switch (client->error()) { + case QBluetoothSocket::NoSocketError: tmp += "NoSocketError"; break; + case QBluetoothSocket::UnknownSocketError: tmp += "UnknownSocketError"; break; + case QBluetoothSocket::HostNotFoundError: tmp += "HostNotFoundError"; break; + case QBluetoothSocket::ServiceNotFoundError: tmp += "ServiceNotFound"; break; + case QBluetoothSocket::NetworkError: tmp += "NetworkError"; break; + case QBluetoothSocket::UnsupportedProtocolError: tmp += "UnsupportedProtocolError"; break; + //case QBluetoothSocket::OperationError: tmp+= "OperationError"; break; + default: tmp += QString::number((int)client->error()); break; + } + + qDebug() << "socket error:" << tmp << client->errorString(); + } + } +} + +void BtLocalDevice::dumpInformation() +{ + qDebug() << "###### default local device"; + dumpLocalDevice(localDevice); + QList list = QBluetoothLocalDevice::allDevices(); + qDebug() << "Found local devices: " << list.count(); + foreach (const QBluetoothHostInfo &info, list) { + qDebug() << " " << info.address().toString() << " " <pairingStatus(BB); + qDebug() << "###### Bonding state with" << address2.toString() << ": " << localDevice->pairingStatus(address2); + qDebug() << "###### Bonding state with" << address.toString() << ": " << localDevice->pairingStatus(address); + + qDebug() << "###### Connected Devices"; + foreach (const QBluetoothAddress &addr, localDevice->connectedDevices()) + qDebug() << " " << addr.toString(); + + qDebug() << "###### Discovered Devices"; + if (deviceAgent) + foreach (const QBluetoothDeviceInfo &info, deviceAgent->discoveredDevices()) + deviceDiscovered(info); + + QBluetoothDeviceDiscoveryAgent invalidAgent(QBluetoothAddress("11:22:33:44:55:66")); + invalidAgent.start(); + qDebug() << "######" << "Testing device discovery agent constructor with invalid address"; + qDebug() << "######" << (invalidAgent.error() == QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError) + << "(Expected: true)"; + QBluetoothDeviceDiscoveryAgent validAgent(localDevice->address()); + validAgent.start(); + qDebug() << "######" << (validAgent.error() == QBluetoothDeviceDiscoveryAgent::NoError) << "(Expected: true)"; + + QBluetoothServiceDiscoveryAgent invalidSAgent(QBluetoothAddress("11:22:33:44:55:66")); + invalidSAgent.start(); + qDebug() << "######" << "Testing service discovery agent constructor with invalid address"; + qDebug() << "######" << (invalidSAgent.error() == QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError) + << "(Expected: true)"; + QBluetoothServiceDiscoveryAgent validSAgent(localDevice->address()); + validSAgent.start(); + qDebug() << "######" << (validSAgent.error() == QBluetoothServiceDiscoveryAgent::NoError) << "(Expected: true)"; +} + +void BtLocalDevice::powerOn() +{ + qDebug() << "Powering on"; + localDevice->powerOn(); +} + +void BtLocalDevice::reset() +{ + emit error((QBluetoothLocalDevice::Error)1000); + if (serviceAgent) { + serviceAgent->clear(); + } + foundTestServers.clear(); +} + +void BtLocalDevice::dumpLocalDevice(QBluetoothLocalDevice *dev) +{ + qDebug() << " Valid: " << dev->isValid(); + qDebug() << " Name" << dev->name(); + qDebug() << " Address" << dev->address().toString(); + qDebug() << " HostMode" << dev->hostMode(); +} diff --git a/tests/bttestui/btlocaldevice.h b/tests/bttestui/btlocaldevice.h new file mode 100644 index 00000000..759599b7 --- /dev/null +++ b/tests/bttestui/btlocaldevice.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** 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 BTLOCALDEVICE_H +#define BTLOCALDEVICE_H + +#include +#include +#include +#include +#include +#include + +class BtLocalDevice : public QObject +{ + Q_OBJECT +public: + explicit BtLocalDevice(QObject *parent = 0); + ~BtLocalDevice(); + Q_PROPERTY(QString hostMode READ hostMode NOTIFY hostModeStateChanged) + + QString hostMode() const; + +signals: + void error(QBluetoothLocalDevice::Error error); + void hostModeStateChanged(); + void socketStateUpdate(int foobar); + +public slots: + //QBluetoothLocalDevice + void dumpInformation(); + void powerOn(); + void reset(); + void setHostMode(int newMode); + void requestPairingUpdate(bool isPairing); + void pairingFinished(const QBluetoothAddress &address, QBluetoothLocalDevice::Pairing pairing); + void connected(const QBluetoothAddress &addr); + void disconnected(const QBluetoothAddress &addr); + void pairingDisplayConfirmation(const QBluetoothAddress &address, const QString &pin); + void confirmPairing(); + + //QBluetoothDeviceDiscoveryAgent + void deviceDiscovered(const QBluetoothDeviceInfo &info); + void discoveryFinished(); + void discoveryCanceled(); + void discoveryError(QBluetoothDeviceDiscoveryAgent::Error error); + void startDiscovery(); + void stopDiscovery(); + + //QBluetoothServiceDiscoveryAgent + void startServiceDiscovery(bool isMinimalDiscovery); + void stopServiceDiscovery(); + void serviceDiscovered(const QBluetoothServiceInfo &info); + void serviceDiscoveryFinished(); + void serviceDiscoveryCanceled(); + void serviceDiscoveryError(QBluetoothServiceDiscoveryAgent::Error error); + void dumpServiceDiscovery(); + + //QBluetoothSocket + void connectToService(); + void connectToServiceViaSearch(); + void disconnectToService(); + void closeSocket(); + void abortSocket(); + void socketConnected(); + void socketDisconnected(); + void socketError(QBluetoothSocket::SocketError error); + void socketStateChanged(QBluetoothSocket::SocketState); + void dumpSocketInformation(); + void writeData(); + void readData(); + + //QBluetoothServer + void serverError(QBluetoothServer::Error error); + void serverListenPort(); + void serverListenUuid(); + void serverClose(); + void serverNewConnection(); + void clientSocketDisconnected(); + void clientSocketReadyRead(); + void dumpServerInformation(); + +private: + void dumpLocalDevice(QBluetoothLocalDevice *dev); + + QBluetoothLocalDevice *localDevice; + QBluetoothDeviceDiscoveryAgent *deviceAgent; + QBluetoothServiceDiscoveryAgent *serviceAgent; + QBluetoothSocket *socket; + QBluetoothServer *server; + QList serverSockets; + QBluetoothServiceInfo serviceInfo; + + QList foundTestServers; +}; + +#endif // BTLOCALDEVICE_H diff --git a/tests/bttestui/bttest.qrc b/tests/bttestui/bttest.qrc new file mode 100644 index 00000000..bb3a7ad6 --- /dev/null +++ b/tests/bttestui/bttest.qrc @@ -0,0 +1,6 @@ + + + main.qml + Button.qml + + diff --git a/tests/bttestui/bttestui.pro b/tests/bttestui/bttestui.pro new file mode 100644 index 00000000..06248691 --- /dev/null +++ b/tests/bttestui/bttestui.pro @@ -0,0 +1,16 @@ +TARGET = bttestui +TEMPLATE = app + +SOURCES += main.cpp \ + btlocaldevice.cpp + +QT += quick bluetooth + +OTHER_FILES += main.qml \ + Button.qml + +RESOURCES += \ + bttest.qrc + +HEADERS += \ + btlocaldevice.h diff --git a/tests/bttestui/main.cpp b/tests/bttestui/main.cpp new file mode 100644 index 00000000..964eec59 --- /dev/null +++ b/tests/bttestui/main.cpp @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include + +#include + +#include "btlocaldevice.h" + +int main(int argc, char *argv[]) +{ + QLoggingCategory::setFilterRules(QStringLiteral("qt.bluetooth* = true")); + QGuiApplication app(argc, argv); + qmlRegisterType("Local", 5, 2, "BluetoothDevice"); + + QQuickView view; + view.setSource(QStringLiteral("qrc:///main.qml")); + view.setWidth(550); + view.setHeight(550); + view.setResizeMode(QQuickView::SizeRootObjectToView); + + QObject::connect(view.engine(), SIGNAL(quit()), qApp, SLOT(quit())); + view.show(); + + return app.exec(); +} diff --git a/tests/bttestui/main.qml b/tests/bttestui/main.qml new file mode 100644 index 00000000..d53aa4e1 --- /dev/null +++ b/tests/bttestui/main.qml @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +import QtQuick 2.0 +import Local 5.2 + +Rectangle { + width: 360 + height: 360 + + BluetoothDevice { + id: device + function evaluateError(error) + { + switch (error) { + case 0: return "Last Error: NoError" + case 1: return "Last Error: Pairing Error" + case 100: return "Last Error: Unknown Error" + case 1000: return "Last Error: " + } + } + + function evaluateSocketState(s) { + switch (s) { + case 0: return "Socket: Unconnected"; + case 1: return "Socket: HostLookup"; + case 2: return "Socket: Connecting"; + case 3: return "Socket: Connected"; + case 4: return "Socket: Bound"; + case 5: return "Socket: Listening"; + case 6: return "Socket: Closing"; + } + return "Socket: "; + } + + + onError: errorText.text = evaluateError(error) + onHostModeStateChanged: hostModeText.text = device.hostMode; + onSocketStateUpdate : socketStateText.text = evaluateSocketState(foobar); + } + + Text { + id: errorText + anchors.left: parent.left + anchors.bottom: parent.bottom + anchors.bottomMargin: 3 + text: "Last Error: " + } + Text { + id: hostModeText + anchors.left: parent.left + anchors.bottom: socketStateText.top + anchors.bottomMargin: 3 + text: device.hostMode + } + Text { + id: socketStateText + anchors.left: parent.left + anchors.bottom: errorText.top + anchors.bottomMargin: 3 + text: device.evaluateSocketState(0) + } + + Row { + anchors.top: parent.top + anchors.left: parent.left + anchors.margins: 4 + + spacing: 8 + Column { + spacing: 8 + Text{ + width: connectBtn.width + horizontalAlignment: Text.AlignLeft + font.pointSize: 12 + wrapMode: Text.WordWrap + text: "Device Management" + } + Button { + + buttonText: "PowerOn" + onClicked: device.powerOn() + } + Button { + buttonText: "PowerOff" + onClicked: device.setHostMode(0) + } + Button { + id: connectBtn + buttonText: "Connectable" + onClicked: device.setHostMode(1) + } + Button { + buttonText: "Discover" + onClicked: device.setHostMode(2) + } + Button { + buttonText: "Pair" + onClicked: device.requestPairingUpdate(true) + } + Button { + buttonText: "Unpair" + onClicked: device.requestPairingUpdate(false) + } + } + Column { + spacing: 8 + Text{ + width: startFullSDiscBtn.width + horizontalAlignment: Text.AlignLeft + font.pointSize: 12 + wrapMode: Text.WordWrap + text: "Device & Service Discovery" + } + Button { + buttonText: "StartDDisc" + onClicked: device.startDiscovery() + } + Button { + buttonText: "StopDDisc" + onClicked: device.stopDiscovery() + } + Button { + buttonText: "StartMinSDisc" + onClicked: device.startServiceDiscovery(true) + } + Button { + id: startFullSDiscBtn + buttonText: "StartFullSDisc" + onClicked: device.startServiceDiscovery(false) + } + Button { + buttonText: "StopSDisc" + onClicked: device.stopServiceDiscovery(); + } + Button { + buttonText: "DumpSDisc" + onClicked: device.dumpServiceDiscovery(); + } + + } + + Column { + spacing: 8 + Text{ + width: connectSearchBtn.width + horizontalAlignment: Text.AlignLeft + font.pointSize: 12 + wrapMode: Text.WordWrap + text: "Client & Server Socket" + } + Button { + buttonText: "SocketDump" + onClicked: device.dumpSocketInformation() + } + Button { + buttonText: "CConnect" + onClicked: device.connectToService() + } + Button { + id: connectSearchBtn + buttonText: "ConnectSearch" + onClicked: device.connectToServiceViaSearch() + } + Button { + buttonText: "CDisconnect" + onClicked: device.disconnectToService() + } + + Button { + buttonText: "CClose" + onClicked: device.closeSocket() + } + + Button { + buttonText: "CAbort" + onClicked: device.abortSocket() + } + Button { + //Write to all open sockets ABCABC\n + buttonText: "CSWrite" + onClicked: device.writeData() + } + Button { + buttonText: "ServerDump" + onClicked: device.dumpServerInformation() + } + Button { + //Listen on server via port + buttonText: "SListenPort" + onClicked: device.serverListenPort() + } + Button { + //Listen on server via uuid + buttonText: "SListenUuid" + onClicked: device.serverListenUuid() + } + Button { + //Close Bluetooth server + buttonText: "SClose" + onClicked: device.serverClose() + } + } + Column { + spacing: 8 + Text{ + width: resetBtn.width + horizontalAlignment: Text.AlignLeft + font.pointSize: 12 + wrapMode: Text.WordWrap + text: "Misc Controls" + } + Button { + buttonText: "Dump" + onClicked: device.dumpInformation() + } + Button { + id: resetBtn + buttonText: "Reset" + onClicked: device.reset() + } + } + } +} -- cgit v1.2.3