Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosbluetoothremotedevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtBluetooth/private/qohosbluetoothcommon_p.h>
7
8#include <QtCore/qloggingcategory.h>
9#include <QtCore/private/qcore_ohos_p.h>
10#include <QtCore/private/qnapi_p.h>
11#include <QtCore/private/qohosjstools_p.h>
12#include <QtCore/private/qohoslogger_p.h>
13
14#include <optional>
15#include <string>
16#include <vector>
17
19
20Q_DECLARE_LOGGING_CATEGORY(QT_BT_OHOS)
21
22namespace QtOhosBluetooth {
23
24namespace {
25
26std::shared_ptr<void> registerBondStateChangeConsumer(
27 QOhosJsState &jsState,
28 QOhosConsumer<std::string, std::optional<QOhosBluetoothRemoteDeviceProxy::BondState>, std::optional<QOhosBluetoothRemoteDeviceProxy::UnbondCause>> bondStateChangeConsumer)
29{
30 return registerQOhosOnOffMethodsBasedEventHandler(
31 jsState.eval<QNapi::Object>("@ohos.bluetooth.connection"), "bondStateChange",
32 [bondStateChangeConsumer = std::move(bondStateChangeConsumer)](const QOhosCallbackInfo &cbInfo) {
33 auto bondStateObject = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
34
35 auto deviceId = bondStateObject.get<QNapi::String>("deviceId").Utf8Value();
36 const auto optBondState = cbInfo.jsState().tryMapOhosEnumFromJs<QOhosBluetoothRemoteDeviceProxy::BondState>(
37 bondStateObject.get<QNapi::Number>("state"));
38 const auto unbondCauseValue =
39 QNapi::getOptionalPropOrEmpty<QNapi::Number>(bondStateObject, "cause");
40 const auto optUnbondCause = unbondCauseValue.IsEmpty()
41 ? std::optional<QOhosBluetoothRemoteDeviceProxy::UnbondCause>()
42 : cbInfo.jsState().tryMapOhosEnumFromJs<QOhosBluetoothRemoteDeviceProxy::UnbondCause>(
43 unbondCauseValue);
44
45 bondStateChangeConsumer(deviceId, optBondState, optUnbondCause);
46 },
47 {
48 .optOnCallExceptionHandler = [](const Napi::Error &error) {
49 if (const auto optErrorCode = tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)) {
50 qOhosPrintfWarning(
51 "%s: cannot register the bond state change consumer, error code: %u",
52 Q_FUNC_INFO, qToUnderlying(*optErrorCode));
53 }
54 },
55 });
56}
57
58std::optional<QOhosBluetoothRemoteDeviceProxy::BondState> readPairState(
59 QOhosJsState &jsState, const std::string &deviceId)
60{
61 if (!isBluetoothEnabled(jsState))
62 return std::nullopt;
63
64 try {
65 return jsState.tryMapOhosEnumFromJs<QOhosBluetoothRemoteDeviceProxy::BondState>(
66 jsState.eval<QNapi::Number>(
67 "@ohos.bluetooth.connection.getPairState(*)", {deviceId}));
68 } catch (const Napi::Error &error) {
69 if (const auto optErrorCode = tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)) {
70 qOhosPrintfWarning(
71 "%s: cannot read the pair state of the device %s, error code: %u", Q_FUNC_INFO,
72 deviceId.c_str(), qToUnderlying(*optErrorCode));
73 }
74 return std::nullopt;
75 }
76}
77
78}
79
80std::optional<std::string> readRemoteDeviceName(QOhosJsState &jsState, const std::string &deviceId)
81{
82 try {
83 return jsState.eval<QNapi::String>(
84 "@ohos.bluetooth.connection.getRemoteDeviceName(*)", {deviceId}).Utf8Value();
85 } catch (const Napi::Error &error) {
86 if (const auto optErrorCode = tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)) {
87 qOhosPrintfWarning(
88 "%s: cannot read the name of the device %s, error code: %u", Q_FUNC_INFO,
89 deviceId.c_str(), qToUnderlying(*optErrorCode));
90 }
91 return std::nullopt;
92 }
93}
94
95std::optional<quint32> readRemoteDeviceClass(QOhosJsState &jsState, const std::string &deviceId)
96{
97 try {
98 return jsState.eval<QNapi::Object>(
99 "@ohos.bluetooth.connection.getRemoteDeviceClass(*)", {deviceId})
100 .get<QNapi::Number>("classOfDevice").Uint32Value();
101 } catch (const Napi::Error &error) {
102 if (const auto optErrorCode = tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)) {
103 qOhosPrintfWarning(
104 "%s: cannot read the class of the device %s, error code: %u", Q_FUNC_INFO,
105 deviceId.c_str(), qToUnderlying(*optErrorCode));
106 }
107 return std::nullopt;
108 }
109}
110
112 QOhosJsState &jsState, const std::string &deviceId)
113{
114 try {
115 return jsState.tryMapOhosEnumFromJs<QOhosBluetoothRemoteDeviceProxy::BluetoothTransport>(
116 jsState.eval<QNapi::Number>(
117 "@ohos.bluetooth.connection.getRemoteDeviceTransport(*)", {deviceId}));
118 } catch (const Napi::Error &error) {
119 if (const auto optErrorCode = tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)) {
120 qOhosPrintfWarning(
121 "%s: cannot read the transport of the device %s, error code: %u", Q_FUNC_INFO,
122 deviceId.c_str(), qToUnderlying(*optErrorCode));
123 }
124 return std::nullopt;
125 }
126}
127
128bool isRemoteDeviceLowEnergyOnly(QOhosJsState &jsState, const std::string &deviceId)
129{
130 return readRemoteDeviceTransport(jsState, deviceId)
131 == QOhosBluetoothRemoteDeviceProxy::BluetoothTransport::TRANSPORT_LE;
132}
133
135{
136 ensureBondStateChangeConsumerRegistered();
137}
138
139bool QOhosBluetoothRemoteDeviceProxy::ensureBondStateChangeConsumerRegistered()
140{
141 if (m_bondStateChangeConsumerHandle)
142 return true;
143
144 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO))
145 return false;
146
147 auto selfRef = QtOhos::makeQThreadSafeRef(this);
148 m_bondStateChangeConsumerHandle = QOhosJsThreadGateway::eval(
149 [&](QOhosJsState &jsState) -> std::shared_ptr<void> {
150 auto bondStateChangeConsumerHandle = registerBondStateChangeConsumer(
151 jsState,
152 [selfRef](std::string deviceId, std::optional<BondState> optBondState, std::optional<UnbondCause> optUnbondCause) {
153 if (!optBondState)
154 return;
155
156 selfRef.visitInQtThreadIfAlive(
157 [deviceId = QString::fromStdString(deviceId), bondState = *optBondState, optUnbondCause](auto &self) {
158 Q_EMIT self.bondStateChanged(deviceId, bondState, optUnbondCause);
159 });
160 });
161
162 if (!bondStateChangeConsumerHandle)
163 return {};
164
165 return QtOhos::makeProxyWithJsThreadDeleter(std::move(bondStateChangeConsumerHandle));
166 },
167 Q_FUNC_INFO);
168
169 return m_bondStateChangeConsumerHandle != nullptr;
170}
171
173{
174 static std::weak_ptr<QOhosBluetoothRemoteDeviceProxy> weakInstance;
175
176 auto instance = weakInstance.lock();
177 if (!instance) {
178 instance = std::shared_ptr<QOhosBluetoothRemoteDeviceProxy>(
180 weakInstance = instance;
181 }
182
183 return instance;
184}
185
187{
188 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO))
189 return std::nullopt;
190
191 return QOhosJsThreadGateway::eval(
192 [&](QOhosJsState &jsState) -> std::optional<QString> {
193 if (!isBluetoothEnabled(jsState))
194 return std::nullopt;
195
196 const auto optRemoteDeviceName = readRemoteDeviceName(jsState, deviceId.toStdString());
197
198 return optRemoteDeviceName
199 ? std::optional(QString::fromStdString(*optRemoteDeviceName))
200 : std::nullopt;
201 },
202 Q_FUNC_INFO);
203}
204
206 const QString &deviceId)
207{
208 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO))
209 return std::nullopt;
210
211 return QOhosJsThreadGateway::eval(
212 [&](QOhosJsState &jsState) {
213 return readPairState(jsState, deviceId.toStdString());
214 },
215 Q_FUNC_INFO);
216}
217
219 const QString &deviceId)
220{
221 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO))
222 return QOhosBluetoothErrorCode::PermissionDenied;
223
224 auto selfRef = QtOhos::makeQThreadSafeRef(this);
225 return QOhosJsThreadGateway::eval(
226 [&](QOhosJsState &jsState) -> std::optional<QOhosBluetoothErrorCode> {
227 if (!isBluetoothEnabled(jsState))
228 return QOhosBluetoothErrorCode::BluetoothDisabled;
229
230 RemoteDeviceServices remoteDeviceServices;
231 remoteDeviceServices.deviceId = deviceId.toStdString();
232 remoteDeviceServices.optDeviceName =
233 readRemoteDeviceName(jsState, remoteDeviceServices.deviceId);
234 remoteDeviceServices.optClassOfDevice =
235 readRemoteDeviceClass(jsState, remoteDeviceServices.deviceId);
236 remoteDeviceServices.lowEnergyOnly =
237 isRemoteDeviceLowEnergyOnly(jsState, remoteDeviceServices.deviceId);
238
239 auto pendingServices = QtOhos::moveToSharedPtr(std::move(remoteDeviceServices));
240 auto reportRemoteDeviceServices =
241 [selfRef, pendingServices]() {
242 selfRef.visitInQtThreadIfAlive(
243 [pendingServices](auto &self) {
244 Q_EMIT self.remoteDeviceServicesRead(*pendingServices);
245 });
246 };
247
248 try {
249 jsState.evalToPromiseOrRejectOnThrow(
250 "@ohos.bluetooth.connection.getRemoteProfileUuids(*)",
251 {pendingServices->deviceId})
252 .onThen(
253 [reportRemoteDeviceServices, pendingServices](
254 const QOhosCallbackInfo &cbInfo) {
255 auto resultArray = cbInfo.getFirstArg<QNapi::Array>(Q_FUNC_INFO);
256 pendingServices->optProfileUuids =
257 QNapi::getArrayElements<std::vector<std::string>, QNapi::String>(
258 resultArray,
259 [](QNapi::String &&profileUuid) {
260 return profileUuid.Utf8Value();
261 });
262 reportRemoteDeviceServices();
263 },
264 [reportRemoteDeviceServices](const QOhosCallbackInfo &cbInfo) {
265 QtOhos::logJsCallbackError(cbInfo, "Got error from getRemoteProfileUuids()");
266 reportRemoteDeviceServices();
267 });
268 return std::nullopt;
269 } catch (const Napi::Error &error) {
270 const auto errorCode =
271 tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO)
272 .value_or(QOhosBluetoothErrorCode::OperationFailed);
273 qOhosPrintfWarning(
274 "%s: cannot read the remote profile uuids, error code: %u", Q_FUNC_INFO,
275 qToUnderlying(errorCode));
276 return errorCode;
277 }
278 },
279 Q_FUNC_INFO);
280}
281
282bool QOhosBluetoothRemoteDeviceProxy::pairDevice(const QString &deviceId)
283{
284 if (!checkAccessBluetoothPermissionGranted(Q_FUNC_INFO)) {
285 Q_EMIT missingPermission();
286 return false;
287 }
288
289 if (!ensureBondStateChangeConsumerRegistered()) {
290 qCCritical(
291 QT_BT_OHOS,
292 "%s: bond state change consumer not registered. Pairing result will not be reported.",
293 Q_FUNC_INFO);
294 }
295
296 auto selfRef = QtOhos::makeQThreadSafeRef(this);
297 const auto optErrorCode = QOhosJsThreadGateway::eval(
298 [&](QOhosJsState &jsState) -> std::optional<QOhosBluetoothErrorCode> {
299 try {
300 jsState.evalToPromiseOrRejectOnThrow(
301 "@ohos.bluetooth.connection.pairDevice(*)", {deviceId.toStdString()})
302 .onCatch(
303 [selfRef, pairedDeviceId = deviceId.toStdString()](
304 const QOhosCallbackInfo &cbInfo) {
305 QtOhos::logJsCallbackError(cbInfo, "Got error from pairDevice()");
306 selfRef.visitInQtThreadIfAlive(
307 [pairedDeviceId](auto &self) {
308 Q_EMIT self.pairDeviceFailed(QString::fromStdString(pairedDeviceId));
309 });
310 });
311 return std::nullopt;
312 } catch (const Napi::Error &error) {
313 return tryGetKnownErrorCode(error.Value(), Q_FUNC_INFO);
314 }
315 },
316 Q_FUNC_INFO);
317
318 if (!optErrorCode)
319 return true;
320
321 qCWarning(
322 QT_BT_OHOS, "%s: cannot start pairing with the device %ls, error code: %u", Q_FUNC_INFO,
323 qUtf16Printable(deviceId), qToUnderlying(*optErrorCode));
324
325 return false;
326}
327
328}
329
330QT_END_NAMESPACE
331
332#include "moc_qohosbluetoothremotedevice_p.cpp"
std::optional< QString > tryGetRemoteDeviceName(const QString &deviceId)
std::optional< QOhosBluetoothErrorCode > requestRemoteDeviceServices(const QString &deviceId)
std::optional< BondState > tryGetPairState(const QString &deviceId)
static std::shared_ptr< QOhosBluetoothRemoteDeviceProxy > instance()
Combined button and popup list for selecting options.
bool isRemoteDeviceLowEnergyOnly(QOhosJsState &jsState, const std::string &deviceId)
std::optional< quint32 > readRemoteDeviceClass(QOhosJsState &jsState, const std::string &deviceId)
std::optional< QOhosBluetoothRemoteDeviceProxy::BluetoothTransport > readRemoteDeviceTransport(QOhosJsState &jsState, const std::string &deviceId)
std::optional< std::string > readRemoteDeviceName(QOhosJsState &jsState, const std::string &deviceId)