diff options
Diffstat (limited to 'src/bluetooth/qbluetoothsocket.cpp')
-rw-r--r-- | src/bluetooth/qbluetoothsocket.cpp | 660 |
1 files changed, 660 insertions, 0 deletions
diff --git a/src/bluetooth/qbluetoothsocket.cpp b/src/bluetooth/qbluetoothsocket.cpp new file mode 100644 index 00000000..d265cc22 --- /dev/null +++ b/src/bluetooth/qbluetoothsocket.cpp @@ -0,0 +1,660 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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, Nokia gives you certain additional +** rights. These rights are described in the Nokia 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qbluetoothsocket.h" +#include "qbluetoothsocket_p.h" + +#include "qbluetoothdeviceinfo.h" +#include "qbluetoothserviceinfo.h" +#include "qbluetoothservicediscoveryagent.h" + + +#include <QDebug> +#include <QSocketNotifier> + +/*! + \class QBluetoothSocket + \brief The QBluetoothSocket class provides a Bluetooth socket. + + \ingroup connectivity-bluetooth + \inmodule QtConnectivity + \since 5.0 + + QBluetoothSocket supports two socket types, \l {QBluetoothSocket::L2capSocket}{L2CAP} and + \l {QBluetoothSocket::RfcommSocket}{RFCOMM}. + + \l {QBluetoothSocket::L2capSocket}{L2CAP} is a low level datagram-oriented Bluetooth socket. + + \l {QBluetoothSocket::RfcommSocket}{RFCOMM} is a reliable, stream-oriented socket. RFCOMM + sockets emulate an RS-232 serial port. + + To create a connection to a Bluetooth service create a socket of the appropriate type and call + connectToService() passing the Bluetooth address and port number. QBluetoothSocket will emit + the connected() signal when the connection is established. + + +*/ + +/*! + \enum QBluetoothSocket::SocketType + + This enum describes the Bluetooth socket type. + + \value UnknownSocketType Unknown socket type. + \value L2capSocket L2CAP socket. + \value RfcommSocket RFCOMM socket. +*/ + +/*! + \enum QBluetoothSocket::SocketState + + This enum describes the state of the Bluetooth socket. + + \value UnconnectedState Socket is not connected. + \value ServiceLookupState Socket is querying connection parameters. + \value ConnectingState Socket is attempting to connect to a device. + \value ConnectedState Socket is connected to a device. + \value BoundState 242.nmp.nokia.com +_IceTransSocketUNIXConnect: Cannot connect to non-local host saisd Socket is bound to a local address and port. + \value ClosingState Socket is connected and will be closed once all pending data is + written to the socket. + \value ListeningState Socket is listening for incoming connections. +*/ + +/*! + \enum QBluetoothSocket::SocketError + + This enum describes Bluetooth socket error types. + + \value UnknownSocketError An unknown error has occurred. + \value NoSocketError No error. Used for testing. + \value ConnectionRefusedError Connection refused or device not available. + \value RemoteHostClosedError The remote host closed the socket + \value HostNotFoundError Could not find the remote host + \value ServiceNotFoundError Could not find the service UUID on remote host + \value NetworkError Attempt to read or write from socket returned an error +*/ + +/*! + \fn void QBluetoothSocket::connected() + + This signal is emitted when a connection is established. + + \sa QBluetoothSocket::ConnectedState, stateChanged() +*/ + +/*! + \fn void QBluetoothSocket::disconnected() + + This signal is emitted when the socket is disconnected. + + \sa QBluetoothSocket::UnconnectedState, stateChanged() +*/ + +/*! + \fn void QBluetoothSocket::error(QBluetoothSocket::SocketError error) + + This signal is emitted when an \a error occurs. + + \sa error() +*/ + +/*! + \fn QBluetoothSocket::stateChanged(QBluetoothSocket::SocketState state) + + This signal is emitted when the socket state changes to \a state. + + \sa connected(), disconnected(), state(), QBluetoothSocket::SocketState +*/ + +/*! + \fn void QBluetoothSocket::abort() + + Aborts the current connection and resets the socket. Unlike disconnectFromService(), this + function immediately closes the socket, discarding any pending data in the write buffer. + + \sa disconnectFromService(), close() +*/ + +/*! + \fn void QBluetoothSocket::close() + + Disconnects the socket's connection with the device. +*/ + +/*! + \fn void QBluetoothSocket::disconnectFromService() + + Attempts to close the socket. If there is pending data waiting to be written QBluetoothSocket + will enter ClosingState and wait until all data has been written. Eventually, it will enter + UnconnectedState and emit the disconnected() signal. + + \sa connectToService() +*/ + +/*! + \fn QString QBluetoothSocket::localName() const + + Returns the name of the local device. +*/ + +/*! + \fn QBluetoothAddress QBluetoothSocket::localAddress() const + + Returns the address of the local device. +*/ + +/*! + \fn quint16 QBluetoothSocket::localPort() const + + Returns the port number of the local socket if available; otherwise returns 0. +*/ + +/*! + \fn QString QBluetoothSocket::peerName() const + + Returns the name of the peer device. +*/ + +/*! + \fn QBluetoothAddress QBluetoothSocket::peerAddress() const + + Returns the address of the peer device. +*/ + +/*! + \fn quint16 QBluetoothSocket::peerPort() const + + Return the port number of the peer socket if available; otherwise returns 0. +*/ + +/*! + \fn qint64 QBluetoothSocket::readData(char *data, qint64 maxSize) + + \reimp +*/ + +/*! + \fn qint64 QBluetoothSocket::writeData(const char *data, qint64 maxSize) + + \reimp +*/ + +/*! + Constructs a Bluetooth socket of \a socketType type, with \a parent. +*/ +QBluetoothSocket::QBluetoothSocket(QBluetoothSocket::SocketType socketType, QObject *parent) +: QIODevice(parent), d_ptr(new QBluetoothSocketPrivate) +{ + d_ptr->q_ptr = this; + + Q_D(QBluetoothSocket); + d->ensureNativeSocket(socketType); + + setOpenMode(QIODevice::ReadWrite); +} + +/*! + Constructs a Bluetooth socket with \a parent. +*/ +QBluetoothSocket::QBluetoothSocket(QObject *parent) + : QIODevice(parent), d_ptr(new QBluetoothSocketPrivate) +{ + d_ptr->q_ptr = this; + setOpenMode(QIODevice::ReadWrite); +} + +/*! + Destroys the Bluetooth socket. +*/ +QBluetoothSocket::~QBluetoothSocket() +{ + delete d_ptr; + d_ptr = 0; +} + +/*! + \reimp +*/ +bool QBluetoothSocket::isSequential() const +{ + return true; +} + +/*! + Returns the number of incoming bytes that are waiting to be read. + + \sa bytesToWrite(), read() +*/ +qint64 QBluetoothSocket::bytesAvailable() const +{ + Q_D(const QBluetoothSocket); + return QIODevice::bytesAvailable() + d->bytesAvailable(); +} + +/*! + Returns the number of bytes that are waiting to be written. The bytes are written when control + goes back to the event loop. +*/ +qint64 QBluetoothSocket::bytesToWrite() const +{ + Q_D(const QBluetoothSocket); + return d->txBuffer.size(); +} + +/*! + Attempts to connect to the service described by \a service. + + The socket is opened in the given \a openMode. + + The socket first enters ConnectingState and attempts to connect to the device providing + \a service. If a connection is established, QBluetoothSocket enters ConnectedState and + emits connected(). + + At any point, the socket can emit error() to siganl that an error occurred. + + \sa state(), disconnectFromService() +*/ +void QBluetoothSocket::connectToService(const QBluetoothServiceInfo &service, OpenMode openMode) +{ + Q_D(QBluetoothSocket); + setOpenMode(openMode); + + if (service.protocolServiceMultiplexer() > 0) { + if (!d->ensureNativeSocket(L2capSocket)) { + emit error(UnknownSocketError); + return; + } + d->connectToService(service.device().address(), service.protocolServiceMultiplexer(), openMode); + } else if (service.serverChannel() > 0) { + if (!d->ensureNativeSocket(RfcommSocket)) { + emit error(UnknownSocketError); + return; + } + d->connectToService(service.device().address(), service.serverChannel(), openMode); + } else { + // try doing service discovery to see if we can find the socket + if(service.serviceUuid().isNull()){ + qWarning() << "No port, no PSM, and no UUID provided, unable to connect"; + return; + } + qDebug() << "Need a port/psm, doing discovery"; + doDeviceDiscovery(service, openMode); + } +} + +/*! + Attempts to make a connection to the service identified by \a uuid on the device with address + \a address. + + The socket is opened in the given \a openMode. + + The socket first enters the ServiceLookupState and queries the connection parameters for + \a uuid. If the service parameters are successfully retrieved the socket enters + ConnectingState, and attempts to connect to \a address. If a connection is established, + QBluetoothSocket enters Connected State and emits connected(). + + At any point, the socket can emit error() to signal that an error occurred. + + \sa state(), disconnectFromService() +*/ +void QBluetoothSocket::connectToService(const QBluetoothAddress &address, const QBluetoothUuid &uuid, OpenMode openMode) +{ + QBluetoothServiceInfo service; + QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::MiscellaneousDevice); + service.setDevice(device); + service.setServiceUuid(uuid); + doDeviceDiscovery(service, openMode); +} + +/*! + Attempts to make a connection with \a address on the given \a port. + + The socket is opened in the given \a openMode. + + The socket first enters ConnectingState, and attempts to connect to \a address. If a + connection is established, QBluetoothSocket enters ConnectedState and emits connected(). + + At any point, the socket can emit error() to signal that an error occurred. + + \sa state(), disconnectFromService() +*/ +void QBluetoothSocket::connectToService(const QBluetoothAddress &address, quint16 port, OpenMode openMode) +{ + Q_D(QBluetoothSocket); + setOpenMode(openMode); + + d->connectToService(address, port, openMode); +} + +/*! + Returns the socket type. +*/ +QBluetoothSocket::SocketType QBluetoothSocket::socketType() const +{ + Q_D(const QBluetoothSocket); + return d->socketType; +} + +/*! + Returns the current state of the socket. +*/ +QBluetoothSocket::SocketState QBluetoothSocket::state() const +{ + Q_D(const QBluetoothSocket); + return d->state; +} + +/*! + Returns the last error. +*/ +QBluetoothSocket::SocketError QBluetoothSocket::error() const +{ + Q_D(const QBluetoothSocket); + return d->socketError; +} + +/*! + Returns a user displayable text string for the error. + */ +QString QBluetoothSocket::errorString() const +{ + Q_D(const QBluetoothSocket); + return d->errorString; +} + +/*! + Sets the socket state to \a state. +*/ +void QBluetoothSocket::setSocketState(QBluetoothSocket::SocketState state) +{ + Q_D(QBluetoothSocket); + SocketState old = d->state; + d->state = state; + if(old != d->state) + emit stateChanged(state); + if(state == ListeningState){ + // TODO: look at this, is this really correct? + // if we're a listening socket we can't handle connects? + if (d->readNotifier) { + d->readNotifier->setEnabled(false); + } + } +} + +/*! + Returns true if you can read at least one line from the device + */ + +bool QBluetoothSocket::canReadLine() const +{ + Q_D(const QBluetoothSocket); + return d->buffer.canReadLine() || QIODevice::canReadLine(); +} + +/*! + Sets the type of error that last occurred to \a error_. +*/ +void QBluetoothSocket::setSocketError(QBluetoothSocket::SocketError error_) +{ + Q_D(QBluetoothSocket); + d->socketError = error_; + emit error(error_); +} + +/*! + Start device discovery for \a service and open the socket with \a openMode. If the socket + is created with a service uuid device address we must use service discovery to find the + port number to connect to. +*/ + +void QBluetoothSocket::doDeviceDiscovery(const QBluetoothServiceInfo &service, OpenMode openMode) +{ + Q_D(QBluetoothSocket); + + qDebug() << "Starting discovery"; + + if(d->discoveryAgent) { + delete d->discoveryAgent; + } + + d->discoveryAgent = new QBluetoothServiceDiscoveryAgent(service.device().address(),this); + + qDebug() << "Got agent"; + + connect(d->discoveryAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), this, SLOT(serviceDiscovered(QBluetoothServiceInfo))); + connect(d->discoveryAgent, SIGNAL(finished()), this, SLOT(discoveryFinished())); + + d->openMode = openMode; + + if(!service.serviceUuid().isNull()) + d->discoveryAgent->setUuidFilter(service.serviceUuid()); + + if(!service.serviceClassUuids().isEmpty()) + d->discoveryAgent->setUuidFilter(service.serviceClassUuids()); + + // we have to ID the service somehow + Q_ASSERT(!d->discoveryAgent->uuidFilter().isEmpty()); + + qDebug() << "UUID filter" << d->discoveryAgent->uuidFilter(); + + d->discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery); +} + +void QBluetoothSocket::serviceDiscovered(const QBluetoothServiceInfo &service) +{ + Q_D(QBluetoothSocket); + qDebug() << "FOUND SERVICE!" << service; + if(service.protocolServiceMultiplexer() != 0 || service.serverChannel() != 0) { + connectToService(service, d->openMode); + d->discoveryAgent->deleteLater(); + d->discoveryAgent = 0; + } +} + +void QBluetoothSocket::discoveryFinished() +{ + qDebug() << "Socket discovery finished"; + Q_D(QBluetoothSocket); + if(d->discoveryAgent){ + qDebug() << "Didn't find any"; + emit error(QBluetoothSocket::ServiceNotFoundError); + d->discoveryAgent->deleteLater(); + d->discoveryAgent = 0; + } +} + +void QBluetoothSocket::abort() +{ + Q_D(QBluetoothSocket); + d->abort(); + setSocketState(QBluetoothSocket::UnconnectedState); +} + +void QBluetoothSocket::disconnectFromService() +{ + // TODO: is this all we need to do? + Q_D(QBluetoothSocket); + d->close(); +} + +QString QBluetoothSocket::localName() const +{ + Q_D(const QBluetoothSocket); + return d->localName(); +} + +QBluetoothAddress QBluetoothSocket::localAddress() const +{ + Q_D(const QBluetoothSocket); + return d->localAddress(); +} + +quint16 QBluetoothSocket::localPort() const +{ + Q_D(const QBluetoothSocket); + return d->localPort(); +} + +QString QBluetoothSocket::peerName() const +{ + Q_D(const QBluetoothSocket); + return d->peerName(); +} + +QBluetoothAddress QBluetoothSocket::peerAddress() const +{ + Q_D(const QBluetoothSocket); + return d->peerAddress(); +} + +quint16 QBluetoothSocket::peerPort() const +{ + Q_D(const QBluetoothSocket); + return d->peerPort(); +} + +qint64 QBluetoothSocket::writeData(const char *data, qint64 maxSize) +{ + Q_D(QBluetoothSocket); + return d->writeData(data, maxSize); +} + +qint64 QBluetoothSocket::readData(char *data, qint64 maxSize) +{ + Q_D(QBluetoothSocket); + return d->readData(data, maxSize); +} + +void QBluetoothSocket::close() +{ + Q_D(QBluetoothSocket); + setSocketState(ClosingState); + + d->close(); + + setSocketState(UnconnectedState); +} + +/*! + Set the socket to use \a socketDescriptor with a type of \a socketType + which is in state \a socketState and mode \a openMode. + + Returns true on success +*/ + + +bool QBluetoothSocket::setSocketDescriptor(int socketDescriptor, SocketType socketType, + SocketState socketState, OpenMode openMode) +{ + Q_D(QBluetoothSocket); + return d->setSocketDescriptor(socketDescriptor, socketType, socketState, openMode); +} + +/*! + Returns the platform specific socket descriptor, if available +*/ + +int QBluetoothSocket::socketDescriptor() const +{ + Q_D(const QBluetoothSocket); + return d->socket; +} + + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, QBluetoothSocket::SocketError error) +{ + switch (error) { + case QBluetoothSocket::UnknownSocketError: + debug << "QBluetoothSocket::UnknownSocketError"; + break; + case QBluetoothSocket::ConnectionRefusedError: + debug << "QBluetoothSocket::ConnectionRefusedError"; + break; + case QBluetoothSocket::RemoteHostClosedError: + debug << "QBluetoothSocket::RemoteHostClosedError"; + break; + case QBluetoothSocket::HostNotFoundError: + debug << "QBluetoothSocket::HostNotFoundError"; + break; + case QBluetoothSocket::ServiceNotFoundError: + debug << "QBluetoothSocket::ServiceNotFoundError"; + break; + case QBluetoothSocket::NetworkError: + debug << "QBluetoothSocket::NetworkError"; + break; + default: + debug << "QBluetoothSocket::SocketError(" << (int)error << ")"; + } + return debug; +} + +QDebug operator<<(QDebug debug, QBluetoothSocket::SocketState state) +{ + switch (state) { + case QBluetoothSocket::UnconnectedState: + debug << "QBluetoothSocket::UnconnectedState"; + break; + case QBluetoothSocket::ConnectingState: + debug << "QBluetoothSocket::ConnectingState"; + break; + case QBluetoothSocket::ConnectedState: + debug << "QBluetoothSocket::ConnectedState"; + break; + case QBluetoothSocket::BoundState: + debug << "QBluetoothSocket::BoundState"; + break; + case QBluetoothSocket::ClosingState: + debug << "QBluetoothSocket::ClosingState"; + break; + case QBluetoothSocket::ListeningState: + debug << "QBluetoothSocket::ListeningState"; + break; + default: + debug << "QBluetoothSocket::SocketState(" << (int)state << ")"; + } + return debug; +} +#endif + +#include "moc_qbluetoothsocket.cpp" |