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
qbluetoothlocaldevice_ohos.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/qbluetoothaddress.h>
7#include <QtBluetooth/qbluetoothlocaldevice.h>
8#include <QtBluetooth/private/qohosbluetoothaccess_p.h>
9#include <QtBluetooth/private/qohosbluetoothcommon_p.h>
10#include <QtBluetooth/private/qohosbluetoothlocaldevice_p.h>
11#include <QtBluetooth/private/qohosbluetoothremotedevice_p.h>
12
13#include <QtCore/qdeadlinetimer.h>
14#include <QtCore/qhash.h>
15#include <QtCore/qloggingcategory.h>
16#include <QtCore/qtimer.h>
17
18#include <algorithm>
19#include <chrono>
20#include <memory>
21#include <optional>
22
23QT_BEGIN_NAMESPACE
24
25using QtOhosBluetooth::QOhosBluetoothErrorCode;
26
27Q_DECLARE_LOGGING_CATEGORY(QT_BT_OHOS)
28
29namespace {
30
31constexpr auto pairingRequestTimeout = std::chrono::seconds(30);
32constexpr auto hostModeRequestRetryDelay = std::chrono::milliseconds(500);
33constexpr auto hostModeRequestGiveUpDelay = std::chrono::seconds(20);
34
35QBluetoothLocalDevice::HostMode mapScanModeToQtHostMode(
36 QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode scanMode)
37{
38 using ScanMode = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode;
39
40 switch (scanMode) {
41 case ScanMode::SCAN_MODE_CONNECTABLE:
42 return QBluetoothLocalDevice::HostMode::HostConnectable;
43 case ScanMode::SCAN_MODE_GENERAL_DISCOVERABLE:
44 case ScanMode::SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE:
45 return QBluetoothLocalDevice::HostMode::HostDiscoverable;
46 case ScanMode::SCAN_MODE_LIMITED_DISCOVERABLE:
47 case ScanMode::SCAN_MODE_CONNECTABLE_LIMITED_DISCOVERABLE:
48 return QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry;
49 case ScanMode::SCAN_MODE_NONE:
50 return QBluetoothLocalDevice::HostMode::HostPoweredOff;
51 }
52
53 qOhosReportFatalErrorAndAbort(
54 "%s: unexpected ScanMode value %d", Q_FUNC_INFO, static_cast<int>(scanMode));
55}
56
57QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode mapHostModeToOhosScanMode(
58 QBluetoothLocalDevice::HostMode hostMode)
59{
60 using ScanMode = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode;
61
62 switch (hostMode) {
63 case QBluetoothLocalDevice::HostMode::HostPoweredOff:
64 return ScanMode::SCAN_MODE_NONE;
65 case QBluetoothLocalDevice::HostMode::HostConnectable:
66 return ScanMode::SCAN_MODE_CONNECTABLE;
67 case QBluetoothLocalDevice::HostMode::HostDiscoverable:
68 return ScanMode::SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE;
69 case QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry:
70 return ScanMode::SCAN_MODE_CONNECTABLE_LIMITED_DISCOVERABLE;
71 }
72
73 qOhosReportFatalErrorAndAbort(
74 "%s: unexpected HostMode value %d", Q_FUNC_INFO, static_cast<int>(hostMode));
75}
76
77bool isAdapterStateSettled(QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState bluetoothState)
78{
79 using BluetoothState = QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState;
80
81 switch (bluetoothState) {
82 case BluetoothState::STATE_BLE_TURNING_OFF:
83 case BluetoothState::STATE_BLE_TURNING_ON:
84 case BluetoothState::STATE_TURNING_OFF:
85 case BluetoothState::STATE_TURNING_ON:
86 return false;
87 case BluetoothState::STATE_BLE_ON:
88 case BluetoothState::STATE_OFF:
89 case BluetoothState::STATE_ON:
90 return true;
91 }
92
93 qOhosReportFatalErrorAndAbort(
94 "%s: unexpected BluetoothState value %d", Q_FUNC_INFO, static_cast<int>(bluetoothState));
95}
96
97QBluetoothLocalDevice::HostMode getBluetoothLocalDeviceHostMode(
98 QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState bluetoothLocalAdapterState)
99{
100 if (bluetoothLocalAdapterState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
101 return QBluetoothLocalDevice::HostMode::HostPoweredOff;
102
103 const auto optScanMode = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->tryGetScanMode();
104 return optScanMode
105 ? mapScanModeToQtHostMode(*optScanMode)
106 : QBluetoothLocalDevice::HostMode::HostPoweredOff;
107}
108
109QBluetoothLocalDevice::Pairing mapOhosBondStateToLocalDevicePairing(
110 QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState bondState)
111{
112 return bondState == QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDED
113 ? QBluetoothLocalDevice::Pairing::Paired
114 : QBluetoothLocalDevice::Pairing::Unpaired;
115}
116
117enum class HostModeUpdateSource { ChangeEvent, LiveRead };
118
119class QOhosBluetoothLocalDevicePrivate : public QBluetoothLocalDevicePrivate
120{
121public:
122 QOhosBluetoothLocalDevicePrivate(QBluetoothLocalDevice *qBluetoothLocalDevice, const QBluetoothAddress &address);
123
124 bool isValid() const override;
125 std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> tryGetBluetoothState() const;
126 std::optional<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState> tryGetPairState(
127 const QBluetoothAddress &address) const;
128 void requestHostMode(QBluetoothLocalDevice::HostMode requestedHostMode);
129 void requestPowerOn();
130 void cancelHostModeRequest();
131 void handleAdapterHostModeChanged(
132 QBluetoothLocalDevice::HostMode hostMode,
133 std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> optBluetoothState,
134 HostModeUpdateSource source = HostModeUpdateSource::ChangeEvent);
135
136 bool pairDevice(const QBluetoothAddress &address);
137 void cancelPendingPairingRequests();
138
139private:
140 struct HostModeRequest
141 {
142 QBluetoothLocalDevice::HostMode target = QBluetoothLocalDevice::HostMode::HostPoweredOff;
143 bool anyPoweredOnAccepted = false;
144 bool awaitingOutcome = false;
145 bool powerSwitchIssued = false;
146 bool scanModeIssued = false;
147 QDeadlineTimer deadline;
148 };
149
150 void registerHostModeRequest(
151 QBluetoothLocalDevice::HostMode target, bool anyPoweredOnAccepted);
152 void issueHostModeRequest();
153 void resolveHostModeRequestFromAdapterState();
154 void scheduleHostModeRetry();
155 void awaitHostModeRequestOutcome(bool powerSwitchIssued);
156 void giveUpHostModeRequest();
157 static bool isRequestSatisfiedBy(
158 const HostModeRequest &request, QBluetoothLocalDevice::HostMode hostMode,
159 bool adapterPoweredOn);
160 static bool isPowerSwitchOutcomePending(
161 const HostModeRequest &request, bool adapterPoweredOn);
162 void rearmPendingPairingRequestsTimer();
163 void handleExpiredPendingPairingRequests();
164 void emitAsyncError(QBluetoothLocalDevice::Error error);
165
166 QBluetoothLocalDevice *m_qBluetoothLocalDevice;
167 std::shared_ptr<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy> m_remoteDeviceProxy;
168 bool m_isValid = false;
169 QBluetoothLocalDevice::HostMode m_lastHostMode =
170 QBluetoothLocalDevice::HostMode::HostPoweredOff;
171 std::optional<HostModeRequest> m_optHostModeRequest = std::nullopt;
172 QHash<QBluetoothAddress, QDeadlineTimer> m_pendingPairingRequests;
173 QTimer m_pendingPairingRequestsTimer;
174 QTimer m_hostModeRequestTimer;
175};
176
177QOhosBluetoothLocalDevicePrivate::QOhosBluetoothLocalDevicePrivate(
178 QBluetoothLocalDevice *qBluetoothLocalDevice, const QBluetoothAddress &address)
179 : m_qBluetoothLocalDevice(qBluetoothLocalDevice)
180 , m_remoteDeviceProxy(QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::instance())
181{
182 if (!address.isNull()) {
183 qCWarning(
184 QT_BT_OHOS, "%s: HarmonyOS does not support device adapter setting. Ignoring address %ls",
185 Q_FUNC_INFO, qUtf16Printable(address.toString()));
186
187 return;
188 }
189
190 m_isValid = true;
191
192 if (const auto optBluetoothState = tryGetBluetoothState())
193 m_lastHostMode = getBluetoothLocalDeviceHostMode(*optBluetoothState);
194
195 QObject::connect(
196 QtOhosBluetooth::QOhosBluetoothAccessProxy::instance().get(),
197 &QtOhosBluetooth::QOhosBluetoothAccessProxy::stateChanged,
198 this,
199 [this](auto state) {
200 if (state != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
201 cancelPendingPairingRequests();
202
203 handleAdapterHostModeChanged(getBluetoothLocalDeviceHostMode(state), state);
204 });
205
206 QObject::connect(
207 QtOhosBluetooth::QOhosBluetoothAccessProxy::instance().get(),
208 &QtOhosBluetooth::QOhosBluetoothAccessProxy::setBluetoothEnabledFailed,
209 this,
210 [this](QOhosBluetoothErrorCode errorCode) {
211 if (!m_optHostModeRequest || !m_optHostModeRequest->powerSwitchIssued)
212 return;
213
214 qCWarning(
215 QT_BT_OHOS, "%s: the bluetooth switch was not performed, error code: %u",
216 Q_FUNC_INFO, qToUnderlying(errorCode));
217 giveUpHostModeRequest();
218 });
219
220 QObject::connect(
221 QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance().get(),
222 &QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::scanModeChanged,
223 this,
224 [this](auto scanMode) {
225 const auto optBluetoothState = tryGetBluetoothState();
226 if (optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
227 return;
228
229 handleAdapterHostModeChanged(mapScanModeToQtHostMode(scanMode), optBluetoothState);
230 });
231
232 QObject::connect(
233 m_remoteDeviceProxy.get(),
234 &QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::bondStateChanged,
235 this,
236 [this](const QString &deviceId, QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState bondState,
237 std::optional<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::UnbondCause> optUnbondCause) {
238 const QBluetoothAddress deviceAddress(deviceId);
239 if (deviceAddress.isNull()) {
240 qCWarning(
241 QT_BT_OHOS,
242 "%s: ignoring the bond state change for a device with an unexpected address: '%ls'",
243 Q_FUNC_INFO, qUtf16Printable(deviceId));
244 return;
245 }
246
247 if (!m_pendingPairingRequests.contains(deviceAddress))
248 return;
249
250 switch (bondState) {
251 case QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_INVALID:
252 qCWarning(
253 QT_BT_OHOS, "%s: pairing with the device %ls failed with the unbond cause %d",
254 Q_FUNC_INFO, qUtf16Printable(deviceAddress.toString()),
255 optUnbondCause ? static_cast<int>(*optUnbondCause) : -1);
256 m_pendingPairingRequests.remove(deviceAddress);
257 rearmPendingPairingRequestsTimer();
258 emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
259 break;
260 case QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDING:
261 break;
262 case QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDED:
263 m_pendingPairingRequests.remove(deviceAddress);
264 rearmPendingPairingRequestsTimer();
265 Q_EMIT m_qBluetoothLocalDevice->pairingFinished(
266 deviceAddress, QBluetoothLocalDevice::Paired);
267 break;
268 }
269 });
270
271 QObject::connect(
272 m_remoteDeviceProxy.get(),
273 &QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::pairDeviceFailed,
274 this,
275 [this](const QString &deviceId) {
276 const QBluetoothAddress deviceAddress(deviceId);
277 if (!m_pendingPairingRequests.contains(deviceAddress))
278 return;
279
280 qCWarning(
281 QT_BT_OHOS, "%s: the pairing request for the device %ls was rejected", Q_FUNC_INFO,
282 qUtf16Printable(deviceId));
283 m_pendingPairingRequests.remove(deviceAddress);
284 rearmPendingPairingRequestsTimer();
285 emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
286 });
287
288 auto emitMissingPermissionsError = [this]() {
289 emitAsyncError(QBluetoothLocalDevice::MissingPermissionsError);
290 };
291 QObject::connect(
292 QtOhosBluetooth::QOhosBluetoothAccessProxy::instance().get(),
293 &QtOhosBluetooth::QOhosBluetoothAccessProxy::missingPermission,
294 this, emitMissingPermissionsError);
295 QObject::connect(
296 QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance().get(),
297 &QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::missingPermission,
298 this, emitMissingPermissionsError);
299 QObject::connect(
300 m_remoteDeviceProxy.get(),
301 &QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::missingPermission,
302 this, emitMissingPermissionsError);
303
304 m_pendingPairingRequestsTimer.setSingleShot(true);
305 QObject::connect(
306 &m_pendingPairingRequestsTimer, &QTimer::timeout, this,
307 [this]() {
308 handleExpiredPendingPairingRequests();
309 });
310
311 m_hostModeRequestTimer.setSingleShot(true);
312 QObject::connect(
313 &m_hostModeRequestTimer, &QTimer::timeout, this,
314 [this]() {
315 if (!m_optHostModeRequest)
316 return;
317
318 if (m_optHostModeRequest->awaitingOutcome
319 || m_optHostModeRequest->deadline.hasExpired()) {
320 resolveHostModeRequestFromAdapterState();
321
322 if (m_optHostModeRequest && !m_hostModeRequestTimer.isActive())
323 giveUpHostModeRequest();
324
325 return;
326 }
327
328 issueHostModeRequest();
329 });
330}
331
332bool QOhosBluetoothLocalDevicePrivate::isValid() const
333{
334 return m_isValid;
335}
336
337std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> QOhosBluetoothLocalDevicePrivate::tryGetBluetoothState() const
338{
339 return QtOhosBluetooth::QOhosBluetoothAccessProxy::instance()->tryGetBluetoothState();
340}
341
342std::optional<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState> QOhosBluetoothLocalDevicePrivate::tryGetPairState(
343 const QBluetoothAddress &address) const
344{
345 return m_remoteDeviceProxy->tryGetPairState(address.toString());
346}
347
348void QOhosBluetoothLocalDevicePrivate::requestHostMode(
349 QBluetoothLocalDevice::HostMode requestedHostMode)
350{
351 registerHostModeRequest(requestedHostMode, false);
352}
353
354void QOhosBluetoothLocalDevicePrivate::requestPowerOn()
355{
356 registerHostModeRequest(QBluetoothLocalDevice::HostMode::HostConnectable, true);
357}
358
359void QOhosBluetoothLocalDevicePrivate::registerHostModeRequest(
360 QBluetoothLocalDevice::HostMode target, bool anyPoweredOnAccepted)
361{
362 m_hostModeRequestTimer.stop();
363 m_optHostModeRequest = HostModeRequest {
364 .target = target,
365 .anyPoweredOnAccepted = anyPoweredOnAccepted,
366 .awaitingOutcome = false,
367 .powerSwitchIssued = false,
368 .scanModeIssued = false,
369 .deadline = QDeadlineTimer(hostModeRequestGiveUpDelay),
370 };
371
372 issueHostModeRequest();
373}
374
375bool QOhosBluetoothLocalDevicePrivate::isRequestSatisfiedBy(
376 const HostModeRequest &request, QBluetoothLocalDevice::HostMode hostMode,
377 bool adapterPoweredOn)
378{
379 if (request.anyPoweredOnAccepted)
380 return adapterPoweredOn && hostMode != QBluetoothLocalDevice::HostMode::HostPoweredOff;
381
382 if (request.target == QBluetoothLocalDevice::HostMode::HostPoweredOff)
383 return !adapterPoweredOn;
384
385 if (request.target == QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry)
386 return hostMode == QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry
387 || hostMode == QBluetoothLocalDevice::HostMode::HostDiscoverable;
388
389 return hostMode == request.target;
390}
391
392void QOhosBluetoothLocalDevicePrivate::resolveHostModeRequestFromAdapterState()
393{
394 const auto optBluetoothState = tryGetBluetoothState();
395 if (!optBluetoothState)
396 return;
397
398 handleAdapterHostModeChanged(
399 getBluetoothLocalDeviceHostMode(*optBluetoothState), optBluetoothState,
400 HostModeUpdateSource::LiveRead);
401}
402
403void QOhosBluetoothLocalDevicePrivate::issueHostModeRequest()
404{
405 Q_ASSERT(m_optHostModeRequest);
406 using BluetoothState = QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState;
407
408 const auto target = m_optHostModeRequest->target;
409 const auto optBluetoothState = tryGetBluetoothState();
410
411 if (optBluetoothState && isAdapterStateSettled(*optBluetoothState)
412 && isRequestSatisfiedBy(
413 *m_optHostModeRequest, getBluetoothLocalDeviceHostMode(*optBluetoothState),
414 *optBluetoothState == BluetoothState::STATE_ON)) {
415 cancelHostModeRequest();
416 return;
417 }
418
419 const auto handleOutcome =
420 [this](std::optional<QOhosBluetoothErrorCode> optErrorCode, bool isPowerSwitchCall) {
421 if (!optErrorCode) {
422 if (isPowerSwitchCall) {
423 awaitHostModeRequestOutcome(true);
424 return;
425 }
426
427 m_optHostModeRequest->scanModeIssued = true;
428 resolveHostModeRequestFromAdapterState();
429
430 if (m_optHostModeRequest && !m_hostModeRequestTimer.isActive())
431 awaitHostModeRequestOutcome(false);
432
433 return;
434 }
435
436 if (*optErrorCode == QOhosBluetoothErrorCode::OperationFailed
437 || *optErrorCode == QOhosBluetoothErrorCode::BluetoothDisabled) {
438 if (isPowerSwitchCall)
439 awaitHostModeRequestOutcome(true);
440 else
441 scheduleHostModeRetry();
442
443 return;
444 }
445
446 if (*optErrorCode == QOhosBluetoothErrorCode::PermissionDenied) {
447 cancelHostModeRequest();
448 return;
449 }
450
451 qCWarning(
452 QT_BT_OHOS, "%s: the host mode request was rejected with error code: %u",
453 Q_FUNC_INFO, qToUnderlying(*optErrorCode));
454 giveUpHostModeRequest();
455 };
456
457 if (!optBluetoothState) {
458 scheduleHostModeRetry();
459 return;
460 }
461
462 if (!isAdapterStateSettled(*optBluetoothState)) {
463 awaitHostModeRequestOutcome(false);
464 return;
465 }
466
467 m_optHostModeRequest->scanModeIssued = false;
468
469 if (target == QBluetoothLocalDevice::HostMode::HostPoweredOff) {
470 Q_ASSERT(*optBluetoothState == BluetoothState::STATE_ON);
471 handleOutcome(
472 QtOhosBluetooth::QOhosBluetoothAccessProxy::instance()->trySetBluetoothEnabled(false),
473 true);
474 return;
475 }
476
477 if (*optBluetoothState != BluetoothState::STATE_ON) {
478 handleOutcome(
479 QtOhosBluetooth::QOhosBluetoothAccessProxy::instance()->trySetBluetoothEnabled(true),
480 true);
481 return;
482 }
483
484 handleOutcome(
485 QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->trySetBluetoothScanMode(
486 mapHostModeToOhosScanMode(target)),
487 false);
488}
489
490void QOhosBluetoothLocalDevicePrivate::scheduleHostModeRetry()
491{
492 Q_ASSERT(m_optHostModeRequest);
493 if (m_optHostModeRequest->deadline.hasExpired()) {
494 giveUpHostModeRequest();
495 return;
496 }
497
498 m_optHostModeRequest->awaitingOutcome = false;
499 m_optHostModeRequest->powerSwitchIssued = false;
500 m_hostModeRequestTimer.start(hostModeRequestRetryDelay);
501}
502
503bool QOhosBluetoothLocalDevicePrivate::isPowerSwitchOutcomePending(
504 const HostModeRequest &request, bool adapterPoweredOn)
505{
506 if (!request.powerSwitchIssued)
507 return false;
508
509 return request.target == QBluetoothLocalDevice::HostMode::HostPoweredOff
510 ? adapterPoweredOn
511 : !adapterPoweredOn;
512}
513
514void QOhosBluetoothLocalDevicePrivate::awaitHostModeRequestOutcome(bool powerSwitchIssued)
515{
516 Q_ASSERT(m_optHostModeRequest);
517 const auto remainingTime = m_optHostModeRequest->deadline.remainingTimeAsDuration();
518 if (remainingTime <= std::chrono::milliseconds::zero()) {
519 giveUpHostModeRequest();
520 return;
521 }
522
523 m_optHostModeRequest->awaitingOutcome = true;
524 m_optHostModeRequest->powerSwitchIssued = powerSwitchIssued;
525 m_hostModeRequestTimer.start(
526 std::chrono::ceil<std::chrono::milliseconds>(remainingTime));
527}
528
529void QOhosBluetoothLocalDevicePrivate::giveUpHostModeRequest()
530{
531 Q_ASSERT(m_optHostModeRequest);
532 qCWarning(
533 QT_BT_OHOS, "%s: giving up on the requested host mode %d, the adapter stays in %d",
534 Q_FUNC_INFO, static_cast<int>(m_optHostModeRequest->target),
535 static_cast<int>(m_lastHostMode));
536
537 cancelHostModeRequest();
538
539 emitAsyncError(QBluetoothLocalDevice::Error::UnknownError);
540}
541
542void QOhosBluetoothLocalDevicePrivate::cancelHostModeRequest()
543{
544 m_hostModeRequestTimer.stop();
545 m_optHostModeRequest.reset();
546}
547
548void QOhosBluetoothLocalDevicePrivate::handleAdapterHostModeChanged(
549 QBluetoothLocalDevice::HostMode hostMode,
550 std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> optBluetoothState,
551 HostModeUpdateSource source)
552{
553 const auto hostModeChanged = m_lastHostMode != hostMode;
554 m_lastHostMode = hostMode;
555
556 const auto adapterSettled = optBluetoothState && isAdapterStateSettled(*optBluetoothState);
557 const auto adapterPoweredOn =
558 optBluetoothState == QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON;
559
560 if (m_optHostModeRequest && adapterSettled) {
561 const auto scanModeApplied = source == HostModeUpdateSource::LiveRead
562 || m_optHostModeRequest->anyPoweredOnAccepted
563 || m_optHostModeRequest->target == QBluetoothLocalDevice::HostMode::HostPoweredOff
564 || m_optHostModeRequest->scanModeIssued;
565
566 if (scanModeApplied
567 && isRequestSatisfiedBy(*m_optHostModeRequest, hostMode, adapterPoweredOn)) {
568 cancelHostModeRequest();
569 } else if (!isPowerSwitchOutcomePending(*m_optHostModeRequest, adapterPoweredOn)) {
570 if (m_optHostModeRequest->powerSwitchIssued && adapterPoweredOn)
571 m_optHostModeRequest->deadline = QDeadlineTimer(hostModeRequestGiveUpDelay);
572
573 scheduleHostModeRetry();
574 }
575 }
576
577 if (hostModeChanged) {
578 QMetaObject::invokeMethod(
579 m_qBluetoothLocalDevice,
580 [qBluetoothLocalDevice = m_qBluetoothLocalDevice, hostMode]() {
581 Q_EMIT qBluetoothLocalDevice->hostModeStateChanged(hostMode);
582 },
583 Qt::QueuedConnection);
584 }
585}
586
587bool QOhosBluetoothLocalDevicePrivate::pairDevice(const QBluetoothAddress &address)
588{
589 const auto addressStr = address.toString().toStdString();
590 if (m_pendingPairingRequests.contains(address)) {
591 qCDebug(
592 QT_BT_OHOS,
593 "%s: pairing request ignored due to already pending pairing process to the device with address %s",
594 Q_FUNC_INFO, addressStr.c_str());
595 return true;
596 }
597
598 m_pendingPairingRequests.insert(address, QDeadlineTimer(pairingRequestTimeout));
599 rearmPendingPairingRequestsTimer();
600
601 if (!m_remoteDeviceProxy->pairDevice(address.toString())) {
602 m_pendingPairingRequests.remove(address);
603 rearmPendingPairingRequestsTimer();
604 return false;
605 }
606
607 return true;
608}
609
610void QOhosBluetoothLocalDevicePrivate::cancelPendingPairingRequests()
611{
612 if (m_pendingPairingRequests.isEmpty())
613 return;
614
615 m_pendingPairingRequests.clear();
616 rearmPendingPairingRequestsTimer();
617 emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
618}
619
620void QOhosBluetoothLocalDevicePrivate::emitAsyncError(QBluetoothLocalDevice::Error error)
621{
622 QMetaObject::invokeMethod(
623 m_qBluetoothLocalDevice,
624 [this, error]() {
625 Q_EMIT m_qBluetoothLocalDevice->errorOccurred(error);
626 },
627 Qt::QueuedConnection);
628}
629
630void QOhosBluetoothLocalDevicePrivate::rearmPendingPairingRequestsTimer()
631{
632 if (m_pendingPairingRequests.isEmpty()) {
633 m_pendingPairingRequestsTimer.stop();
634 return;
635 }
636
637 const auto earliestDeadline = std::min_element(
638 m_pendingPairingRequests.cbegin(), m_pendingPairingRequests.cend());
639
640 m_pendingPairingRequestsTimer.start(
641 std::max(
642 std::chrono::ceil<std::chrono::milliseconds>(earliestDeadline->remainingTimeAsDuration()),
643 std::chrono::milliseconds::zero()));
644}
645
646void QOhosBluetoothLocalDevicePrivate::handleExpiredPendingPairingRequests()
647{
648 QList<QBluetoothAddress> expiredAddresses;
649 for (auto pendingPairingRequest = m_pendingPairingRequests.cbegin();
650 pendingPairingRequest != m_pendingPairingRequests.cend(); ++pendingPairingRequest) {
651 if (pendingPairingRequest.value().hasExpired())
652 expiredAddresses.append(pendingPairingRequest.key());
653 }
654
655 for (const auto &expiredAddress : expiredAddresses)
656 m_pendingPairingRequests.remove(expiredAddress);
657
658 rearmPendingPairingRequestsTimer();
659
660 for (const auto &expiredAddress : expiredAddresses) {
661 const auto optPairState = tryGetPairState(expiredAddress);
662 if (optPairState
663 && *optPairState == QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDED) {
664 Q_EMIT m_qBluetoothLocalDevice->pairingFinished(
665 expiredAddress, QBluetoothLocalDevice::Paired);
666 continue;
667 }
668
669 qCWarning(
670 QT_BT_OHOS, "%s: pairing with the device %ls not confirmed by the system in time",
671 Q_FUNC_INFO, qUtf16Printable(expiredAddress.toString()));
672 emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
673 }
674}
675
676}
677
678QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate() = default;
679
680QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() = default;
681
682QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent)
683 : QBluetoothLocalDevice(QBluetoothAddress(), parent)
684{
685}
686
687QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent)
688 : QObject(parent)
689 , d_ptr(new QOhosBluetoothLocalDevicePrivate(this, address))
690{
691 registerQBluetoothLocalDeviceMetaType();
692}
693
694QString QBluetoothLocalDevice::name() const
695{
696 if (!d_ptr->isValid()) {
697 qCWarning(
698 QT_BT_OHOS, "%s: local device is not valid. Can't get local device name.", Q_FUNC_INFO);
699 return QString();
700 }
701
702 const auto optLocalDeviceName = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->tryGetLocalDeviceName();
703
704 return optLocalDeviceName
705 ? *optLocalDeviceName
706 : QString();
707}
708
709QBluetoothAddress QBluetoothLocalDevice::address() const
710{
711 return {};
712}
713
714void QBluetoothLocalDevice::powerOn()
715{
716 if (!d_ptr->isValid()) {
717 qCWarning(QT_BT_OHOS, "%s: local device is not valid. Ignoring...", Q_FUNC_INFO);
718 return;
719 }
720
721 if (hostMode() != HostMode::HostPoweredOff) {
722 qCWarning(QT_BT_OHOS, "%s: bluetooth is not powered off. Ignoring...", Q_FUNC_INFO);
723 return;
724 }
725
726 static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->requestPowerOn();
727}
728
729void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
730{
731 if (!d_ptr->isValid()) {
732 qCWarning(QT_BT_OHOS, "%s: local device is not valid. Ignoring...", Q_FUNC_INFO);
733 return;
734 }
735
736 auto *ohosBluetoothLocalDevicePriv = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr);
737
738 const auto optBluetoothState = ohosBluetoothLocalDevicePriv->tryGetBluetoothState();
739 const auto adapterSettled = optBluetoothState && isAdapterStateSettled(*optBluetoothState);
740
741 if (adapterSettled && mode == QBluetoothLocalDevice::HostMode::HostPoweredOff
742 && *optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON) {
743 qCWarning(
744 QT_BT_OHOS,
745 "%s: Bluetooth is not in powered on state. Cancelling the pending host mode request.",
746 Q_FUNC_INFO);
747 ohosBluetoothLocalDevicePriv->cancelHostModeRequest();
748 return;
749 }
750
751 ohosBluetoothLocalDevicePriv->requestHostMode(mode);
752}
753
754QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
755{
756 if (!d_ptr->isValid()) {
757 qCWarning(
758 QT_BT_OHOS, "%s: local device is not valid. Reporting powered off host mode.",
759 Q_FUNC_INFO);
760 return QBluetoothLocalDevice::HostPoweredOff;
761 }
762
763 const auto optBluetoothState = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetBluetoothState();
764 if (!optBluetoothState)
765 return QBluetoothLocalDevice::HostPoweredOff;
766
767 return getBluetoothLocalDeviceHostMode(*optBluetoothState);
768}
769
770QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
771{
772 return {};
773}
774
775QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
776{
777 const auto optLocalDeviceName =
778 QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->tryGetLocalDeviceName();
779 if (!optLocalDeviceName)
780 return {};
781
782 QBluetoothHostInfo bluetoothHostInfo;
783 bluetoothHostInfo.setName(*optLocalDeviceName);
784 return { bluetoothHostInfo };
785}
786
787void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
788{
789 auto emitPairingError = [this]() {
790 QMetaObject::invokeMethod(
791 this,
792 [this]() {
793 Q_EMIT errorOccurred(QBluetoothLocalDevice::PairingError);
794 },
795 Qt::QueuedConnection);
796 };
797
798 auto emitPairingFinishedAsync = [this, address](Pairing finishedPairing) {
799 QMetaObject::invokeMethod(
800 this,
801 [this, address, finishedPairing]() {
802 Q_EMIT pairingFinished(address, finishedPairing);
803 },
804 Qt::QueuedConnection);
805 };
806
807 if (!isValid() || address.isNull()) {
808 qCWarning(
809 QT_BT_OHOS, "%s: local device is not valid or the device address is null. Ignoring...",
810 Q_FUNC_INFO);
811 emitPairingError();
812 return;
813 }
814
815 const auto optBluetoothState = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetBluetoothState();
816 if (optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON) {
817 qCWarning(QT_BT_OHOS, "%s: bluetooth not in powered on state. Ignoring...", Q_FUNC_INFO);
818 emitPairingError();
819 return;
820 }
821
822 switch (pairing) {
823 case AuthorizedPaired:
824 qCWarning(
825 QT_BT_OHOS,
826 "%s: AuthorizedPaired not supported on HarmonyOS. Falling back to Paired.",
827 Q_FUNC_INFO);
828 Q_FALLTHROUGH();
829 case Paired:
830 if (pairingStatus(address) == QBluetoothLocalDevice::Pairing::Paired) {
831 qCDebug(
832 QT_BT_OHOS, "%s: Already paired with the device %ls. Ignoring pairing request.",
833 Q_FUNC_INFO, qUtf16Printable(address.toString()));
834 emitPairingFinishedAsync(QBluetoothLocalDevice::Pairing::Paired);
835 break;
836 }
837
838 if (!static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->pairDevice(address))
839 emitPairingError();
840 break;
841 case Unpaired:
842 if (pairingStatus(address) == QBluetoothLocalDevice::Pairing::Unpaired) {
843 qCDebug(
844 QT_BT_OHOS, "%s: Not paired with the device %ls. Ignoring unpairing request.",
845 Q_FUNC_INFO, qUtf16Printable(address.toString()));
846 emitPairingFinishedAsync(QBluetoothLocalDevice::Pairing::Unpaired);
847 break;
848 }
849
850 qCWarning(
851 QT_BT_OHOS, "%s: Unpairing not supported on HarmonyOS for paired devices.",
852 Q_FUNC_INFO);
853 emitPairingError();
854 break;
855 }
856}
857
858QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(const QBluetoothAddress &address) const
859{
860 if (!d_ptr->isValid()) {
861 qCWarning(
862 QT_BT_OHOS, "%s: local device is not valid. Reporting unpaired device.", Q_FUNC_INFO);
863 return Unpaired;
864 }
865
866 if (address.isNull()) {
867 qCWarning(
868 QT_BT_OHOS, "%s: device address is null. Reporting unpaired device.", Q_FUNC_INFO);
869 return Unpaired;
870 }
871
872 const auto optBluetoothState = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetBluetoothState();
873 if (optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
874 return Unpaired;
875
876 const auto optPairState =
877 static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetPairState(address);
878 if (!optPairState)
879 return QBluetoothLocalDevice::Pairing::Unpaired;
880
881 return mapOhosBondStateToLocalDevicePairing(*optPairState);
882}
883
884QT_END_NAMESPACE