1// Copyright (C) 2017 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
4#include "qlowenergycontrollerbase_p.h"
5
6#include <QtCore/QLoggingCategory>
7
8#include <QtBluetooth/QBluetoothLocalDevice>
9#include <QtBluetooth/QLowEnergyCharacteristicData>
10#include <QtBluetooth/QLowEnergyDescriptorData>
11#include <QtBluetooth/QLowEnergyServiceData>
12
13QT_BEGIN_NAMESPACE
14
15Q_DECLARE_LOGGING_CATEGORY(QT_BT)
16
17QLowEnergyControllerPrivate::QLowEnergyControllerPrivate()
18 : QObject()
19{
20}
21
22QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate()
23{
24}
25
26bool QLowEnergyControllerPrivate::isValidLocalAdapter()
27{
28#if defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN)
29 return true;
30#endif
31 if (localAdapter.isNull())
32 return false;
33
34 const QList<QBluetoothHostInfo> foundAdapters = QBluetoothLocalDevice::allDevices();
35 bool adapterFound = false;
36
37 for (const QBluetoothHostInfo &info : foundAdapters) {
38 if (info.address() == localAdapter) {
39 adapterFound = true;
40 break;
41 }
42 }
43
44 return adapterFound;
45}
46
47
48void QLowEnergyControllerPrivate::setError(
49 QLowEnergyController::Error newError)
50{
51 Q_Q(QLowEnergyController);
52 error = newError;
53
54 switch (newError) {
55 case QLowEnergyController::UnknownRemoteDeviceError:
56 errorString = QLowEnergyController::tr(s: "Remote device cannot be found");
57 break;
58 case QLowEnergyController::InvalidBluetoothAdapterError:
59 errorString = QLowEnergyController::tr(s: "Cannot find local adapter");
60 break;
61 case QLowEnergyController::NetworkError:
62 errorString = QLowEnergyController::tr(s: "Error occurred during connection I/O");
63 break;
64 case QLowEnergyController::ConnectionError:
65 errorString = QLowEnergyController::tr(s: "Error occurred trying to connect to remote device.");
66 break;
67 case QLowEnergyController::AdvertisingError:
68 errorString = QLowEnergyController::tr(s: "Error occurred trying to start advertising");
69 break;
70 case QLowEnergyController::RemoteHostClosedError:
71 errorString = QLowEnergyController::tr(s: "Remote device closed the connection");
72 break;
73 case QLowEnergyController::AuthorizationError:
74 errorString = QLowEnergyController::tr(s: "Failed to authorize on the remote device");
75 break;
76 case QLowEnergyController::MissingPermissionsError:
77 errorString = QLowEnergyController::tr(s: "Missing permissions error");
78 break;
79 case QLowEnergyController::RssiReadError:
80 errorString = QLowEnergyController::tr(s: "Error reading RSSI value");
81 break;
82 case QLowEnergyController::NoError:
83 return;
84 default:
85 case QLowEnergyController::UnknownError:
86 errorString = QLowEnergyController::tr(s: "Unknown Error");
87 break;
88 }
89
90 emit q->errorOccurred(newError);
91}
92
93void QLowEnergyControllerPrivate::setState(
94 QLowEnergyController::ControllerState newState)
95{
96 qCDebug(QT_BT) << "QLowEnergyControllerPrivate setting state to" << newState;
97 Q_Q(QLowEnergyController);
98 if (state == newState)
99 return;
100
101 state = newState;
102 if (state == QLowEnergyController::UnconnectedState
103 && role == QLowEnergyController::PeripheralRole) {
104 remoteDevice.clear();
105 }
106 emit q->stateChanged(state);
107}
108
109QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivate::serviceForHandle(
110 QLowEnergyHandle handle)
111{
112 ServiceDataMap &currentList = serviceList;
113 if (role == QLowEnergyController::PeripheralRole)
114 currentList = localServices;
115
116 const QList<QSharedPointer<QLowEnergyServicePrivate>> values = currentList.values();
117 for (auto service: values)
118 if (service->startHandle <= handle && handle <= service->endHandle)
119 return service;
120
121 return QSharedPointer<QLowEnergyServicePrivate>();
122}
123
124/*!
125 Returns a valid characteristic if the given handle is the
126 handle of the characteristic itself or one of its descriptors
127 */
128QLowEnergyCharacteristic QLowEnergyControllerPrivate::characteristicForHandle(
129 QLowEnergyHandle handle)
130{
131 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(handle);
132 if (service.isNull())
133 return QLowEnergyCharacteristic();
134
135 if (service->characteristicList.isEmpty())
136 return QLowEnergyCharacteristic();
137
138 // check whether it is the handle of a characteristic header
139 if (service->characteristicList.contains(key: handle))
140 return QLowEnergyCharacteristic(service, handle);
141
142 // check whether it is the handle of the characteristic value or its descriptors
143 QList<QLowEnergyHandle> charHandles = service->characteristicList.keys();
144 std::sort(first: charHandles.begin(), last: charHandles.end());
145 for (qsizetype i = charHandles.size() - 1; i >= 0; --i) {
146 if (charHandles.at(i) > handle)
147 continue;
148
149 return QLowEnergyCharacteristic(service, charHandles.at(i));
150 }
151
152 return QLowEnergyCharacteristic();
153}
154
155/*!
156 Returns a valid descriptor if \a handle belongs to a descriptor;
157 otherwise an invalid one.
158 */
159QLowEnergyDescriptor QLowEnergyControllerPrivate::descriptorForHandle(
160 QLowEnergyHandle handle)
161{
162 const QLowEnergyCharacteristic matchingChar = characteristicForHandle(handle);
163 if (!matchingChar.isValid())
164 return QLowEnergyDescriptor();
165
166 const QLowEnergyServicePrivate::CharData charData = matchingChar.
167 d_ptr->characteristicList[matchingChar.attributeHandle()];
168
169 if (charData.descriptorList.contains(key: handle))
170 return QLowEnergyDescriptor(matchingChar.d_ptr, matchingChar.attributeHandle(),
171 handle);
172
173 return QLowEnergyDescriptor();
174}
175
176/*!
177 Returns the length of the updated characteristic value.
178 */
179quint16 QLowEnergyControllerPrivate::updateValueOfCharacteristic(
180 QLowEnergyHandle charHandle,const QByteArray &value, bool appendValue)
181{
182 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(handle: charHandle);
183 if (!service.isNull()) {
184 CharacteristicDataMap::iterator charIt = service->characteristicList.find(key: charHandle);
185 if (charIt != service->characteristicList.end()) {
186 QLowEnergyServicePrivate::CharData &charDetails = charIt.value();
187
188 if (appendValue)
189 charDetails.value += value;
190 else
191 charDetails.value = value;
192
193 return charDetails.value.size();
194 }
195 }
196
197 return 0;
198}
199
200/*!
201 Returns the length of the updated descriptor value.
202 */
203quint16 QLowEnergyControllerPrivate::updateValueOfDescriptor(
204 QLowEnergyHandle charHandle, QLowEnergyHandle descriptorHandle,
205 const QByteArray &value, bool appendValue)
206{
207 QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(handle: charHandle);
208 if (!service.isNull()) {
209 CharacteristicDataMap::iterator charIt = service->characteristicList.find(key: charHandle);
210 if (charIt != service->characteristicList.end()) {
211 QLowEnergyServicePrivate::CharData &charDetails = charIt.value();
212
213 DescriptorDataMap::iterator descIt = charDetails.descriptorList.find(key: descriptorHandle);
214 if (descIt != charDetails.descriptorList.end()) {
215 QLowEnergyServicePrivate::DescData &descDetails = descIt.value();
216
217 if (appendValue)
218 descDetails.value += value;
219 else
220 descDetails.value = value;
221
222 return descDetails.value.size();
223 }
224 }
225 }
226
227 return 0;
228}
229
230void QLowEnergyControllerPrivate::invalidateServices()
231{
232 for (QSharedPointer<QLowEnergyServicePrivate> service : serviceList.values())
233 service->setController(nullptr);
234
235 for (QSharedPointer<QLowEnergyServicePrivate> service : localServices.values())
236 service->setController(nullptr);
237
238 serviceList.clear();
239 localServices.clear();
240 lastLocalHandle = {};
241}
242
243QLowEnergyService *QLowEnergyControllerPrivate::addServiceHelper(
244 const QLowEnergyServiceData &service)
245{
246 // Spec says services "should" be grouped by uuid length (16-bit first, then 128-bit).
247 // Since this is not mandatory, we ignore it here and let the caller take responsibility
248 // for it.
249
250 const auto servicePrivate = QSharedPointer<QLowEnergyServicePrivate>::create();
251 servicePrivate->setController(this);
252 servicePrivate->state = QLowEnergyService::LocalService;
253 servicePrivate->uuid = service.uuid();
254 servicePrivate->type = service.type() == QLowEnergyServiceData::ServiceTypePrimary
255 ? QLowEnergyService::PrimaryService : QLowEnergyService::IncludedService;
256 const QList<QLowEnergyService *> includedServices = service.includedServices();
257 for (QLowEnergyService * const includedService : includedServices) {
258 servicePrivate->includedServices << includedService->serviceUuid();
259 includedService->d_ptr->type |= QLowEnergyService::IncludedService;
260 }
261
262 // Spec v4.2, Vol 3, Part G, Section 3.
263 const QLowEnergyHandle oldLastHandle = this->lastLocalHandle;
264 servicePrivate->startHandle = ++this->lastLocalHandle; // Service declaration.
265 this->lastLocalHandle += servicePrivate->includedServices.size(); // Include declarations.
266 const QList<QLowEnergyCharacteristicData> characteristics = service.characteristics();
267 for (const QLowEnergyCharacteristicData &cd : characteristics) {
268 const QLowEnergyHandle declHandle = ++this->lastLocalHandle;
269 QLowEnergyServicePrivate::CharData charData;
270 charData.valueHandle = ++this->lastLocalHandle;
271 charData.uuid = cd.uuid();
272 charData.properties = cd.properties();
273 charData.value = cd.value();
274 const QList<QLowEnergyDescriptorData> descriptors = cd.descriptors();
275 for (const QLowEnergyDescriptorData &dd : descriptors) {
276 QLowEnergyServicePrivate::DescData descData;
277 descData.uuid = dd.uuid();
278 descData.value = dd.value();
279 charData.descriptorList.insert(key: ++this->lastLocalHandle, value: descData);
280 }
281 servicePrivate->characteristicList.insert(key: declHandle, value: charData);
282 }
283 servicePrivate->endHandle = this->lastLocalHandle;
284 const bool handleOverflow = this->lastLocalHandle <= oldLastHandle;
285 if (handleOverflow) {
286 qCWarning(QT_BT) << "Not enough attribute handles left to create this service";
287 this->lastLocalHandle = oldLastHandle;
288 return nullptr;
289 }
290
291 if (localServices.contains(key: servicePrivate->uuid)) {
292 qCWarning(QT_BT) << "Overriding existing local service with uuid"
293 << servicePrivate->uuid;
294 }
295 this->localServices.insert(key: servicePrivate->uuid, value: servicePrivate);
296
297 this->addToGenericAttributeList(service, startHandle: servicePrivate->startHandle);
298 return new QLowEnergyService(servicePrivate);
299}
300
301void QLowEnergyControllerPrivate::readRssi()
302{
303 qCWarning(QT_BT, "This platform does not support reading RSSI");
304 setError(QLowEnergyController::RssiReadError);
305}
306
307QT_END_NAMESPACE
308
309#include "moc_qlowenergycontrollerbase_p.cpp"
310

source code of qtconnectivity/src/bluetooth/qlowenergycontrollerbase.cpp