summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorEric Lemanissier <eric.lemanissier@gmail.com>2018-07-02 17:09:26 +0200
committerEric Lemanissier <eric.lemanissier@gmail.com>2018-07-26 20:25:20 +0000
commit2a382272a4b6891d4603e1cb9f2d357b374d7a74 (patch)
tree8f4f31ebc063ee8c8d311eeb2725f8b816655f7b /src
parent6979c889ebd370b673d94907be766c6ef76ca7f4 (diff)
win32: implement QBluetoothSocket
Change-Id: Ib2b33490694c8608edda3eb0cbe7b8d0dd233254 Reviewed-by: Lubomir I. Ivanov <neolit123@gmail.com> Reviewed-by: Denis Shienkov <denis.shienkov@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/bluetooth/qbluetoothsocket_win.cpp370
-rw-r--r--src/bluetooth/qbluetoothsocket_win_p.h16
2 files changed, 358 insertions, 28 deletions
diff --git a/src/bluetooth/qbluetoothsocket_win.cpp b/src/bluetooth/qbluetoothsocket_win.cpp
index f7e0e2d0..14c780d1 100644
--- a/src/bluetooth/qbluetoothsocket_win.cpp
+++ b/src/bluetooth/qbluetoothsocket_win.cpp
@@ -43,6 +43,12 @@
#include <QtCore/qloggingcategory.h>
#include <QtBluetooth/qbluetoothdeviceinfo.h>
+#include <QtBluetooth/QBluetoothLocalDevice>
+#include <QtCore/QSocketNotifier>
+
+#include <winsock2.h>
+#include <ws2bth.h>
+#include <bluetoothapis.h>
QT_BEGIN_NAMESPACE
@@ -55,19 +61,85 @@ QBluetoothSocketPrivateWin::QBluetoothSocketPrivateWin()
QBluetoothSocketPrivateWin::~QBluetoothSocketPrivateWin()
{
+ abort();
}
bool QBluetoothSocketPrivateWin::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
{
+ Q_Q(QBluetoothSocket);
+
+ if (socket != INVALID_SOCKET) {
+ if (socketType == type)
+ return true;
+ abort();
+ }
+
socketType = type;
- return false;
+
+ switch (type) {
+ case QBluetoothServiceInfo::RfcommProtocol:
+ socket = ::socket(AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM);
+ break;
+ default:
+ socket = INVALID_SOCKET;
+ errorString = QBluetoothSocket::tr("Unsupported protocol. Win32 only supports RFCOMM sockets");
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return false;
+ }
+
+ if (socket == INVALID_SOCKET) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to create socket:" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Failed to create socket");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+
+ if (!createNotifiers())
+ return false;
+
+ return true;
}
void QBluetoothSocketPrivateWin::connectToServiceHelper(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
{
- Q_UNUSED(openMode);
- Q_UNUSED(address);
- Q_UNUSED(port);
+ Q_Q(QBluetoothSocket);
+
+ if (socket == INVALID_SOCKET && !ensureNativeSocket(socketType))
+ return;
+
+ if (!configureSecurity())
+ return;
+
+ SOCKADDR_BTH addr = {0};
+ addr.addressFamily = AF_BTH;
+ addr.port = port;
+ addr.btAddr = address.toUInt64();
+
+ switch (socketType) {
+ case QBluetoothServiceInfo::RfcommProtocol:
+ addr.serviceClassId = RFCOMM_PROTOCOL_UUID;
+ break;
+ default:
+ errorString = QBluetoothSocket::tr("Socket type not handled: %1").arg(socketType);
+ q->setSocketError(QBluetoothSocket::UnsupportedProtocolError);
+ return;
+ }
+
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+
+ const int result = ::connect(socket, reinterpret_cast<sockaddr *>(&addr), sizeof(addr));
+
+ const int error = ::WSAGetLastError();
+ if (result != SOCKET_ERROR || error == WSAEWOULDBLOCK) {
+ q->setSocketState(QBluetoothSocket::ConnectingState);
+ q->setOpenMode(openMode);
+ } else {
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ }
}
void QBluetoothSocketPrivateWin::connectToService(
@@ -123,6 +195,97 @@ void QBluetoothSocketPrivateWin::connectToService(
q->doDeviceDiscovery(service, openMode);
}
}
+void QBluetoothSocketPrivateWin::_q_writeNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (state == QBluetoothSocket::ConnectingState) {
+ updateAddressesAndPorts();
+ q->setSocketState(QBluetoothSocket::ConnectedState);
+ emit q->connected();
+ connectWriteNotifier->setEnabled(false);
+ } else {
+ if (txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(false);
+ return;
+ }
+
+ char buf[1024];
+ const int size = txBuffer.read(&buf[0], sizeof(buf));
+ const int writtenBytes = ::send(socket, &buf[0], size, 0);
+ if (writtenBytes == SOCKET_ERROR) {
+ // every other case returns error
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ } else if (writtenBytes <= size) {
+ // add remainder back to buffer
+ char *remainder = &buf[writtenBytes];
+ txBuffer.ungetBlock(remainder, size - writtenBytes);
+ if (writtenBytes > 0)
+ emit q->bytesWritten(writtenBytes);
+ } else {
+ errorString = QBluetoothSocket::tr("Logic error: more bytes sent than passed to ::send");
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (!txBuffer.isEmpty()) {
+ connectWriteNotifier->setEnabled(true);
+ } else if (state == QBluetoothSocket::ClosingState) {
+ connectWriteNotifier->setEnabled(false);
+ this->close();
+ }
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_readNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ const int bytesRead = ::recv(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE, 0);
+ if (bytesRead == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE);
+ readNotifier->setEnabled(false);
+ connectWriteNotifier->setEnabled(false);
+ errorString = qt_error_string(error);
+ qCWarning(QT_BT_WINDOWS) << Q_FUNC_INFO << socket << "error:" << error << errorString;
+ switch (error) {
+ case WSAEHOSTDOWN:
+ q->setSocketError(QBluetoothSocket::HostNotFoundError);
+ break;
+ case WSAECONNRESET:
+ q->setSocketError(QBluetoothSocket::RemoteHostClosedError);
+ break;
+ default:
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ break;
+ }
+
+ q->disconnectFromService();
+ } else {
+ const int unusedBytes = QPRIVATELINEARBUFFER_BUFFERSIZE - bytesRead;
+ buffer.chop(unusedBytes);
+ if (bytesRead > 0)
+ emit q->readyRead();
+ }
+}
+
+void QBluetoothSocketPrivateWin::_q_exceptNotify()
+{
+ Q_Q(QBluetoothSocket);
+
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+
+ if (state == QBluetoothSocket::ConnectingState) {
+ abort();
+ q->setSocketState(QBluetoothSocket::UnconnectedState);
+ emit q->disconnected();
+ }
+}
void QBluetoothSocketPrivateWin::connectToService(
const QBluetoothAddress &address, const QBluetoothUuid &uuid,
@@ -178,43 +341,66 @@ void QBluetoothSocketPrivateWin::connectToService(
void QBluetoothSocketPrivateWin::abort()
{
+ delete readNotifier;
+ readNotifier = nullptr;
+ delete connectWriteNotifier;
+ connectWriteNotifier = nullptr;
+ delete exceptNotifier;
+ exceptNotifier = nullptr;
+
+ m_localAddress.clear();
+ m_localPort = 0;
+ m_peerAddress.clear();
+ m_peerPort = 0;
+
+ // We don't transition through Closing for abort, so
+ // we don't call disconnectFromService or QBluetoothSocket::close
+ ::closesocket(socket);
+ socket = INVALID_SOCKET;
}
QString QBluetoothSocketPrivateWin::localName() const
{
- return QString();
+ const QBluetoothLocalDevice device(m_localAddress);
+ return device.name();
}
QBluetoothAddress QBluetoothSocketPrivateWin::localAddress() const
{
- return QBluetoothAddress();
+ return m_localAddress;
}
quint16 QBluetoothSocketPrivateWin::localPort() const
{
- return 0;
+ return m_localPort;
}
QString QBluetoothSocketPrivateWin::peerName() const
{
- return QString();
+ if (socket == INVALID_SOCKET)
+ return {};
+ BLUETOOTH_DEVICE_INFO bdi = {0};
+ bdi.dwSize = sizeof(bdi);
+ bdi.Address.ullLong = m_peerAddress.toUInt64();
+ const DWORD res = ::BluetoothGetDeviceInfo(nullptr, &bdi);
+ if (res == ERROR_SUCCESS)
+ return QString::fromWCharArray(&bdi.szName[0]);
+ qCWarning(QT_BT_WINDOWS) << "Error calling BluetoothGetDeviceInfo" << res << qt_error_string(res);
+ return {};
}
QBluetoothAddress QBluetoothSocketPrivateWin::peerAddress() const
{
- return QBluetoothAddress();
+ return m_peerAddress;
}
quint16 QBluetoothSocketPrivateWin::peerPort() const
{
- return 0;
+ return m_peerPort;
}
qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize)
{
- Q_UNUSED(data);
- Q_UNUSED(maxSize);
-
Q_Q(QBluetoothSocket);
if (state != QBluetoothSocket::ConnectedState) {
@@ -222,52 +408,180 @@ qint64 QBluetoothSocketPrivateWin::writeData(const char *data, qint64 maxSize)
q->setSocketError(QBluetoothSocket::OperationError);
return -1;
}
- return -1;
+
+ if (q->openMode() & QIODevice::Unbuffered) {
+ const int bytesWritten = ::send(socket, data, maxSize, 0);
+
+ if (bytesWritten == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = QBluetoothSocket::tr("Network Error: %1").arg(qt_error_string(error));
+ q->setSocketError(QBluetoothSocket::NetworkError);
+ }
+
+ if (bytesWritten > 0)
+ emit q->bytesWritten(bytesWritten);
+
+ return bytesWritten;
+ } else {
+
+ if (!connectWriteNotifier)
+ return -1;
+
+ if (txBuffer.isEmpty())
+ connectWriteNotifier->setEnabled(true);
+
+ char *txbuf = txBuffer.reserve(maxSize);
+ ::memcpy(txbuf, data, maxSize);
+
+ return maxSize;
+ }
}
qint64 QBluetoothSocketPrivateWin::readData(char *data, qint64 maxSize)
{
- Q_UNUSED(data);
- Q_UNUSED(maxSize);
-
Q_Q(QBluetoothSocket);
if (state != QBluetoothSocket::ConnectedState) {
- errorString = QBluetoothSocket::tr("Cannot write while not connected");
+ errorString = QBluetoothSocket::tr("Cannot read while not connected");
q->setSocketError(QBluetoothSocket::OperationError);
return -1;
}
- return -1;
+ const int bytesRead = buffer.read(data, maxSize);
+ return bytesRead;
}
void QBluetoothSocketPrivateWin::close()
{
+ if (txBuffer.isEmpty())
+ abort();
+ else
+ connectWriteNotifier->setEnabled(true);
}
-bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType,
+bool QBluetoothSocketPrivateWin::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType_,
QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
{
- Q_UNUSED(socketDescriptor);
- Q_UNUSED(socketType)
- Q_UNUSED(socketState);
- Q_UNUSED(openMode);
- return false;
+ Q_Q(QBluetoothSocket);
+
+ abort();
+
+ socketType = socketType_;
+ socket = socketDescriptor;
+
+ if (!createNotifiers())
+ return false;
+ updateAddressesAndPorts();
+ q->setSocketState(socketState);
+ q->setOpenMode(openMode);
+ if (socketState == QBluetoothSocket::ConnectedState) {
+ connectWriteNotifier->setEnabled(true);
+ readNotifier->setEnabled(true);
+ exceptNotifier->setEnabled(true);
+ }
+
+ return true;
}
qint64 QBluetoothSocketPrivateWin::bytesAvailable() const
{
- return 0;
+ return buffer.size();
}
bool QBluetoothSocketPrivateWin::canReadLine() const
{
- return false;
+ return buffer.canReadLine();
}
qint64 QBluetoothSocketPrivateWin::bytesToWrite() const
{
- return 0;
+ return txBuffer.size();
}
+bool QBluetoothSocketPrivateWin::createNotifiers()
+{
+ Q_Q(QBluetoothSocket);
+
+ ULONG mode = 1; // 1 to enable non-blocking socket
+ const int result = ::ioctlsocket(socket, FIONBIO, &mode);
+
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ errorString = qt_error_string(error);
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ qCWarning(QT_BT_WINDOWS) << "Error setting socket to non-blocking" << error << errorString;
+ abort();
+ return false;
+ }
+ readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
+ QObject::connect(readNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_readNotify);
+ connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q);
+ QObject::connect(connectWriteNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_writeNotify);
+ exceptNotifier = new QSocketNotifier(socket, QSocketNotifier::Exception, q);
+ QObject::connect(exceptNotifier, &QSocketNotifier::activated, this, &QBluetoothSocketPrivateWin::_q_exceptNotify);
+
+ connectWriteNotifier->setEnabled(false);
+ readNotifier->setEnabled(false);
+ exceptNotifier->setEnabled(false);
+ return true;
+}
+
+void QBluetoothSocketPrivateWin::updateAddressesAndPorts()
+{
+ SOCKADDR_BTH localAddr = {0};
+ int localAddrLength = sizeof(localAddr);
+ const int localResult = ::getsockname(socket, reinterpret_cast<sockaddr *>(&localAddr), &localAddrLength);
+ if (localResult != SOCKET_ERROR) {
+ m_localAddress = QBluetoothAddress(localAddr.btAddr);
+ m_localPort = localAddr.port;
+ } else {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting local address and port" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot get socket's local address and port");
+ }
+
+ SOCKADDR_BTH peerAddr = {0};
+ int peerAddrLength = sizeof(peerAddr);
+ const int peerResult = ::getpeername(socket, reinterpret_cast<sockaddr *>(&peerAddr), &peerAddrLength);
+ if (peerResult != SOCKET_ERROR) {
+ m_peerAddress = QBluetoothAddress(peerAddr.btAddr);
+ m_peerPort = peerAddr.port;
+ } else {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Error getting peer address and port" << error << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot get socket's peer address and port");
+ }
+}
+
+bool QBluetoothSocketPrivateWin::configureSecurity()
+{
+ Q_Q(QBluetoothSocket);
+
+ if (secFlags & QBluetooth::Authorization) {
+ ULONG authenticate = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_AUTHENTICATE, reinterpret_cast<const char*>(&authenticate), sizeof(authenticate));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+
+ if (secFlags & QBluetooth::Encryption) {
+ ULONG encrypt = TRUE;
+ const int result = ::setsockopt(socket, SOL_RFCOMM, SO_BTH_ENCRYPT, reinterpret_cast<const char*>(&encrypt), sizeof(encrypt));
+ if (result == SOCKET_ERROR) {
+ const int error = ::WSAGetLastError();
+ qCWarning(QT_BT_WINDOWS) << "Failed to set socket option, closing socket for safety" << error;
+ qCWarning(QT_BT_WINDOWS) << "Error: " << qt_error_string(error);
+ errorString = QBluetoothSocket::tr("Cannot set connection security level");
+ q->setSocketError(QBluetoothSocket::UnknownSocketError);
+ return false;
+ }
+ }
+ return true;
+}
QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothsocket_win_p.h b/src/bluetooth/qbluetoothsocket_win_p.h
index 3255428a..fe0fc99f 100644
--- a/src/bluetooth/qbluetoothsocket_win_p.h
+++ b/src/bluetooth/qbluetoothsocket_win_p.h
@@ -100,6 +100,22 @@ public:
qint64 bytesAvailable() const override;
bool canReadLine() const override;
qint64 bytesToWrite() const override;
+
+private slots:
+ void _q_readNotify();
+ void _q_writeNotify();
+ void _q_exceptNotify();
+
+private:
+ bool createNotifiers();
+ void updateAddressesAndPorts();
+ bool configureSecurity();
+
+ QSocketNotifier *exceptNotifier = nullptr;
+ QBluetoothAddress m_localAddress;
+ quint16 m_localPort = 0;
+ QBluetoothAddress m_peerAddress;
+ quint16 m_peerPort = 0;
};
QT_END_NAMESPACE