Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
btservicerecord.mm
Go to the documentation of this file.
1// Copyright (C) 2022 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#include "btservicerecord_p.h"
6
7#include <QtCore/qvariant.h>
8#include <QtCore/qdebug.h>
9#include <QtCore/qurl.h>
10
11#include <IOBluetooth/IOBluetooth.h>
12
14
15namespace DarwinBluetooth {
16
17//
18// Returns a dictionary containing the Bluetooth RFCOMM service definition
19// corresponding to the provided |uuid| and |options|.
20namespace {
21
22typedef ObjCStrongReference<NSMutableDictionary> Dictionary;
23typedef ObjCStrongReference<IOBluetoothSDPUUID> SDPUUid;
24typedef ObjCStrongReference<NSNumber> Number;
25typedef QBluetoothServiceInfo QSInfo;
26typedef QSInfo::Sequence Sequence;
27typedef QSInfo::AttributeId AttributeId;
28
29}
30
31#if 0
32QBluetoothUuid profile_uuid(const QBluetoothServiceInfo &serviceInfo)
33{
34 // Strategy to pick service uuid:
35 // 1.) use serviceUuid()
36 // 2.) use first custom uuid if available
37 // 3.) use first service class uuid
38 QBluetoothUuid serviceUuid(serviceInfo.serviceUuid());
39
40 if (serviceUuid.isNull()) {
41 const QVariant var(serviceInfo.attribute(QBluetoothServiceInfo::ServiceClassIds));
42 if (var.isValid()) {
43 const Sequence seq(var.value<Sequence>());
44
45 for (qsizetype i = 0; i < seq.size(); ++i) {
46 QBluetoothUuid uuid(seq.at(i).value<QBluetoothUuid>());
47 if (uuid.isNull())
48 continue;
49
50 const int size = uuid.minimumSize();
51 if (size == 2 || size == 4) { // Base UUID derived
52 if (serviceUuid.isNull())
53 serviceUuid = uuid;
54 } else {
55 return uuid;
56 }
57 }
58 }
59 }
60
61 return serviceUuid;
62}
63#endif
64
65template<class IntType>
67
68template<>
70{
71 return Number([NSNumber numberWithUnsignedChar:var.value<unsigned char>()], RetainPolicy::doInitialRetain);
72}
73
74template<>
76{
77 return Number([NSNumber numberWithUnsignedShort:var.value<unsigned short>()], RetainPolicy::doInitialRetain);
78}
79
80template<>
82{
83 return Number([NSNumber numberWithUnsignedInt:var.value<unsigned>()], RetainPolicy::doInitialRetain);
84}
85
86template<>
88{
89 return Number([NSNumber numberWithChar:var.value<char>()], RetainPolicy::doInitialRetain);
90}
91
92template<>
94{
95 return Number([NSNumber numberWithShort:var.value<short>()], RetainPolicy::doInitialRetain);
96}
97
98template<>
100{
101 return Number([NSNumber numberWithInt:var.value<int>()], RetainPolicy::doInitialRetain);
102}
103
104template<class ValueType>
105void add_attribute(const QVariant &var, AttributeId key, Dictionary dict)
106{
107 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)");
108
109 if (!var.canConvert<ValueType>())
110 return;
111
112 const Number num(variant_to_nsnumber<ValueType>(var));
113 [dict setObject:num forKey:[NSString stringWithFormat:@"%x", int(key)]];
114}
115
116template<>
117void add_attribute<QString>(const QVariant &var, AttributeId key, Dictionary dict)
118{
119 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)");
120
121 if (!var.canConvert<QString>())
122 return;
123
124 const QString string(var.value<QString>());
125 if (!string.isEmpty()) {
126 if (NSString *const nsString = string.toNSString())
127 [dict setObject:nsString forKey:[NSString stringWithFormat:@"%x", int(key)]];
128 }
129}
130
131template<>
132void add_attribute<QBluetoothUuid>(const QVariant &var, AttributeId key, Dictionary dict)
133{
134 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)");
135
137 return;
138
139 SDPUUid ioUUID(iobluetooth_uuid(var.value<QBluetoothUuid>()));
140 [dict setObject:ioUUID forKey:[NSString stringWithFormat:@"%x", int(key)]];
141}
142
143template<>
144void add_attribute<QUrl>(const QVariant &var, AttributeId key, Dictionary dict)
145{
146 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)");
147
148 if (!var.canConvert<QUrl>())
149 return;
150
151 Q_UNUSED(var);
152 Q_UNUSED(key);
153 Q_UNUSED(dict);
154
155 // TODO: not clear how should I pass an url in a dictionary, NSURL does not work.
156}
157
158template<class ValueType>
159void add_attribute(const QVariant &var, NSMutableArray *list);
160
161template<class ValueType>
162void add_attribute(const QVariant &var, NSMutableArray *list)
163{
164 Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)");
165
166 if (!var.canConvert<ValueType>())
167 return;
168
169 const Number num(variant_to_nsnumber<ValueType>(var));
170 [list addObject:num];
171}
172
173template<>
174void add_attribute<unsigned short>(const QVariant &var, NSMutableArray *list)
175{
176 Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)");
177
178 if (!var.canConvert<unsigned short>())
179 return;
180
181 const Number num(variant_to_nsnumber<unsigned short>(var));
182
183 NSDictionary* dict = @{
184 @"DataElementType" : [NSNumber numberWithInt:1],
185 @"DataElementSize" : [NSNumber numberWithInt:2],
186 @"DataElementValue" : num
187 };
188
189 [list addObject: dict];
190}
191
192template<>
193void add_attribute<QString>(const QVariant &var, NSMutableArray *list)
194{
195 Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)");
196
197 if (!var.canConvert<QString>())
198 return;
199
200 const QString string(var.value<QString>());
201 if (!string.isEmpty()) {
202 if (NSString *const nsString = string.toNSString())
203 [list addObject:nsString];
204 }
205}
206
207template<>
208void add_attribute<QBluetoothUuid>(const QVariant &var, NSMutableArray *list)
209{
210 Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)");
211
213 return;
214
215 SDPUUid ioUUID(iobluetooth_uuid(var.value<QBluetoothUuid>()));
216 [list addObject:ioUUID];
217}
218
219template<>
220void add_attribute<QUrl>(const QVariant &var, NSMutableArray *list)
221{
222 Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)");
223
224 if (!var.canConvert<QUrl>())
225 return;
226
227 Q_UNUSED(var);
228 Q_UNUSED(list);
229 // TODO: not clear how should I pass an url in a dictionary, NSURL does not work.
230}
231
232void add_rfcomm_protocol_descriptor_list(uint16 channelID, Dictionary dict)
233{
234 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)");
235
237
238 // Objective-C has literals (for arrays and dictionaries), but it will not compile
239 // on 10.7 or below, so quite a lot of code here.
240
241 NSMutableArray *const descriptorList = [NSMutableArray array];
242
243 IOBluetoothSDPUUID *const l2capUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP];
244 NSArray *const l2capList = [NSArray arrayWithObject:l2capUUID];
245
246 [descriptorList addObject:l2capList];
247 //
248 IOBluetoothSDPUUID *const rfcommUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16RFCOMM];
249 NSMutableDictionary *const rfcommDict = [NSMutableDictionary dictionary];
250 [rfcommDict setObject:[NSNumber numberWithInt:1] forKey:@"DataElementType"];
251 [rfcommDict setObject:[NSNumber numberWithInt:1] forKey:@"DataElementSize"];
252 [rfcommDict setObject:[NSNumber numberWithInt:channelID] forKey:@"DataElementValue"];
253 //
254 NSMutableArray *const rfcommList = [NSMutableArray array];
255 [rfcommList addObject:rfcommUUID];
256 [rfcommList addObject:rfcommDict];
257
258 [descriptorList addObject:rfcommList];
259 [dict setObject:descriptorList forKey:[NSString stringWithFormat:@"%x",
260 kBluetoothSDPAttributeIdentifierProtocolDescriptorList]];
261}
262
263void add_l2cap_protocol_descriptor_list(uint16 psm, Dictionary dict)
264{
265 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)");
266
268
269 // Objective-C has literals (for arrays and dictionaries), but it will not compile
270 // on 10.7 or below, so quite a lot of code here.
271
272 NSMutableArray *const descriptorList = [NSMutableArray array];
273 NSMutableArray *const l2capList = [NSMutableArray array];
274
275 IOBluetoothSDPUUID *const l2capUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP];
276 [l2capList addObject:l2capUUID];
277
278 NSMutableDictionary *const l2capDict = [NSMutableDictionary dictionary];
279 [l2capDict setObject:[NSNumber numberWithInt:1] forKey:@"DataElementType"];
280 [l2capDict setObject:[NSNumber numberWithInt:2] forKey:@"DataElementSize"];
281 [l2capDict setObject:[NSNumber numberWithInt:psm] forKey:@"DataElementValue"];
282 [l2capList addObject:l2capDict];
283
284 [descriptorList addObject:l2capList];
285 [dict setObject:descriptorList forKey:[NSString stringWithFormat:@"%x",
286 kBluetoothSDPAttributeIdentifierProtocolDescriptorList]];
287}
288
289bool add_attribute(const QVariant &var, AttributeId key, NSMutableArray *list)
290{
291 Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)");
292
293 if (var.canConvert<Sequence>())
294 return false;
295
296 if (var.typeId() == QMetaType::QString) {
297 //ServiceName, ServiceDescription, ServiceProvider.
299 } else if (var.userType() == qMetaTypeId<QBluetoothUuid>()) {
301 } else {
302 // Here we need 'key' to understand the type.
303 // We can have different integer types actually, so I have to check
304 // the 'key' to be sure the conversion is reasonable.
305 switch (key) {
306 case QSInfo::ServiceRecordHandle:
307 case QSInfo::ServiceRecordState:
308 case QSInfo::ServiceInfoTimeToLive:
309 add_attribute<unsigned>(var, list);
310 break;
311 case QSInfo::BluetoothProfileDescriptorList:
313 break;
314 case QSInfo::ServiceAvailability:
315 add_attribute<unsigned char>(var, list);
316 break;
317 case QSInfo::IconUrl:
318 case QSInfo::DocumentationUrl:
319 case QSInfo::ClientExecutableUrl:
321 break;
322 default:;
323 }
324 }
325
326 return true;
327}
328
329bool add_attribute(const QBluetoothServiceInfo &serviceInfo, AttributeId key, Dictionary dict)
330{
331 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dict (nil)");
332
333 const QVariant var(serviceInfo.attribute(key));
334 if (var.canConvert<Sequence>())
335 return false;
336
337 if (var.typeId() == QMetaType::QString) {
338 //ServiceName, ServiceDescription, ServiceProvider.
340 } else if (var.userType() == qMetaTypeId<QBluetoothUuid>()) {
341 add_attribute<QBluetoothUuid>(serviceInfo.attribute(key), key, dict);
342 } else {
343 // We can have different integer types actually, so I have to check
344 // the 'key' to be sure the conversion is reasonable.
345 switch (key) {
346 case QSInfo::ServiceRecordHandle:
347 case QSInfo::ServiceRecordState:
348 case QSInfo::ServiceInfoTimeToLive:
349 add_attribute<unsigned>(serviceInfo.attribute(key), key, dict);
350 break;
351 case QSInfo::ServiceAvailability:
352 add_attribute<unsigned char>(serviceInfo.attribute(key), key, dict);
353 break;
354 case QSInfo::IconUrl:
355 case QSInfo::DocumentationUrl:
356 case QSInfo::ClientExecutableUrl:
357 add_attribute<QUrl>(serviceInfo.attribute(key), key, dict);
358 break;
359 default:;
360 }
361 }
362
363 return true;
364}
365
366bool add_sequence_attribute(const QVariant &var, AttributeId key, NSMutableArray *list)
367{
368 // Add a "nested" sequence.
369 Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)");
370
371 if (var.isNull() || !var.canConvert<Sequence>())
372 return false;
373
374 NSMutableArray *const nested = [NSMutableArray array];
375 [list addObject:nested];
376
377 const Sequence sequence(var.value<Sequence>());
378 for (const QVariant &var : sequence) {
379 if (var.canConvert<Sequence>()) {
381 } else {
382 add_attribute(var, key, nested);
383 }
384 }
385
386 return true;
387}
388
389bool add_sequence_attribute(const QBluetoothServiceInfo &serviceInfo, AttributeId key, Dictionary dict)
390{
391 Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)");
392
393 const QVariant &var(serviceInfo.attribute(key));
394 if (var.isNull() || !var.canConvert<Sequence>())
395 return false;
396
398
399 NSMutableArray *const list = [NSMutableArray array];
400 const Sequence sequence(var.value<Sequence>());
401 for (const QVariant &element : sequence) {
402 if (!add_sequence_attribute(element, key, list))
403 add_attribute(element, key, list);
404 }
405 [dict setObject:list forKey:[NSString stringWithFormat:@"%x", int(key)]];
406 return true;
407}
408
410{
411 Dictionary dict;
412
413 if (serviceInfo.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol)
414 return dict;
415
416 const QList<quint16> attributeIds(serviceInfo.attributes());
417 if (!attributeIds.size())
418 return dict;
419
420 dict.reset([[NSMutableDictionary alloc] init], RetainPolicy::noInitialRetain);
421
422 for (quint16 key : attributeIds) {
423 if (key == QSInfo::ProtocolDescriptorList) // We handle it in a special way.
424 continue;
425 // TODO: check if non-sequence QVariant still must be
426 // converted into NSArray for some attribute ID.
427 if (!add_sequence_attribute(serviceInfo, AttributeId(key), dict))
428 add_attribute(serviceInfo, AttributeId(key), dict);
429 }
430
431 if (serviceInfo.socketProtocol() == QBluetoothServiceInfo::L2capProtocol) {
432 add_l2cap_protocol_descriptor_list(serviceInfo.protocolServiceMultiplexer(),
433 dict);
434 } else {
435 add_rfcomm_protocol_descriptor_list(serviceInfo.serverChannel(), dict);
436 }
437
438 return dict;
439}
440
441}
442
#define QT_BT_MAC_AUTORELEASEPOOL
Definition btutility_p.h:78
\inmodule QtBluetooth
\inmodule QtBluetooth
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qurl.h:94
\inmodule QtCore
Definition qvariant.h:65
T value() const &
Definition qvariant.h:516
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
int userType() const
Definition qvariant.h:339
int typeId() const
Returns the storage type of the value stored in the variant.
Definition qvariant.h:340
bool isNull() const
Returns true if this is a null variant, false otherwise.
bool canConvert(QMetaType targetType) const
Definition qvariant.h:345
Number variant_to_nsnumber< unsigned short >(const QVariant &var)
bool add_sequence_attribute(const QVariant &var, AttributeId key, NSMutableArray *list)
void add_attribute< QString >(const QVariant &var, AttributeId key, Dictionary dict)
void add_attribute< QBluetoothUuid >(const QVariant &var, AttributeId key, Dictionary dict)
void add_attribute< unsigned short >(const QVariant &var, NSMutableArray *list)
void add_attribute(const QVariant &var, AttributeId key, Dictionary dict)
Number variant_to_nsnumber< short >(const QVariant &var)
Number variant_to_nsnumber< char >(const QVariant &var)
void add_rfcomm_protocol_descriptor_list(uint16 channelID, Dictionary dict)
ObjCStrongReference< IOBluetoothSDPUUID > iobluetooth_uuid(const QBluetoothUuid &uuid)
Definition btutility.mm:81
Number variant_to_nsnumber< int >(const QVariant &var)
Number variant_to_nsnumber< unsigned char >(const QVariant &var)
void add_l2cap_protocol_descriptor_list(uint16 psm, Dictionary dict)
Dictionary iobluetooth_service_dictionary(const QBluetoothServiceInfo &serviceInfo)
Number variant_to_nsnumber< unsigned >(const QVariant &var)
Number variant_to_nsnumber(const QVariant &)
void add_attribute< QUrl >(const QVariant &var, AttributeId key, Dictionary dict)
Combined button and popup list for selecting options.
#define Q_FUNC_INFO
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum array
GLuint num
GLsizei const GLchar *const * string
[0]
Definition qopenglext.h:694
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
#define Q_UNUSED(x)
unsigned short quint16
Definition qtypes.h:48
ptrdiff_t qsizetype
Definition qtypes.h:165
QList< int > list
[14]
QFuture< QSet< QString > > dictionary