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 "qbluetoothservicediscoveryagent_p.h"
++
++ #include <QtBluetooth/qbluetoothaddress.h>
++ #include <QtBluetooth/qbluetoothdeviceinfo.h>
++ #include <QtBluetooth/qbluetoothserviceinfo.h>
++ #include <QtBluetooth/qbluetoothservicediscoveryagent.h>
++ #include <QtBluetooth/qbluetoothuuid.h>
++ #include <QtBluetooth/private/qohosbluetoothaccess_p.h>
++ #include <QtBluetooth/private/qohosbluetoothremotedevice_p.h>
++
++ #include <QtCore/qloggingcategory.h>
++ #include <QtCore/qvariant.h>
++
++ #include <algorithm>
++ #include <optional>
++
++ QT_BEGIN_NAMESPACE
++
++ using QtOhosBluetooth::QOhosBluetoothErrorCode;
++
++ Q_DECLARE_LOGGING_CATEGORY(QT_BT_OHOS)
++
++ namespace {
++
++ bool uuidFilteredOut(const QList<QBluetoothUuid> &uuidFilter, const QBluetoothUuid &uuid)
++ {
++ return !uuidFilter.isEmpty() && !uuidFilter.contains(uuid);
++ }
++
++ QBluetoothServiceInfo makeRfcommServiceInfo(
++ const QBluetoothDeviceInfo &deviceInfo, const QBluetoothUuid &serviceUuid)
++ {
++ constexpr quint8 unknownRfcommServerChannel = 0;
++ constexpr quint16 serialPortProfileVersion = 0x0100;
++
++ QBluetoothServiceInfo serviceInfo;
++ serviceInfo.setDevice(deviceInfo);
++
++ QBluetoothServiceInfo::Sequence l2capProtocol;
++ l2capProtocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::L2cap));
++
++ QBluetoothServiceInfo::Sequence rfcommProtocol;
++ rfcommProtocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::Rfcomm));
++ rfcommProtocol << QVariant::fromValue(unknownRfcommServerChannel);
++
++ QBluetoothServiceInfo::Sequence protocolDescriptorList;
++ protocolDescriptorList << QVariant::fromValue(l2capProtocol);
++ protocolDescriptorList << QVariant::fromValue(rfcommProtocol);
++ serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList);
++
++ QBluetoothServiceInfo::Sequence serialPortProfile;
++ QBluetoothServiceInfo::Sequence serialPortProfileClassId;
++ serialPortProfileClassId
++ << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort));
++ serialPortProfileClassId << QVariant::fromValue(serialPortProfileVersion);
++ serialPortProfile << QVariant::fromValue(serialPortProfileClassId);
++ serviceInfo.setAttribute(
++ QBluetoothServiceInfo::BluetoothProfileDescriptorList, serialPortProfile);
++
++ QBluetoothServiceInfo::Sequence browseGroup;
++ browseGroup << QVariant::fromValue(
++ QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::PublicBrowseGroup));
++ serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, browseGroup);
++
++ QBluetoothServiceInfo::Sequence serviceClassIds;
++ serviceClassIds << QVariant::fromValue(serviceUuid);
++ serviceClassIds << QVariant::fromValue(
++ QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort));
++ serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, serviceClassIds);
++
++ serviceInfo.setServiceUuid(serviceUuid);
++ serviceInfo.setServiceProvider(QBluetoothServiceDiscoveryAgent::tr("QtOhosBluetooth"));
++ serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr("Serial Port Profile"));
++
++ return serviceInfo;
++ }
++
++ }
++
++ QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(
++ QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &)
++ : error(QBluetoothServiceDiscoveryAgent::NoError)
++ , state(Inactive)
++ , mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery)
++ , singleDevice(false)
++ , m_remoteDeviceProxy(QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::instance())
++ , q_ptr(qp)
++ {
++ QObject::connect(
++ m_remoteDeviceProxy.get(),
++ &QtOhosBluetooth::QOhosBluetoothRemoteDeviceProxy::remoteDeviceServicesRead,
++ q_ptr,
++ [this](const QtOhosBluetooth::RemoteDeviceServices &remoteDeviceServices) {
++ reportRemoteDeviceServices(remoteDeviceServices);
++ });
++ }
++
++ QBluetoothServiceDiscoveryAgentPrivate::~QBluetoothServiceDiscoveryAgentPrivate() = default;
++
++ void QBluetoothServiceDiscoveryAgentPrivate::reportErrorAsync(
++ QBluetoothServiceDiscoveryAgent::Error discoveryError, const QString &discoveryErrorString)
++ {
++ error = discoveryError;
++ errorString = discoveryErrorString;
++ QMetaObject::invokeMethod(
++ q_ptr,
++ [this, discoveryError]() {
++ Q_EMIT q_ptr->errorOccurred(discoveryError);
++ },
++ Qt::QueuedConnection);
++ }
++
++ void QBluetoothServiceDiscoveryAgentPrivate::finishDeviceDiscoveryAsync()
++ {
++ QMetaObject::invokeMethod(
++ q_ptr,
++ [this]() {
++ if (discoveryState() != ServiceDiscovery)
++ return;
++
++ _q_serviceDiscoveryFinished();
++ },
++ Qt::QueuedConnection);
++ }
++
++ void QBluetoothServiceDiscoveryAgentPrivate::reportRemoteDeviceServices(
++ const QtOhosBluetooth::RemoteDeviceServices &remoteDeviceServices)
++ {
++ if (m_optRequestedDeviceId != remoteDeviceServices.deviceId)
++ return;
++
++ m_optRequestedDeviceId.reset();
++
++ const auto addressStr = QString::fromStdString(remoteDeviceServices.deviceId);
++ const QBluetoothDeviceInfo bluetoothDeviceInfo(
++ QBluetoothAddress(addressStr),
++ remoteDeviceServices.optDeviceName
++ ? QString::fromStdString(*remoteDeviceServices.optDeviceName)
++ : QString(),
++ remoteDeviceServices.optClassOfDevice.value_or(0));
++
++ const auto discoveredServicesCountBeforeDevice = discoveredServices.size();
++
++ auto reportServiceForUuid = [&](const QBluetoothUuid &uuid) {
++ const auto serviceInfo = makeRfcommServiceInfo(bluetoothDeviceInfo, uuid);
++ if (isDuplicatedService(serviceInfo))
++ return;
++
++ discoveredServices.append(serviceInfo);
++ QMetaObject::invokeMethod(
++ q_ptr,
++ [this, serviceInfo]() {
++ Q_EMIT q_ptr->serviceDiscovered(serviceInfo);
++ },
++ Qt::QueuedConnection);
++ };
++
++ if (remoteDeviceServices.optProfileUuids) {
++ for (const auto &profileUuid : *remoteDeviceServices.optProfileUuids) {
++ const auto uuid = QBluetoothUuid(QString::fromStdString(profileUuid));
++ if (uuidFilteredOut(uuidFilter, uuid))
++ continue;
++
++ reportServiceForUuid(uuid);
++ }
++ } else {
++ qCWarning(
++ QT_BT_OHOS,
++ "%s: could not read the remote standard profile UUIDs of the device %ls. Continuing...",
++ Q_FUNC_INFO, qUtf16Printable(addressStr));
++ }
++
++ if (singleDevice && !uuidFilter.isEmpty() && !remoteDeviceServices.lowEnergyOnly) {
++ for (const auto &requestedUuid : uuidFilter)
++ reportServiceForUuid(requestedUuid);
++ }
++
++ if (!remoteDeviceServices.optProfileUuids && singleDevice
++ && discoveredServices.size() == discoveredServicesCountBeforeDevice) {
++ reportErrorAsync(
++ QBluetoothServiceDiscoveryAgent::InputOutputError,
++ QBluetoothServiceDiscoveryAgent::tr("Cannot obtain service uuids"));
++ }
++
++ finishDeviceDiscoveryAsync();
++ }
++
++ void QBluetoothServiceDiscoveryAgentPrivate::start(const QBluetoothAddress &address)
++ {
++ auto accessProxy = QtOhosBluetooth::QOhosBluetoothAccessProxy::instance();
++ if (accessProxy->tryGetBluetoothState() != QtOhosBluetooth::QOhosBluetoothAccessProxy::BluetoothState::STATE_ON) {
++ qCWarning(QT_BT_OHOS, "%s: bluetooth not in powered on state. Ignoring...", Q_FUNC_INFO);
++ discoveredDevices.clear();
++ reportErrorAsync(
++ QBluetoothServiceDiscoveryAgent::PoweredOffError,
++ QBluetoothServiceDiscoveryAgent::tr("Device is powered off"));
++ finishDeviceDiscoveryAsync();
++ return;
++ }
++
++ m_optRequestedDeviceId = address.toString().toStdString();
++
++ const auto optErrorCode = m_remoteDeviceProxy->requestRemoteDeviceServices(address.toString());
++ if (!optErrorCode)
++ return;
++
++ m_optRequestedDeviceId.reset();
++
++ qCWarning(
++ QT_BT_OHOS, "%s: could not start reading the services of the device %ls, error code: %u",
++ Q_FUNC_INFO, qUtf16Printable(address.toString()), qToUnderlying(*optErrorCode));
++
++ if (singleDevice) {
++ if (*optErrorCode == QOhosBluetoothErrorCode::PermissionDenied) {
++ reportErrorAsync(
++ QBluetoothServiceDiscoveryAgent::MissingPermissionsError,
++ QBluetoothServiceDiscoveryAgent::tr("Missing bluetooth permission"));
++ } else {
++ reportErrorAsync(
++ QBluetoothServiceDiscoveryAgent::InputOutputError,
++ QBluetoothServiceDiscoveryAgent::tr("Cannot obtain service uuids"));
++ }
++ }
++
++ finishDeviceDiscoveryAsync();
++ }
++
++ void QBluetoothServiceDiscoveryAgentPrivate::stop()
++ {
++ m_optRequestedDeviceId.reset();
++ QMetaObject::invokeMethod(q_ptr, &QBluetoothServiceDiscoveryAgent::canceled, Qt::QueuedConnection);
++ }
++
++ QT_END_NAMESPACE