6#include <QtBluetooth/private/qohosbluetoothaccess_p.h>
7#include <QtBluetooth/private/qohosbluetoothcommon_p.h>
8#include <QtBluetooth/private/qohosbluetoothenums_p.h>
9#include <QtBluetooth/private/qohosbluetoothremotedevice_p.h>
11#include <QtCore/q26numeric.h>
12#include <QtCore/qloggingcategory.h>
13#include <QtCore/private/qcore_ohos_p.h>
14#include <QtCore/private/qnapi_p.h>
15#include <QtCore/private/qohosjstools_p.h>
16#include <QtCore/private/qohoslogger_p.h>
32std::shared_ptr<
int> makeSppSocketHandle(
int socketDescriptor,
const char *closeCallExpression)
34 return QtOhos::makeSharedPtrWithAttachedExtraData(
35 std::make_shared<
int>(socketDescriptor),
36 QtOhos::makeDestroyNotifier(
37 [socketDescriptor, closeCallExpression]() {
38 QOhosJsThreadGateway::invoke(
39 [socketDescriptor, closeCallExpression](QOhosJsState &jsState) {
41 jsState.eval(closeCallExpression, {socketDescriptor});
42 }
catch (
const Napi::Error &error) {
43 const auto errorCode =
44 tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)
45 .value_or(QOhosBluetoothErrorCode::OperationFailed);
47 "%s: cannot close the socket %d with %s, error code: %u",
48 Q_FUNC_INFO, socketDescriptor, closeCallExpression,
49 qToUnderlying(errorCode));
59 return makeSppSocketHandle(socketDescriptor,
"@ohos.bluetooth.socket.sppCloseClientSocket(*)");
64 return makeSppSocketHandle(socketDescriptor,
"@ohos.bluetooth.socket.sppCloseServerSocket(*)");
69using SppType = QtOhosBluetooth::enums::ohos::bluetooth::socket::SppType;
71std::shared_ptr<
void> registerSocketSppReadListener(
72 QOhosJsState &jsState,
int socketDescriptor, QOhosConsumer<QByteArray> sppReadListener,
73 QOhosConsumer<QOhosBluetoothErrorCode> errorCodeConsumer)
75 return registerQOhosOnOffMethodsBasedEventHandler(
76 jsState.eval<QNapi::Object>(
"@ohos.bluetooth.socket"),
"sppRead",
77 [sppReadListener = std::move(sppReadListener)](
const QOhosCallbackInfo &cbInfo) {
78 auto arrayBuffer = cbInfo.getFirstArg<QNapi::ArrayBuffer>(Q_FUNC_INFO);
81 static_cast<
const char *>(arrayBuffer.Data()),
82 static_cast<qsizetype>(arrayBuffer.ByteLength())));
85 .extraOnArg =
std::make_optional(QNapi::ValueWrapper(socketDescriptor)),
86 .extraOffArg =
std::make_optional(QNapi::ValueWrapper(socketDescriptor)),
87 .optOnCallExceptionHandler =
88 [errorCodeConsumer = std::move(errorCodeConsumer)](
const Napi::Error &error) {
90 tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)
91 .value_or(QOhosBluetoothErrorCode::OperationFailed));
96std::shared_ptr<
void> registerSocketSppReadHandler(
97 int socketDescriptor, QOhosConsumer<QByteArray> receivedDataConsumer,
98 QOhosConsumer<QOhosBluetoothErrorCode> errorCodeConsumer)
100 auto sharedReceivedDataConsumer = QtOhos::moveToSharedPtr(std::move(receivedDataConsumer));
101 auto weakReceivedDataConsumer = QtOhos::makeWeakPtr(sharedReceivedDataConsumer);
103 auto socketSppReadHandler = QOhosJsThreadGateway::eval(
104 [&](QOhosJsState &jsState) {
105 return registerSocketSppReadListener(
106 jsState, socketDescriptor,
107 [weakReceivedDataConsumer](QByteArray receivedData) {
108 QtOhos::invokeInQtThread(
109 [weakReceivedDataConsumer, receivedData = std::move(receivedData)]() {
110 auto receivedDataConsumer = weakReceivedDataConsumer.lock();
111 if (receivedDataConsumer)
112 (*receivedDataConsumer)(std::move(receivedData));
115 std::move(errorCodeConsumer));
119 if (!socketSppReadHandler)
122 return QtOhos::makeSharedPtrWithAttachedExtraData(
123 socketSppReadHandler, sharedReceivedDataConsumer);
126std::optional<
bool> tryReadSocketConnectedState(QOhosJsState &jsState,
int socketDescriptor)
130 .eval<QNapi::Boolean>(
131 "@ohos.bluetooth.socket.isConnected(*)", {socketDescriptor})
133 }
catch (
const Napi::Error &error) {
134 const auto errorCode =
135 tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)
136 .value_or(QOhosBluetoothErrorCode::OperationFailed);
138 "%s: cannot read the socket connected state, error code: %u", Q_FUNC_INFO,
139 qToUnderlying(errorCode));
144std::optional<
std::string> tryGetDeviceIdFromSocket(
int socket)
149 return QOhosJsThreadGateway::eval(
150 [&](QOhosJsState &jsState) -> std::optional<std::string> {
152 return jsState.eval<QNapi::String>(
153 "@ohos.bluetooth.socket.getDeviceId(*)", {socket});
154 }
catch (
const Napi::Error &error) {
155 if (
const auto optErrorCode = tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)) {
157 "%s: cannot read the device id of the socket %d, error code: %u",
158 Q_FUNC_INFO, socket, qToUnderlying(*optErrorCode));
170 QOhosBluetoothAccessProxy::instance().get(),
171 &QOhosBluetoothAccessProxy::stateChanged,
173 [
this](QOhosBluetoothAccessProxy::BluetoothState bluetoothState) {
174 if (bluetoothState == QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
177 if (m_pendingConnectRequestId) {
179 QT_BT_OHOS,
"%s: bluetooth is turned off. Aborting the connect request ...",
182 *m_pendingConnectRequestId, QOhosBluetoothErrorCode::BluetoothDisabled);
190 QT_BT_OHOS,
"%s: bluetooth is turned off. Closing the socket ...", Q_FUNC_INFO);
191 reportError(QOhosBluetoothErrorCode::BluetoothDisabled);
198 static std::unordered_map<
int, std::shared_ptr<
int>> handles;
205 qCCritical(QT_BT_OHOS,
"%s: no socket handle to register", Q_FUNC_INFO);
209 const auto socketDescriptor = *socketHandle;
211 auto &handles = pendingSocketHandles();
212 if (handles.find(socketDescriptor) != handles.end()) {
215 "%s: socket descriptor %d is already registered as pending. Rejecting the socket ...",
216 Q_FUNC_INFO, socketDescriptor);
220 handles[socketDescriptor] =
std::move(socketHandle);
227 pendingSocketHandles().erase(socketDescriptor);
232 auto &handles = pendingSocketHandles();
233 auto found = handles.find(socketDescriptor);
234 if (found == handles.end())
237 auto socketHandle =
std::move(found->second);
238 handles.erase(found);
244 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO)) {
245 reportError(QOhosBluetoothErrorCode::PermissionDenied);
249 const auto connectRequestId = ++m_connectRequestsCounter;
250 m_pendingConnectRequestId = connectRequestId;
252 auto selfRef = QtOhos::makeQThreadSafeRef(
this);
253 auto optConnectErrorCode = QOhosJsThreadGateway::eval(
254 [&](QOhosJsState &jsState) -> std::optional<QOhosBluetoothErrorCode> {
255 auto sppOptions = QNapi::makeObject(
258 {
"uuid", uuid.toStdString()},
260 {
"type", jsState.mapOhosEnumToJs(SppType::SPP_RFCOMM)},
265 "@ohos.bluetooth.socket.sppConnect(*)",
267 deviceId.toStdString(),
269 [selfRef, connectRequestId](
const QNapi::CallbackInfo &callbackInfo) {
272 callbackInfo.getLeadingArgs(Q_FUNC_INFO, error, data);
273 auto socketId = data.IsNumber()
274 ? QNapi::checkedCast<QNapi::Number>(data).Int32Value()
275 : invalidSocketDescriptor;
277 if (socketId == invalidSocketDescriptor) {
278 const auto errorCode =
279 tryGetKnownErrorCode(error, Q_FUNC_INFO)
280 .value_or(QOhosBluetoothErrorCode::OperationFailed);
282 "%s: sppConnect() failed with error code: %u", Q_FUNC_INFO,
283 qToUnderlying(errorCode));
284 selfRef.visitInQtThreadIfAlive(
285 [connectRequestId, errorCode](
auto &self) {
286 self.onConnectFailed(connectRequestId, errorCode);
291 auto socketHandle = makeSppClientSocketHandle(socketId);
293 selfRef.visitInQtThreadIfAlive(
294 [connectRequestId, socketHandle](
auto &self) {
295 self.onSocketConnected(connectRequestId, socketHandle);
300 }
catch (
const Napi::Error &error) {
301 return tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO);
305 if (optConnectErrorCode) {
307 QT_BT_OHOS,
"%s: sppConnect() failed with error code: %u", Q_FUNC_INFO,
308 qToUnderlying(*optConnectErrorCode));
309 onConnectFailed(connectRequestId, *optConnectErrorCode);
313 m_deviceId = deviceId.toStdString();
319 std::uint64_t connectRequestId,
std::shared_ptr<
int> socketHandle)
321 if (m_pendingConnectRequestId != connectRequestId) {
324 "%s: the connection has been closed before it was established. Letting the socket "
325 "handle close the socket ...",
330 m_pendingConnectRequestId.reset();
334 reportError(QOhosBluetoothErrorCode::OperationFailed);
339 std::uint64_t connectRequestId, QOhosBluetoothErrorCode errorCode)
341 if (m_pendingConnectRequestId != connectRequestId)
344 m_pendingConnectRequestId.reset();
347 reportError(errorCode);
353 qCCritical(QT_BT_OHOS,
"%s: no socket handle to take over", Q_FUNC_INFO);
357 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO))
360 auto selfRef = QtOhos::makeQThreadSafeRef(
this);
361 auto optSubscriptionErrorCode = std::make_shared<std::optional<QOhosBluetoothErrorCode>>();
363 auto sppReadHandlerHandle = registerSocketSppReadHandler(
365 [selfRef](QByteArray receivedData) {
366 if (receivedData.isEmpty())
369 selfRef.visitInQtThreadIfAlive(
370 [receivedData = std::move(receivedData)](
auto &self) {
371 Q_EMIT self.dataReceived(receivedData);
374 [optSubscriptionErrorCode](QOhosBluetoothErrorCode errorCode) {
375 *optSubscriptionErrorCode = errorCode;
378 if (!sppReadHandlerHandle) {
381 "%s: cannot register the SPP read handler, error code: %u. Socket is not connected.",
384 optSubscriptionErrorCode->value_or(QOhosBluetoothErrorCode::OperationFailed)));
388 m_jsScopeData = QtOhos::makeProxyWithJsThreadDeleter(
389 QtOhos::moveToSharedPtr(
391 .socketHandle = socketHandle,
392 .sppReadHandlerHandle = std::move(sppReadHandlerHandle),
395 const auto socketDescriptor = *socketHandle;
396 QMetaObject::invokeMethod(
398 [
this, socketDescriptor]() {
399 Q_EMIT connected(socketDescriptor);
401 Qt::QueuedConnection);
408 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO)) {
409 reportError(QOhosBluetoothErrorCode::PermissionDenied);
413 if (!m_jsScopeData) {
414 qCWarning(QT_BT_OHOS,
"%s: the socket is already closed", Q_FUNC_INFO);
415 reportError(QOhosBluetoothErrorCode::OperationFailed);
419 const auto socketDescriptor = *m_jsScopeData->socketHandle;
423 std::optional<QOhosBluetoothErrorCode> optErrorCode = std::nullopt;
424 bool socketStillConnected =
true;
427 const auto writeSize = q26::saturate_cast<std::size_t>(maxSize);
428 const auto writeOutcome = QOhosJsThreadGateway::eval(
429 [&](QOhosJsState &jsState) -> WriteOutcome {
431 auto arrayBuffer = QNapi::ArrayBuffer::New(jsState.env(), writeSize);
432 std::memcpy(arrayBuffer.Data(), data, writeSize);
433 jsState.eval(
"@ohos.bluetooth.socket.sppWrite(*)", {socketDescriptor, arrayBuffer});
435 }
catch (
const Napi::Error &error) {
437 .optErrorCode = tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO),
438 .socketStillConnected =
439 tryReadSocketConnectedState(jsState, socketDescriptor).value_or(
true),
444 if (!writeOutcome.optErrorCode)
447 reportError(*writeOutcome.optErrorCode);
449 if (!writeOutcome.socketStillConnected) {
451 QT_BT_OHOS,
"%s: the peer is no longer connected. Closing the socket ...", Q_FUNC_INFO);
460 if (!m_jsScopeData && !m_pendingConnectRequestId)
463 m_jsScopeData.reset();
464 m_pendingConnectRequestId.reset();
467 QMetaObject::invokeMethod(
this, &QOhosBluetoothSocketProxy::disconnected, Qt::QueuedConnection);
472 const auto peerDeviceIdFromSocket = m_jsScopeData
473 ? tryGetDeviceIdFromSocket(*m_jsScopeData->socketHandle)
474 : std::optional<std::string>();
475 const auto &optPeerDeviceId = peerDeviceIdFromSocket
476 ? peerDeviceIdFromSocket
479 return optPeerDeviceId
480 ? std::optional(QString::fromStdString(*optPeerDeviceId))
486 const auto deviceId = tryGetPeerDeviceAddress();
490 auto remoteDeviceProxy = QOhosBluetoothRemoteDeviceProxy::instance();
491 return remoteDeviceProxy->tryGetRemoteDeviceName(*deviceId);
496 QMetaObject::invokeMethod(
498 [
this, errorCode]() {
499 Q_EMIT errorOccurred(errorCode);
501 Qt::QueuedConnection);
508#include "moc_qohosbluetoothsocket_p.cpp"
bool connect(const QString &deviceId, const QString &uuid, bool secure)
std::optional< QString > tryGetPeerDeviceAddress() const
bool write(const char *data, qint64 maxSize)
static void dropPendingSocketHandle(int socketDescriptor)
static std::shared_ptr< int > takePendingSocketHandle(int socketDescriptor)
std::optional< QString > tryGetPeerDeviceName() const
bool takeOverSocket(std::shared_ptr< int > socketHandle)
static bool registerPendingSocketHandle(std::shared_ptr< int > socketHandle)
constexpr int invalidSocketDescriptor
std::shared_ptr< int > makeSppClientSocketHandle(int socketDescriptor)
std::shared_ptr< int > makeSppServerSocketHandle(int socketDescriptor)
QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(lcQIORing)