Revision control
Copy as Markdown
// Copyright (C) 2026 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qbluetoothsocket_ohos_p.h"
#include <QtBluetooth/qbluetoothdeviceinfo.h>
#include <QtBluetooth/qbluetoothsocket.h>
#include <QtBluetooth/private/qohosbluetoothlocaldevice_p.h>
#include <QtBluetooth/private/qohosbluetoothsocket_p.h>
#include <QtCore/q26numeric.h>
#include <QtCore/qloggingcategory.h>
#include <cstring>
QT_BEGIN_NAMESPACE
using QtOhosBluetooth::QOhosBluetoothErrorCode;
Q_DECLARE_LOGGING_CATEGORY(QT_BT_OHOS)
namespace {
struct SocketErrorReport
{
QBluetoothSocket::SocketError error = QBluetoothSocket::SocketError::UnknownSocketError;
QString message;
};
SocketErrorReport makeSocketErrorReport(QOhosBluetoothErrorCode errorCode)
{
switch (errorCode) {
case QOhosBluetoothErrorCode::PermissionDenied:
return {
.error = QBluetoothSocket::SocketError::MissingPermissionsError,
.message = QBluetoothSocket::tr("Missing permissions for socket operation")};
case QOhosBluetoothErrorCode::InvalidParameter:
return {
.error = QBluetoothSocket::SocketError::OperationError,
.message = QBluetoothSocket::tr("Socket operation rejected by the system")};
case QOhosBluetoothErrorCode::CapabilityNotSupported:
case QOhosBluetoothErrorCode::ProfileNotSupported:
return {
.error = QBluetoothSocket::SocketError::UnsupportedProtocolError,
.message = QBluetoothSocket::tr("Socket type not supported")};
case QOhosBluetoothErrorCode::InputOutput:
return {
.error = QBluetoothSocket::SocketError::UnknownSocketError,
.message = QBluetoothSocket::tr("I/O error on socket")};
case QOhosBluetoothErrorCode::ServiceStopped:
case QOhosBluetoothErrorCode::BluetoothDisabled:
return {
.error = QBluetoothSocket::SocketError::NetworkError,
.message = QBluetoothSocket::tr("Bluetooth is not available")};
case QOhosBluetoothErrorCode::UserDidNotRespond:
case QOhosBluetoothErrorCode::UserRefused:
case QOhosBluetoothErrorCode::OperationFailed:
break;
}
return {
.error = QBluetoothSocket::SocketError::OperationError,
.message = QBluetoothSocket::tr("Operation failed on socket")};
}
}
QBluetoothSocketPrivateOhos::QBluetoothSocketPrivateOhos(QBluetoothSocket *qq)
: QBluetoothSocketBasePrivate{qq}
{
secFlags = QBluetooth::Security::Secure;
QObject::connect(
&m_bluetoothSocketProxy, &QtOhosBluetooth::QOhosBluetoothSocketProxy::connected,
this,
[this](int socketDescriptor) {
if (state != QBluetoothSocket::SocketState::ConnectingState)
return;
socket = socketDescriptor;
q_ptr->setOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered);
q_ptr->setSocketState(QBluetoothSocket::SocketState::ConnectedState);
if (!rxBuffer.isEmpty())
Q_EMIT q_ptr->readyRead();
});
QObject::connect(
&m_bluetoothSocketProxy, &QtOhosBluetooth::QOhosBluetoothSocketProxy::disconnected,
this,
[this]() {
onProxyDisconnected();
});
QObject::connect(
&m_bluetoothSocketProxy, &QtOhosBluetooth::QOhosBluetoothSocketProxy::dataReceived,
this,
[this](const QByteArray &receivedData) {
if (state != QBluetoothSocket::SocketState::ConnectingState
&& state != QBluetoothSocket::SocketState::ConnectedState) {
return;
}
char *target = rxBuffer.reserve(receivedData.size());
std::memcpy(target, receivedData.constData(), receivedData.size());
if (state == QBluetoothSocket::SocketState::ConnectedState)
Q_EMIT q_ptr->readyRead();
});
QObject::connect(
&m_bluetoothSocketProxy, &QtOhosBluetooth::QOhosBluetoothSocketProxy::errorOccurred,
this,
[this](QOhosBluetoothErrorCode errorCode) {
onProxyError(errorCode);
});
}
QBluetoothSocketPrivateOhos::~QBluetoothSocketPrivateOhos()
{
abort();
}
void QBluetoothSocketPrivateOhos::onProxyError(QOhosBluetoothErrorCode errorCode)
{
const auto report = makeSocketErrorReport(errorCode);
errorString = report.message;
q_ptr->setSocketError(report.error);
if (state == QBluetoothSocket::SocketState::ConnectingState
|| state == QBluetoothSocket::SocketState::ClosingState) {
socket = QtOhosBluetooth::invalidSocketDescriptor;
q_ptr->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
}
}
void QBluetoothSocketPrivateOhos::onProxyDisconnected()
{
socket = QtOhosBluetooth::invalidSocketDescriptor;
q_ptr->setOpenMode(QIODevice::NotOpen);
q_ptr->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
Q_EMIT q_ptr->readChannelFinished();
}
bool QBluetoothSocketPrivateOhos::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
{
socketType = type;
return type == QBluetoothServiceInfo::RfcommProtocol;
}
void QBluetoothSocketPrivateOhos::connectToServiceHelper(
const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
{
if (openMode != QIODevice::ReadWrite)
qCWarning(QT_BT_OHOS, "%s: Only read-write socket mode supported.", Q_FUNC_INFO);
if (state != QBluetoothSocket::SocketState::UnconnectedState) {
qCWarning(QT_BT_OHOS, "%s: Called on busy socket", Q_FUNC_INFO);
errorString = QBluetoothSocket::tr("Trying to connect while connection is established");
q_ptr->setSocketError(QBluetoothSocket::SocketError::OperationError);
return;
}
rxBuffer.clear();
q_ptr->setSocketState(QBluetoothSocket::SocketState::ConnectingState);
const auto secure = (secFlags != QBluetooth::Security::NoSecurity);
const auto connectRequested = m_bluetoothSocketProxy.connect(
address.toString(), uuid.toString(QUuid::WithoutBraces), secure);
if (!connectRequested)
q_ptr->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
}
void QBluetoothSocketPrivateOhos::connectToService(
const QBluetoothServiceInfo &serviceInfo, QIODevice::OpenMode openMode)
{
const auto protocol = serviceInfo.socketProtocol() != QBluetoothServiceInfo::UnknownProtocol
? serviceInfo.socketProtocol()
: q_ptr->socketType();
if (!ensureNativeSocket(protocol)) {
qCWarning(QT_BT_OHOS, "%s: Only RfcommProtocol supported", Q_FUNC_INFO);
errorString = QBluetoothSocket::tr("Socket type not supported");
q_ptr->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
return;
}
connectToServiceHelper(serviceInfo.device().address(), serviceInfo.serviceUuid(), openMode);
}
void QBluetoothSocketPrivateOhos::connectToService(
const QBluetoothAddress &address, const QBluetoothUuid &uuid, QIODevice::OpenMode openMode)
{
if (!ensureNativeSocket(q_ptr->socketType())) {
qCWarning(QT_BT_OHOS, "%s: Only RfcommProtocol supported", Q_FUNC_INFO);
errorString = QBluetoothSocket::tr("Socket type not supported");
q_ptr->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
return;
}
connectToServiceHelper(address, uuid, openMode);
}
void QBluetoothSocketPrivateOhos::connectToService(
const QBluetoothAddress &, quint16, QIODevice::OpenMode)
{
qCWarning(
QT_BT_OHOS, "%s: HarmonyOS connects to a service by its UUID, not by an RFCOMM port",
Q_FUNC_INFO);
errorString = QBluetoothSocket::tr("Connecting to a port is not supported");
q_ptr->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
}
void QBluetoothSocketPrivateOhos::abort()
{
close();
}
QString QBluetoothSocketPrivateOhos::localName() const
{
const auto optLocalDeviceName =
QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->tryGetLocalDeviceName();
return optLocalDeviceName
? *optLocalDeviceName
: QString();
}
QBluetoothAddress QBluetoothSocketPrivateOhos::localAddress() const
{
return {};
}
quint16 QBluetoothSocketPrivateOhos::localPort() const
{
return 0;
}
QString QBluetoothSocketPrivateOhos::peerName() const
{
const auto optName = m_bluetoothSocketProxy.tryGetPeerDeviceName();
return optName
? *optName
: QString();
}
QBluetoothAddress QBluetoothSocketPrivateOhos::peerAddress() const
{
const auto optAddress = m_bluetoothSocketProxy.tryGetPeerDeviceAddress();
return optAddress
? QBluetoothAddress(*optAddress)
: QBluetoothAddress();
}
quint16 QBluetoothSocketPrivateOhos::peerPort() const
{
return 0;
}
qint64 QBluetoothSocketPrivateOhos::writeData(const char *data, qint64 maxSize)
{
if (state != QBluetoothSocket::SocketState::ConnectedState) {
errorString = QBluetoothSocket::tr("No SPP connection between devices. Cannot write.");
q_ptr->setSocketError(QBluetoothSocket::SocketError::OperationError);
return -1;
}
if (!m_bluetoothSocketProxy.write(data, maxSize))
return -1;
QMetaObject::invokeMethod(
q_ptr,
[this, maxSize]() {
Q_EMIT q_ptr->bytesWritten(maxSize);
},
Qt::QueuedConnection);
return maxSize;
}
qint64 QBluetoothSocketPrivateOhos::readData(char *data, qint64 maxSize)
{
if (!rxBuffer.isEmpty())
return rxBuffer.read(data, q26::saturate_cast<qsizetype>(maxSize));
if (state != QBluetoothSocket::SocketState::ConnectedState) {
errorString = QBluetoothSocket::tr("Cannot read while not connected");
q_ptr->setSocketError(QBluetoothSocket::SocketError::OperationError);
return -1;
}
return 0;
}
void QBluetoothSocketPrivateOhos::close()
{
m_bluetoothSocketProxy.close();
}
bool QBluetoothSocketPrivateOhos::setSocketDescriptor(
int socketDescriptor, QBluetoothServiceInfo::Protocol type,
QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
{
if (openMode != QIODevice::ReadWrite)
qCWarning(QT_BT_OHOS, "%s: Only read-write socket mode supported.", Q_FUNC_INFO);
if (!ensureNativeSocket(type)) {
qCWarning(QT_BT_OHOS, "%s: Only RfcommProtocol supported", Q_FUNC_INFO);
errorString = QBluetoothSocket::tr("Socket type not supported");
q_ptr->setSocketError(QBluetoothSocket::SocketError::UnsupportedProtocolError);
return false;
}
if (socketState != QBluetoothSocket::SocketState::ConnectedState) {
qCWarning(
QT_BT_OHOS, "%s: a socket accepted by the server can only be taken over as connected",
Q_FUNC_INFO);
errorString = QBluetoothSocket::tr("Taking over the socket failed");
q_ptr->setSocketError(QBluetoothSocket::SocketError::OperationError);
return false;
}
if (state != QBluetoothSocket::SocketState::UnconnectedState
|| socket != QtOhosBluetooth::invalidSocketDescriptor) {
qCWarning(QT_BT_OHOS, "%s: Called on busy socket", Q_FUNC_INFO);
errorString = QBluetoothSocket::tr("Trying to connect while connection is in progress");
q_ptr->setSocketError(QBluetoothSocket::SocketError::OperationError);
return false;
}
auto socketHandle =
QtOhosBluetooth::QOhosBluetoothSocketProxy::takePendingSocketHandle(socketDescriptor);
if (!socketHandle) {
qCWarning(
QT_BT_OHOS, "%s: no socket accepted by the server for descriptor %d", Q_FUNC_INFO,
socketDescriptor);
errorString = QBluetoothSocket::tr("Taking over the socket failed");
q_ptr->setSocketError(QBluetoothSocket::SocketError::UnknownSocketError);
q_ptr->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
return false;
}
rxBuffer.clear();
if (!m_bluetoothSocketProxy.takeOverSocket(std::move(socketHandle))) {
errorString = QBluetoothSocket::tr("Obtaining data channel for service failed");
q_ptr->setSocketError(QBluetoothSocket::SocketError::NetworkError);
q_ptr->setSocketState(QBluetoothSocket::SocketState::UnconnectedState);
return false;
}
socket = socketDescriptor;
q_ptr->setOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered);
q_ptr->setSocketState(socketState);
return true;
}
qint64 QBluetoothSocketPrivateOhos::bytesAvailable() const
{
return rxBuffer.size();
}
bool QBluetoothSocketPrivateOhos::canReadLine() const
{
return rxBuffer.canReadLine();
}
qint64 QBluetoothSocketPrivateOhos::bytesToWrite() const
{
return 0;
}
QT_END_NAMESPACE