| /**************************************************************************** |
| ** |
| ** Copyright (C) 2017 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtBluetooth module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qlowenergycontrollerbase_p.h" |
| |
| #include <QtCore/QLoggingCategory> |
| |
| #include <QtBluetooth/QBluetoothLocalDevice> |
| #include <QtBluetooth/QLowEnergyCharacteristicData> |
| #include <QtBluetooth/QLowEnergyDescriptorData> |
| #include <QtBluetooth/QLowEnergyServiceData> |
| |
| QT_BEGIN_NAMESPACE |
| |
| Q_DECLARE_LOGGING_CATEGORY(QT_BT) |
| |
| QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() |
| : QObject() |
| { |
| } |
| |
| QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() |
| { |
| } |
| |
| bool QLowEnergyControllerPrivate::isValidLocalAdapter() |
| { |
| #if defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN) |
| return true; |
| #endif |
| if (localAdapter.isNull()) |
| return false; |
| |
| const QList<QBluetoothHostInfo> foundAdapters = QBluetoothLocalDevice::allDevices(); |
| bool adapterFound = false; |
| |
| for (const QBluetoothHostInfo &info : foundAdapters) { |
| if (info.address() == localAdapter) { |
| adapterFound = true; |
| break; |
| } |
| } |
| |
| return adapterFound; |
| } |
| |
| |
| void QLowEnergyControllerPrivate::setError( |
| QLowEnergyController::Error newError) |
| { |
| Q_Q(QLowEnergyController); |
| error = newError; |
| |
| switch (newError) { |
| case QLowEnergyController::UnknownRemoteDeviceError: |
| errorString = QLowEnergyController::tr("Remote device cannot be found"); |
| break; |
| case QLowEnergyController::InvalidBluetoothAdapterError: |
| errorString = QLowEnergyController::tr("Cannot find local adapter"); |
| break; |
| case QLowEnergyController::NetworkError: |
| errorString = QLowEnergyController::tr("Error occurred during connection I/O"); |
| break; |
| case QLowEnergyController::ConnectionError: |
| errorString = QLowEnergyController::tr("Error occurred trying to connect to remote device."); |
| break; |
| case QLowEnergyController::AdvertisingError: |
| errorString = QLowEnergyController::tr("Error occurred trying to start advertising"); |
| break; |
| case QLowEnergyController::RemoteHostClosedError: |
| errorString = QLowEnergyController::tr("Remote device closed the connection"); |
| break; |
| case QLowEnergyController::AuthorizationError: |
| errorString = QLowEnergyController::tr("Failed to authorize on the remote device"); |
| break; |
| case QLowEnergyController::NoError: |
| return; |
| default: |
| case QLowEnergyController::UnknownError: |
| errorString = QLowEnergyController::tr("Unknown Error"); |
| break; |
| } |
| |
| emit q->error(newError); |
| } |
| |
| void QLowEnergyControllerPrivate::setState( |
| QLowEnergyController::ControllerState newState) |
| { |
| Q_Q(QLowEnergyController); |
| if (state == newState) |
| return; |
| |
| state = newState; |
| if (state == QLowEnergyController::UnconnectedState |
| && role == QLowEnergyController::PeripheralRole) { |
| remoteDevice.clear(); |
| } |
| emit q->stateChanged(state); |
| } |
| |
| QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivate::serviceForHandle( |
| QLowEnergyHandle handle) |
| { |
| ServiceDataMap ¤tList = serviceList; |
| if (role == QLowEnergyController::PeripheralRole) |
| currentList = localServices; |
| |
| const QList<QSharedPointer<QLowEnergyServicePrivate>> values = currentList.values(); |
| for (auto service: values) |
| if (service->startHandle <= handle && handle <= service->endHandle) |
| return service; |
| |
| return QSharedPointer<QLowEnergyServicePrivate>(); |
| } |
| |
| /*! |
| Returns a valid characteristic if the given handle is the |
| handle of the characteristic itself or one of its descriptors |
| */ |
| QLowEnergyCharacteristic QLowEnergyControllerPrivate::characteristicForHandle( |
| QLowEnergyHandle handle) |
| { |
| QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(handle); |
| if (service.isNull()) |
| return QLowEnergyCharacteristic(); |
| |
| if (service->characteristicList.isEmpty()) |
| return QLowEnergyCharacteristic(); |
| |
| // check whether it is the handle of a characteristic header |
| if (service->characteristicList.contains(handle)) |
| return QLowEnergyCharacteristic(service, handle); |
| |
| // check whether it is the handle of the characteristic value or its descriptors |
| QList<QLowEnergyHandle> charHandles = service->characteristicList.keys(); |
| std::sort(charHandles.begin(), charHandles.end()); |
| for (int i = charHandles.size() - 1; i >= 0; i--) { |
| if (charHandles.at(i) > handle) |
| continue; |
| |
| return QLowEnergyCharacteristic(service, charHandles.at(i)); |
| } |
| |
| return QLowEnergyCharacteristic(); |
| } |
| |
| /*! |
| Returns a valid descriptor if \a handle belongs to a descriptor; |
| otherwise an invalid one. |
| */ |
| QLowEnergyDescriptor QLowEnergyControllerPrivate::descriptorForHandle( |
| QLowEnergyHandle handle) |
| { |
| const QLowEnergyCharacteristic matchingChar = characteristicForHandle(handle); |
| if (!matchingChar.isValid()) |
| return QLowEnergyDescriptor(); |
| |
| const QLowEnergyServicePrivate::CharData charData = matchingChar. |
| d_ptr->characteristicList[matchingChar.attributeHandle()]; |
| |
| if (charData.descriptorList.contains(handle)) |
| return QLowEnergyDescriptor(matchingChar.d_ptr, matchingChar.attributeHandle(), |
| handle); |
| |
| return QLowEnergyDescriptor(); |
| } |
| |
| /*! |
| Returns the length of the updated characteristic value. |
| */ |
| quint16 QLowEnergyControllerPrivate::updateValueOfCharacteristic( |
| QLowEnergyHandle charHandle,const QByteArray &value, bool appendValue) |
| { |
| QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); |
| if (!service.isNull()) { |
| CharacteristicDataMap::iterator charIt = service->characteristicList.find(charHandle); |
| if (charIt != service->characteristicList.end()) { |
| QLowEnergyServicePrivate::CharData &charDetails = charIt.value(); |
| |
| if (appendValue) |
| charDetails.value += value; |
| else |
| charDetails.value = value; |
| |
| return charDetails.value.size(); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /*! |
| Returns the length of the updated descriptor value. |
| */ |
| quint16 QLowEnergyControllerPrivate::updateValueOfDescriptor( |
| QLowEnergyHandle charHandle, QLowEnergyHandle descriptorHandle, |
| const QByteArray &value, bool appendValue) |
| { |
| QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle); |
| if (!service.isNull()) { |
| CharacteristicDataMap::iterator charIt = service->characteristicList.find(charHandle); |
| if (charIt != service->characteristicList.end()) { |
| QLowEnergyServicePrivate::CharData &charDetails = charIt.value(); |
| |
| DescriptorDataMap::iterator descIt = charDetails.descriptorList.find(descriptorHandle); |
| if (descIt != charDetails.descriptorList.end()) { |
| QLowEnergyServicePrivate::DescData &descDetails = descIt.value(); |
| |
| if (appendValue) |
| descDetails.value += value; |
| else |
| descDetails.value = value; |
| |
| return descDetails.value.size(); |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| void QLowEnergyControllerPrivate::invalidateServices() |
| { |
| for (QSharedPointer<QLowEnergyServicePrivate> service : serviceList.values()) |
| service->setController(nullptr); |
| |
| for (QSharedPointer<QLowEnergyServicePrivate> service : localServices.values()) |
| service->setController(nullptr); |
| |
| serviceList.clear(); |
| localServices.clear(); |
| lastLocalHandle = {}; |
| } |
| |
| QLowEnergyService *QLowEnergyControllerPrivate::addServiceHelper( |
| const QLowEnergyServiceData &service) |
| { |
| // Spec says services "should" be grouped by uuid length (16-bit first, then 128-bit). |
| // Since this is not mandatory, we ignore it here and let the caller take responsibility |
| // for it. |
| |
| const auto servicePrivate = QSharedPointer<QLowEnergyServicePrivate>::create(); |
| servicePrivate->setController(this); |
| servicePrivate->state = QLowEnergyService::LocalService; |
| servicePrivate->uuid = service.uuid(); |
| servicePrivate->type = service.type() == QLowEnergyServiceData::ServiceTypePrimary |
| ? QLowEnergyService::PrimaryService : QLowEnergyService::IncludedService; |
| const QList<QLowEnergyService *> includedServices = service.includedServices(); |
| for (QLowEnergyService * const includedService : includedServices) { |
| servicePrivate->includedServices << includedService->serviceUuid(); |
| includedService->d_ptr->type |= QLowEnergyService::IncludedService; |
| } |
| |
| // Spec v4.2, Vol 3, Part G, Section 3. |
| const QLowEnergyHandle oldLastHandle = this->lastLocalHandle; |
| servicePrivate->startHandle = ++this->lastLocalHandle; // Service declaration. |
| this->lastLocalHandle += servicePrivate->includedServices.count(); // Include declarations. |
| const QList<QLowEnergyCharacteristicData> characteristics = service.characteristics(); |
| for (const QLowEnergyCharacteristicData &cd : characteristics) { |
| const QLowEnergyHandle declHandle = ++this->lastLocalHandle; |
| QLowEnergyServicePrivate::CharData charData; |
| charData.valueHandle = ++this->lastLocalHandle; |
| charData.uuid = cd.uuid(); |
| charData.properties = cd.properties(); |
| charData.value = cd.value(); |
| const QList<QLowEnergyDescriptorData> descriptors = cd.descriptors(); |
| for (const QLowEnergyDescriptorData &dd : descriptors) { |
| QLowEnergyServicePrivate::DescData descData; |
| descData.uuid = dd.uuid(); |
| descData.value = dd.value(); |
| charData.descriptorList.insert(++this->lastLocalHandle, descData); |
| } |
| servicePrivate->characteristicList.insert(declHandle, charData); |
| } |
| servicePrivate->endHandle = this->lastLocalHandle; |
| const bool handleOverflow = this->lastLocalHandle <= oldLastHandle; |
| if (handleOverflow) { |
| qCWarning(QT_BT) << "Not enough attribute handles left to create this service"; |
| this->lastLocalHandle = oldLastHandle; |
| return nullptr; |
| } |
| |
| if (localServices.contains(servicePrivate->uuid)) { |
| qWarning() << "Overriding existing local service with uuid" |
| << servicePrivate->uuid; |
| } |
| this->localServices.insert(servicePrivate->uuid, servicePrivate); |
| |
| this->addToGenericAttributeList(service, servicePrivate->startHandle); |
| return new QLowEnergyService(servicePrivate); |
| } |
| |
| QT_END_NAMESPACE |