Revision control

// 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 "qbluetoothlocaldevice_p.h"
#include <QtBluetooth/qbluetoothaddress.h>
#include <QtBluetooth/qbluetoothlocaldevice.h>
#include <QtBluetooth/private/qohosbluetoothaccess_p.h>
#include <QtBluetooth/private/qohosbluetoothcommon_p.h>
#include <QtBluetooth/private/qohosbluetoothlocaldevice_p.h>
+ #include <QtBluetooth/private/qohosbluetoothremotedevice_p.h>
#include <QtCore/qdeadlinetimer.h>
+ #include <QtCore/qhash.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qtimer.h>
#include <algorithm>
#include <chrono>
#include <memory>
#include <optional>
QT_BEGIN_NAMESPACE
using QtOhosBluetooth::QOhosBluetoothErrorCode;
Q_DECLARE_LOGGING_CATEGORY(QT_BT_OHOS)
namespace {
+ constexpr auto pairingRequestTimeout = std::chrono::seconds(30);
constexpr auto hostModeRequestRetryDelay = std::chrono::milliseconds(500);
constexpr auto hostModeRequestGiveUpDelay = std::chrono::seconds(20);
QBluetoothLocalDevice::HostMode mapScanModeToQtHostMode(
QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode scanMode)
{
using ScanMode = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode;
switch (scanMode) {
case ScanMode::SCAN_MODE_CONNECTABLE:
return QBluetoothLocalDevice::HostMode::HostConnectable;
case ScanMode::SCAN_MODE_GENERAL_DISCOVERABLE:
case ScanMode::SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE:
return QBluetoothLocalDevice::HostMode::HostDiscoverable;
case ScanMode::SCAN_MODE_LIMITED_DISCOVERABLE:
case ScanMode::SCAN_MODE_CONNECTABLE_LIMITED_DISCOVERABLE:
return QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry;
case ScanMode::SCAN_MODE_NONE:
return QBluetoothLocalDevice::HostMode::HostPoweredOff;
}
qOhosReportFatalErrorAndAbort(
"%s: unexpected ScanMode value %d", Q_FUNC_INFO, static_cast<int>(scanMode));
}
QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode mapHostModeToOhosScanMode(
QBluetoothLocalDevice::HostMode hostMode)
{
using ScanMode = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::ScanMode;
switch (hostMode) {
case QBluetoothLocalDevice::HostMode::HostPoweredOff:
return ScanMode::SCAN_MODE_NONE;
case QBluetoothLocalDevice::HostMode::HostConnectable:
return ScanMode::SCAN_MODE_CONNECTABLE;
case QBluetoothLocalDevice::HostMode::HostDiscoverable:
return ScanMode::SCAN_MODE_CONNECTABLE_GENERAL_DISCOVERABLE;
case QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry:
return ScanMode::SCAN_MODE_CONNECTABLE_LIMITED_DISCOVERABLE;
}
qOhosReportFatalErrorAndAbort(
"%s: unexpected HostMode value %d", Q_FUNC_INFO, static_cast<int>(hostMode));
}
bool isAdapterStateSettled(QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState bluetoothState)
{
using BluetoothState = QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState;
switch (bluetoothState) {
case BluetoothState::STATE_BLE_TURNING_OFF:
case BluetoothState::STATE_BLE_TURNING_ON:
case BluetoothState::STATE_TURNING_OFF:
case BluetoothState::STATE_TURNING_ON:
return false;
case BluetoothState::STATE_BLE_ON:
case BluetoothState::STATE_OFF:
case BluetoothState::STATE_ON:
return true;
}
qOhosReportFatalErrorAndAbort(
"%s: unexpected BluetoothState value %d", Q_FUNC_INFO, static_cast<int>(bluetoothState));
}
QBluetoothLocalDevice::HostMode getBluetoothLocalDeviceHostMode(
QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState bluetoothLocalAdapterState)
{
if (bluetoothLocalAdapterState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
return QBluetoothLocalDevice::HostMode::HostPoweredOff;
const auto optScanMode = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->tryGetScanMode();
return optScanMode
? mapScanModeToQtHostMode(*optScanMode)
: QBluetoothLocalDevice::HostMode::HostPoweredOff;
}
+ QBluetoothLocalDevice::Pairing mapOhosBondStateToLocalDevicePairing(
+ QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState bondState)
+ {
+ return bondState == QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDED
+ ? QBluetoothLocalDevice::Pairing::Paired
+ : QBluetoothLocalDevice::Pairing::Unpaired;
+ }
+
enum class HostModeUpdateSource { ChangeEvent, LiveRead };
class QOhosBluetoothLocalDevicePrivate : public QBluetoothLocalDevicePrivate
{
public:
QOhosBluetoothLocalDevicePrivate(QBluetoothLocalDevice *qBluetoothLocalDevice, const QBluetoothAddress &address);
bool isValid() const override;
std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> tryGetBluetoothState() const;
+ std::optional<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState> tryGetPairState(
+ const QBluetoothAddress &address) const;
void requestHostMode(QBluetoothLocalDevice::HostMode requestedHostMode);
void requestPowerOn();
void cancelHostModeRequest();
void handleAdapterHostModeChanged(
QBluetoothLocalDevice::HostMode hostMode,
std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> optBluetoothState,
HostModeUpdateSource source = HostModeUpdateSource::ChangeEvent);
+ bool pairDevice(const QBluetoothAddress &address);
+ void cancelPendingPairingRequests();
+
private:
struct HostModeRequest
{
QBluetoothLocalDevice::HostMode target = QBluetoothLocalDevice::HostMode::HostPoweredOff;
bool anyPoweredOnAccepted = false;
bool awaitingOutcome = false;
bool powerSwitchIssued = false;
bool scanModeIssued = false;
QDeadlineTimer deadline;
};
void registerHostModeRequest(
QBluetoothLocalDevice::HostMode target, bool anyPoweredOnAccepted);
void issueHostModeRequest();
void resolveHostModeRequestFromAdapterState();
void scheduleHostModeRetry();
void awaitHostModeRequestOutcome(bool powerSwitchIssued);
void giveUpHostModeRequest();
static bool isRequestSatisfiedBy(
const HostModeRequest &request, QBluetoothLocalDevice::HostMode hostMode,
bool adapterPoweredOn);
static bool isPowerSwitchOutcomePending(
const HostModeRequest &request, bool adapterPoweredOn);
+ void rearmPendingPairingRequestsTimer();
+ void handleExpiredPendingPairingRequests();
void emitAsyncError(QBluetoothLocalDevice::Error error);
QBluetoothLocalDevice *m_qBluetoothLocalDevice;
+ std::shared_ptr<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy> m_remoteDeviceProxy;
bool m_isValid = false;
QBluetoothLocalDevice::HostMode m_lastHostMode =
QBluetoothLocalDevice::HostMode::HostPoweredOff;
std::optional<HostModeRequest> m_optHostModeRequest = std::nullopt;
+ QHash<QBluetoothAddress, QDeadlineTimer> m_pendingPairingRequests;
+ QTimer m_pendingPairingRequestsTimer;
QTimer m_hostModeRequestTimer;
};
QOhosBluetoothLocalDevicePrivate::QOhosBluetoothLocalDevicePrivate(
QBluetoothLocalDevice *qBluetoothLocalDevice, const QBluetoothAddress &address)
: m_qBluetoothLocalDevice(qBluetoothLocalDevice)
+ , m_remoteDeviceProxy(QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::instance())
{
if (!address.isNull()) {
qCWarning(
QT_BT_OHOS, "%s: HarmonyOS does not support device adapter setting. Ignoring address %ls",
Q_FUNC_INFO, qUtf16Printable(address.toString()));
return;
}
m_isValid = true;
if (const auto optBluetoothState = tryGetBluetoothState())
m_lastHostMode = getBluetoothLocalDeviceHostMode(*optBluetoothState);
QObject::connect(
QtOhosBluetooth::QOhosBluetoothAccessProxy::instance().get(),
&QtOhosBluetooth::QOhosBluetoothAccessProxy::stateChanged,
this,
[this](auto state) {
+ if (state != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
+ cancelPendingPairingRequests();
+
handleAdapterHostModeChanged(getBluetoothLocalDeviceHostMode(state), state);
});
QObject::connect(
QtOhosBluetooth::QOhosBluetoothAccessProxy::instance().get(),
&QtOhosBluetooth::QOhosBluetoothAccessProxy::setBluetoothEnabledFailed,
this,
[this](QOhosBluetoothErrorCode errorCode) {
if (!m_optHostModeRequest || !m_optHostModeRequest->powerSwitchIssued)
return;
qCWarning(
QT_BT_OHOS, "%s: the bluetooth switch was not performed, error code: %u",
Q_FUNC_INFO, qToUnderlying(errorCode));
giveUpHostModeRequest();
});
QObject::connect(
QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance().get(),
&QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::scanModeChanged,
this,
[this](auto scanMode) {
const auto optBluetoothState = tryGetBluetoothState();
if (optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
return;
handleAdapterHostModeChanged(mapScanModeToQtHostMode(scanMode), optBluetoothState);
});
+ QObject::connect(
+ m_remoteDeviceProxy.get(),
+ &QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::bondStateChanged,
+ this,
+ [this](const QString &deviceId, QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState bondState,
+ std::optional<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::UnbondCause> optUnbondCause) {
+ const QBluetoothAddress deviceAddress(deviceId);
+ if (deviceAddress.isNull()) {
+ qCWarning(
+ QT_BT_OHOS,
+ "%s: ignoring the bond state change for a device with an unexpected address: '%ls'",
+ Q_FUNC_INFO, qUtf16Printable(deviceId));
+ return;
+ }
+
+ if (!m_pendingPairingRequests.contains(deviceAddress))
+ return;
+
+ switch (bondState) {
+ case QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_INVALID:
+ qCWarning(
+ QT_BT_OHOS, "%s: pairing with the device %ls failed with the unbond cause %d",
+ Q_FUNC_INFO, qUtf16Printable(deviceAddress.toString()),
+ optUnbondCause ? static_cast<int>(*optUnbondCause) : -1);
+ m_pendingPairingRequests.remove(deviceAddress);
+ rearmPendingPairingRequestsTimer();
+ emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
+ break;
+ case QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDING:
+ break;
+ case QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDED:
+ m_pendingPairingRequests.remove(deviceAddress);
+ rearmPendingPairingRequestsTimer();
+ Q_EMIT m_qBluetoothLocalDevice->pairingFinished(
+ deviceAddress, QBluetoothLocalDevice::Paired);
+ break;
+ }
+ });
+
+ QObject::connect(
+ m_remoteDeviceProxy.get(),
+ &QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::pairDeviceFailed,
+ this,
+ [this](const QString &deviceId) {
+ const QBluetoothAddress deviceAddress(deviceId);
+ if (!m_pendingPairingRequests.contains(deviceAddress))
+ return;
+
+ qCWarning(
+ QT_BT_OHOS, "%s: the pairing request for the device %ls was rejected", Q_FUNC_INFO,
+ qUtf16Printable(deviceId));
+ m_pendingPairingRequests.remove(deviceAddress);
+ rearmPendingPairingRequestsTimer();
+ emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
+ });
+
auto emitMissingPermissionsError = [this]() {
emitAsyncError(QBluetoothLocalDevice::MissingPermissionsError);
};
QObject::connect(
QtOhosBluetooth::QOhosBluetoothAccessProxy::instance().get(),
&QtOhosBluetooth::QOhosBluetoothAccessProxy::missingPermission,
this, emitMissingPermissionsError);
QObject::connect(
QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance().get(),
&QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::missingPermission,
this, emitMissingPermissionsError);
+ QObject::connect(
+ m_remoteDeviceProxy.get(),
+ &QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::missingPermission,
+ this, emitMissingPermissionsError);
+
+ m_pendingPairingRequestsTimer.setSingleShot(true);
+ QObject::connect(
+ &m_pendingPairingRequestsTimer, &QTimer::timeout, this,
+ [this]() {
+ handleExpiredPendingPairingRequests();
+ });
+
m_hostModeRequestTimer.setSingleShot(true);
QObject::connect(
&m_hostModeRequestTimer, &QTimer::timeout, this,
[this]() {
if (!m_optHostModeRequest)
return;
if (m_optHostModeRequest->awaitingOutcome
|| m_optHostModeRequest->deadline.hasExpired()) {
resolveHostModeRequestFromAdapterState();
if (m_optHostModeRequest && !m_hostModeRequestTimer.isActive())
giveUpHostModeRequest();
return;
}
issueHostModeRequest();
});
}
bool QOhosBluetoothLocalDevicePrivate::isValid() const
{
return m_isValid;
}
std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> QOhosBluetoothLocalDevicePrivate::tryGetBluetoothState() const
{
return QtOhosBluetooth::QOhosBluetoothAccessProxy::instance()->tryGetBluetoothState();
}
+ std::optional<QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState> QOhosBluetoothLocalDevicePrivate::tryGetPairState(
+ const QBluetoothAddress &address) const
+ {
+ return m_remoteDeviceProxy->tryGetPairState(address.toString());
+ }
+
void QOhosBluetoothLocalDevicePrivate::requestHostMode(
QBluetoothLocalDevice::HostMode requestedHostMode)
{
registerHostModeRequest(requestedHostMode, false);
}
void QOhosBluetoothLocalDevicePrivate::requestPowerOn()
{
registerHostModeRequest(QBluetoothLocalDevice::HostMode::HostConnectable, true);
}
void QOhosBluetoothLocalDevicePrivate::registerHostModeRequest(
QBluetoothLocalDevice::HostMode target, bool anyPoweredOnAccepted)
{
m_hostModeRequestTimer.stop();
m_optHostModeRequest = HostModeRequest {
.target = target,
.anyPoweredOnAccepted = anyPoweredOnAccepted,
.awaitingOutcome = false,
.powerSwitchIssued = false,
.scanModeIssued = false,
.deadline = QDeadlineTimer(hostModeRequestGiveUpDelay),
};
issueHostModeRequest();
}
bool QOhosBluetoothLocalDevicePrivate::isRequestSatisfiedBy(
const HostModeRequest &request, QBluetoothLocalDevice::HostMode hostMode,
bool adapterPoweredOn)
{
if (request.anyPoweredOnAccepted)
return adapterPoweredOn && hostMode != QBluetoothLocalDevice::HostMode::HostPoweredOff;
if (request.target == QBluetoothLocalDevice::HostMode::HostPoweredOff)
return !adapterPoweredOn;
if (request.target == QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry)
return hostMode == QBluetoothLocalDevice::HostMode::HostDiscoverableLimitedInquiry
|| hostMode == QBluetoothLocalDevice::HostMode::HostDiscoverable;
return hostMode == request.target;
}
void QOhosBluetoothLocalDevicePrivate::resolveHostModeRequestFromAdapterState()
{
const auto optBluetoothState = tryGetBluetoothState();
if (!optBluetoothState)
return;
handleAdapterHostModeChanged(
getBluetoothLocalDeviceHostMode(*optBluetoothState), optBluetoothState,
HostModeUpdateSource::LiveRead);
}
void QOhosBluetoothLocalDevicePrivate::issueHostModeRequest()
{
Q_ASSERT(m_optHostModeRequest);
using BluetoothState = QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState;
const auto target = m_optHostModeRequest->target;
const auto optBluetoothState = tryGetBluetoothState();
if (optBluetoothState && isAdapterStateSettled(*optBluetoothState)
&& isRequestSatisfiedBy(
*m_optHostModeRequest, getBluetoothLocalDeviceHostMode(*optBluetoothState),
*optBluetoothState == BluetoothState::STATE_ON)) {
cancelHostModeRequest();
return;
}
const auto handleOutcome =
[this](std::optional<QOhosBluetoothErrorCode> optErrorCode, bool isPowerSwitchCall) {
if (!optErrorCode) {
if (isPowerSwitchCall) {
awaitHostModeRequestOutcome(true);
return;
}
m_optHostModeRequest->scanModeIssued = true;
resolveHostModeRequestFromAdapterState();
if (m_optHostModeRequest && !m_hostModeRequestTimer.isActive())
awaitHostModeRequestOutcome(false);
return;
}
if (*optErrorCode == QOhosBluetoothErrorCode::OperationFailed
|| *optErrorCode == QOhosBluetoothErrorCode::BluetoothDisabled) {
if (isPowerSwitchCall)
awaitHostModeRequestOutcome(true);
else
scheduleHostModeRetry();
return;
}
if (*optErrorCode == QOhosBluetoothErrorCode::PermissionDenied) {
cancelHostModeRequest();
return;
}
qCWarning(
QT_BT_OHOS, "%s: the host mode request was rejected with error code: %u",
Q_FUNC_INFO, qToUnderlying(*optErrorCode));
giveUpHostModeRequest();
};
if (!optBluetoothState) {
scheduleHostModeRetry();
return;
}
if (!isAdapterStateSettled(*optBluetoothState)) {
awaitHostModeRequestOutcome(false);
return;
}
m_optHostModeRequest->scanModeIssued = false;
if (target == QBluetoothLocalDevice::HostMode::HostPoweredOff) {
Q_ASSERT(*optBluetoothState == BluetoothState::STATE_ON);
handleOutcome(
QtOhosBluetooth::QOhosBluetoothAccessProxy::instance()->trySetBluetoothEnabled(false),
true);
return;
}
if (*optBluetoothState != BluetoothState::STATE_ON) {
handleOutcome(
QtOhosBluetooth::QOhosBluetoothAccessProxy::instance()->trySetBluetoothEnabled(true),
true);
return;
}
handleOutcome(
QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->trySetBluetoothScanMode(
mapHostModeToOhosScanMode(target)),
false);
}
void QOhosBluetoothLocalDevicePrivate::scheduleHostModeRetry()
{
Q_ASSERT(m_optHostModeRequest);
if (m_optHostModeRequest->deadline.hasExpired()) {
giveUpHostModeRequest();
return;
}
m_optHostModeRequest->awaitingOutcome = false;
m_optHostModeRequest->powerSwitchIssued = false;
m_hostModeRequestTimer.start(hostModeRequestRetryDelay);
}
bool QOhosBluetoothLocalDevicePrivate::isPowerSwitchOutcomePending(
const HostModeRequest &request, bool adapterPoweredOn)
{
if (!request.powerSwitchIssued)
return false;
return request.target == QBluetoothLocalDevice::HostMode::HostPoweredOff
? adapterPoweredOn
: !adapterPoweredOn;
}
void QOhosBluetoothLocalDevicePrivate::awaitHostModeRequestOutcome(bool powerSwitchIssued)
{
Q_ASSERT(m_optHostModeRequest);
const auto remainingTime = m_optHostModeRequest->deadline.remainingTimeAsDuration();
if (remainingTime <= std::chrono::milliseconds::zero()) {
giveUpHostModeRequest();
return;
}
m_optHostModeRequest->awaitingOutcome = true;
m_optHostModeRequest->powerSwitchIssued = powerSwitchIssued;
m_hostModeRequestTimer.start(
std::chrono::ceil<std::chrono::milliseconds>(remainingTime));
}
void QOhosBluetoothLocalDevicePrivate::giveUpHostModeRequest()
{
Q_ASSERT(m_optHostModeRequest);
qCWarning(
QT_BT_OHOS, "%s: giving up on the requested host mode %d, the adapter stays in %d",
Q_FUNC_INFO, static_cast<int>(m_optHostModeRequest->target),
static_cast<int>(m_lastHostMode));
cancelHostModeRequest();
emitAsyncError(QBluetoothLocalDevice::Error::UnknownError);
}
void QOhosBluetoothLocalDevicePrivate::cancelHostModeRequest()
{
m_hostModeRequestTimer.stop();
m_optHostModeRequest.reset();
}
void QOhosBluetoothLocalDevicePrivate::handleAdapterHostModeChanged(
QBluetoothLocalDevice::HostMode hostMode,
std::optional<QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState> optBluetoothState,
HostModeUpdateSource source)
{
const auto hostModeChanged = m_lastHostMode != hostMode;
m_lastHostMode = hostMode;
const auto adapterSettled = optBluetoothState && isAdapterStateSettled(*optBluetoothState);
const auto adapterPoweredOn =
optBluetoothState == QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON;
if (m_optHostModeRequest && adapterSettled) {
const auto scanModeApplied = source == HostModeUpdateSource::LiveRead
|| m_optHostModeRequest->anyPoweredOnAccepted
|| m_optHostModeRequest->target == QBluetoothLocalDevice::HostMode::HostPoweredOff
|| m_optHostModeRequest->scanModeIssued;
if (scanModeApplied
&& isRequestSatisfiedBy(*m_optHostModeRequest, hostMode, adapterPoweredOn)) {
cancelHostModeRequest();
} else if (!isPowerSwitchOutcomePending(*m_optHostModeRequest, adapterPoweredOn)) {
if (m_optHostModeRequest->powerSwitchIssued && adapterPoweredOn)
m_optHostModeRequest->deadline = QDeadlineTimer(hostModeRequestGiveUpDelay);
scheduleHostModeRetry();
}
}
if (hostModeChanged) {
QMetaObject::invokeMethod(
m_qBluetoothLocalDevice,
[qBluetoothLocalDevice = m_qBluetoothLocalDevice, hostMode]() {
Q_EMIT qBluetoothLocalDevice->hostModeStateChanged(hostMode);
},
Qt::QueuedConnection);
}
}
+ bool QOhosBluetoothLocalDevicePrivate::pairDevice(const QBluetoothAddress &address)
+ {
+ const auto addressStr = address.toString().toStdString();
+ if (m_pendingPairingRequests.contains(address)) {
+ qCDebug(
+ QT_BT_OHOS,
+ "%s: pairing request ignored due to already pending pairing process to the device with address %s",
+ Q_FUNC_INFO, addressStr.c_str());
+ return true;
+ }
+
+ m_pendingPairingRequests.insert(address, QDeadlineTimer(pairingRequestTimeout));
+ rearmPendingPairingRequestsTimer();
+
+ if (!m_remoteDeviceProxy->pairDevice(address.toString())) {
+ m_pendingPairingRequests.remove(address);
+ rearmPendingPairingRequestsTimer();
+ return false;
+ }
+
+ return true;
+ }
+
+ void QOhosBluetoothLocalDevicePrivate::cancelPendingPairingRequests()
+ {
+ if (m_pendingPairingRequests.isEmpty())
+ return;
+
+ m_pendingPairingRequests.clear();
+ rearmPendingPairingRequestsTimer();
+ emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
+ }
+
void QOhosBluetoothLocalDevicePrivate::emitAsyncError(QBluetoothLocalDevice::Error error)
{
QMetaObject::invokeMethod(
m_qBluetoothLocalDevice,
[this, error]() {
Q_EMIT m_qBluetoothLocalDevice->errorOccurred(error);
},
Qt::QueuedConnection);
}
+ void QOhosBluetoothLocalDevicePrivate::rearmPendingPairingRequestsTimer()
+ {
+ if (m_pendingPairingRequests.isEmpty()) {
+ m_pendingPairingRequestsTimer.stop();
+ return;
+ }
+
+ const auto earliestDeadline = std::min_element(
+ m_pendingPairingRequests.cbegin(), m_pendingPairingRequests.cend());
+
+ m_pendingPairingRequestsTimer.start(
+ std::max(
+ std::chrono::ceil<std::chrono::milliseconds>(earliestDeadline->remainingTimeAsDuration()),
+ std::chrono::milliseconds::zero()));
+ }
+
+ void QOhosBluetoothLocalDevicePrivate::handleExpiredPendingPairingRequests()
+ {
+ QList<QBluetoothAddress> expiredAddresses;
+ for (auto pendingPairingRequest = m_pendingPairingRequests.cbegin();
+ pendingPairingRequest != m_pendingPairingRequests.cend(); ++pendingPairingRequest) {
+ if (pendingPairingRequest.value().hasExpired())
+ expiredAddresses.append(pendingPairingRequest.key());
+ }
+
+ for (const auto &expiredAddress : expiredAddresses)
+ m_pendingPairingRequests.remove(expiredAddress);
+
+ rearmPendingPairingRequestsTimer();
+
+ for (const auto &expiredAddress : expiredAddresses) {
+ const auto optPairState = tryGetPairState(expiredAddress);
+ if (optPairState
+ && *optPairState == QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::BondState::BOND_STATE_BONDED) {
+ Q_EMIT m_qBluetoothLocalDevice->pairingFinished(
+ expiredAddress, QBluetoothLocalDevice::Paired);
+ continue;
+ }
+
+ qCWarning(
+ QT_BT_OHOS, "%s: pairing with the device %ls not confirmed by the system in time",
+ Q_FUNC_INFO, qUtf16Printable(expiredAddress.toString()));
+ emitAsyncError(QBluetoothLocalDevice::Error::PairingError);
+ }
+ }
+
}
QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate() = default;
QBluetoothLocalDevicePrivate::~QBluetoothLocalDevicePrivate() = default;
QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent)
: QBluetoothLocalDevice(QBluetoothAddress(), parent)
{
}
QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent)
: QObject(parent)
, d_ptr(new QOhosBluetoothLocalDevicePrivate(this, address))
{
registerQBluetoothLocalDeviceMetaType();
}
QString QBluetoothLocalDevice::name() const
{
if (!d_ptr->isValid()) {
qCWarning(
QT_BT_OHOS, "%s: local device is not valid. Can't get local device name.", Q_FUNC_INFO);
return QString();
}
const auto optLocalDeviceName = QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->tryGetLocalDeviceName();
return optLocalDeviceName
? *optLocalDeviceName
: QString();
}
QBluetoothAddress QBluetoothLocalDevice::address() const
{
return {};
}
void QBluetoothLocalDevice::powerOn()
{
if (!d_ptr->isValid()) {
qCWarning(QT_BT_OHOS, "%s: local device is not valid. Ignoring...", Q_FUNC_INFO);
return;
}
if (hostMode() != HostMode::HostPoweredOff) {
qCWarning(QT_BT_OHOS, "%s: bluetooth is not powered off. Ignoring...", Q_FUNC_INFO);
return;
}
static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->requestPowerOn();
}
void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
{
if (!d_ptr->isValid()) {
qCWarning(QT_BT_OHOS, "%s: local device is not valid. Ignoring...", Q_FUNC_INFO);
return;
}
auto *ohosBluetoothLocalDevicePriv = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr);
const auto optBluetoothState = ohosBluetoothLocalDevicePriv->tryGetBluetoothState();
const auto adapterSettled = optBluetoothState && isAdapterStateSettled(*optBluetoothState);
if (adapterSettled && mode == QBluetoothLocalDevice::HostMode::HostPoweredOff
&& *optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON) {
qCWarning(
QT_BT_OHOS,
"%s: Bluetooth is not in powered on state. Cancelling the pending host mode request.",
Q_FUNC_INFO);
ohosBluetoothLocalDevicePriv->cancelHostModeRequest();
return;
}
ohosBluetoothLocalDevicePriv->requestHostMode(mode);
}
QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
{
if (!d_ptr->isValid()) {
qCWarning(
QT_BT_OHOS, "%s: local device is not valid. Reporting powered off host mode.",
Q_FUNC_INFO);
return QBluetoothLocalDevice::HostPoweredOff;
}
const auto optBluetoothState = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetBluetoothState();
if (!optBluetoothState)
return QBluetoothLocalDevice::HostPoweredOff;
return getBluetoothLocalDeviceHostMode(*optBluetoothState);
}
QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
{
return {};
}
QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
{
const auto optLocalDeviceName =
QtOhosBluetooth::QOhosBluetoothLocalDeviceProxy::instance()->tryGetLocalDeviceName();
if (!optLocalDeviceName)
return {};
QBluetoothHostInfo bluetoothHostInfo;
bluetoothHostInfo.setName(*optLocalDeviceName);
return { bluetoothHostInfo };
}
- void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &, Pairing)
+ void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
{
+ auto emitPairingError = [this]() {
+ QMetaObject::invokeMethod(
+ this,
+ [this]() {
+ Q_EMIT errorOccurred(QBluetoothLocalDevice::PairingError);
+ },
+ Qt::QueuedConnection);
+ };
+
+ auto emitPairingFinishedAsync = [this, address](Pairing finishedPairing) {
+ QMetaObject::invokeMethod(
+ this,
+ [this, address, finishedPairing]() {
+ Q_EMIT pairingFinished(address, finishedPairing);
+ },
+ Qt::QueuedConnection);
+ };
+
+ if (!isValid() || address.isNull()) {
+ qCWarning(
+ QT_BT_OHOS, "%s: local device is not valid or the device address is null. Ignoring...",
+ Q_FUNC_INFO);
+ emitPairingError();
+ return;
+ }
+
+ const auto optBluetoothState = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetBluetoothState();
+ if (optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON) {
+ qCWarning(QT_BT_OHOS, "%s: bluetooth not in powered on state. Ignoring...", Q_FUNC_INFO);
+ emitPairingError();
+ return;
+ }
+
+ switch (pairing) {
+ case AuthorizedPaired:
+ qCWarning(
+ QT_BT_OHOS,
+ "%s: AuthorizedPaired not supported on HarmonyOS. Falling back to Paired.",
+ Q_FUNC_INFO);
+ Q_FALLTHROUGH();
+ case Paired:
+ if (pairingStatus(address) == QBluetoothLocalDevice::Pairing::Paired) {
+ qCDebug(
+ QT_BT_OHOS, "%s: Already paired with the device %ls. Ignoring pairing request.",
+ Q_FUNC_INFO, qUtf16Printable(address.toString()));
+ emitPairingFinishedAsync(QBluetoothLocalDevice::Pairing::Paired);
+ break;
+ }
+
+ if (!static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->pairDevice(address))
+ emitPairingError();
+ break;
+ case Unpaired:
+ if (pairingStatus(address) == QBluetoothLocalDevice::Pairing::Unpaired) {
+ qCDebug(
+ QT_BT_OHOS, "%s: Not paired with the device %ls. Ignoring unpairing request.",
+ Q_FUNC_INFO, qUtf16Printable(address.toString()));
+ emitPairingFinishedAsync(QBluetoothLocalDevice::Pairing::Unpaired);
+ break;
+ }
+
+ qCWarning(
+ QT_BT_OHOS, "%s: Unpairing not supported on HarmonyOS for paired devices.",
+ Q_FUNC_INFO);
+ emitPairingError();
+ break;
+ }
}
- QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(const QBluetoothAddress &) const
+ QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(const QBluetoothAddress &address) const
{
- return Unpaired;
+ if (!d_ptr->isValid()) {
+ qCWarning(
+ QT_BT_OHOS, "%s: local device is not valid. Reporting unpaired device.", Q_FUNC_INFO);
+ return Unpaired;
+ }
+
+ if (address.isNull()) {
+ qCWarning(
+ QT_BT_OHOS, "%s: device address is null. Reporting unpaired device.", Q_FUNC_INFO);
+ return Unpaired;
+ }
+
+ const auto optBluetoothState = static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetBluetoothState();
+ if (optBluetoothState != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON)
+ return Unpaired;
+
+ const auto optPairState =
+ static_cast<QOhosBluetoothLocalDevicePrivate *>(d_ptr)->tryGetPairState(address);
+ if (!optPairState)
+ return QBluetoothLocalDevice::Pairing::Unpaired;
+
+ return mapOhosBondStateToLocalDevicePairing(*optPairState);
}
QT_END_NAMESPACE