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
btutility.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 "qbluetoothaddress.h"
6#include "qbluetoothuuid.h"
7#include "btutility_p.h"
8
9#include <QtCore/qoperatingsystemversion.h>
10#include <QtCore/qendian.h>
11#include <QtCore/qstring.h>
12
13#ifndef QT_IOS_BLUETOOTH
14
15#import <IOBluetooth/objc/IOBluetoothSDPUUID.h>
16#import <CoreFoundation/CoreFoundation.h>
17#import <CoreBluetooth/CBUUID.h>
18
19#endif
20
21#include <algorithm>
22#include <limits>
23
25
26Q_LOGGING_CATEGORY(QT_BT_DARWIN, "qt.bluetooth.darwin")
27
28namespace DarwinBluetooth {
29
30const int defaultLEScanTimeoutMS = 40000;
31// We use it only on iOS for now:
32const int maxValueLength = 512;
33
34const int defaultMtu = 23;
35
37{
38 if (address && address.length) {
39 NSString *const fixed = [address stringByReplacingOccurrencesOfString:@"-" withString:@":"];
40 return QString::fromNSString(fixed);
41 }
42
43 return QString();
44}
45
46#ifndef QT_IOS_BLUETOOTH
47
48
49QBluetoothAddress qt_address(const BluetoothDeviceAddress *a)
50{
51 if (a) {
52 // TODO: can a byte order be different in BluetoothDeviceAddress?
53 const quint64 qAddress = a->data[5] |
54 qint64(a->data[4]) << 8 |
55 qint64(a->data[3]) << 16 |
56 qint64(a->data[2]) << 24 |
57 qint64(a->data[1]) << 32 |
58 qint64(a->data[0]) << 40;
59 return QBluetoothAddress(qAddress);
60 }
61
62 return QBluetoothAddress();
63}
64
65BluetoothDeviceAddress iobluetooth_address(const QBluetoothAddress &qAddress)
66{
67 BluetoothDeviceAddress a = {};
68 if (!qAddress.isNull()) {
69 const quint64 val = qAddress.toUInt64();
70 a.data[0] = (val >> 40) & 0xff;
71 a.data[1] = (val >> 32) & 0xff;
72 a.data[2] = (val >> 24) & 0xff;
73 a.data[3] = (val >> 16) & 0xff;
74 a.data[4] = (val >> 8) & 0xff;
75 a.data[5] = val & 0xff;
76 }
77
78 return a;
79}
80
81ObjCStrongReference<IOBluetoothSDPUUID> iobluetooth_uuid(const QBluetoothUuid &uuid)
82{
83 const unsigned nBytes = 128 / std::numeric_limits<unsigned char>::digits;
84 const QUuid::Id128Bytes intVal(uuid.toBytes());
85
86 const ObjCStrongReference<IOBluetoothSDPUUID> iobtUUID([IOBluetoothSDPUUID uuidWithBytes:intVal.data
87 length:nBytes], RetainPolicy::doInitialRetain);
88 return iobtUUID;
89}
90
91QBluetoothUuid qt_uuid(IOBluetoothSDPUUID *uuid)
92{
93 QBluetoothUuid qtUuid;
94 if (!uuid || [uuid length] != 16) // TODO: issue any diagnostic?
95 return qtUuid;
96
97 // TODO: ensure the correct byte-order!!!
98 QUuid::Id128Bytes uuidVal = {};
99 const quint8 *const source = static_cast<const quint8 *>([uuid bytes]);
100 std::copy(source, source + 16, uuidVal.data);
101 return QBluetoothUuid(uuidVal);
102}
103
104QString qt_error_string(IOReturn errorCode)
105{
106 switch (errorCode) {
107 case kIOReturnSuccess:
108 // NoError in many classes == an empty string description.
109 return QString();
110 case kIOReturnNoMemory:
111 return QString::fromLatin1("memory allocation failed");
112 case kIOReturnNoResources:
113 return QString::fromLatin1("failed to obtain a resource");
114 case kIOReturnBusy:
115 return QString::fromLatin1("device is busy");
116 case kIOReturnStillOpen:
117 return QString::fromLatin1("device(s) still open");
118 // Others later ...
119 case kIOReturnError: // "general error" (IOReturn.h)
120 default:
121 return QString::fromLatin1("unknown error");
122 }
123}
124
126{
127 // IOBluetooth heavily relies on a CFRunLoop machinery in a way it dispatches
128 // its callbacks. Technically, having a QThread with CFRunLoop-based event
129 // dispatcher would suffice. At the moment of writing we do not have such
130 // event dispatcher, so we only can work on the main thread.
131 if (CFRunLoopGetMain() != CFRunLoopGetCurrent()) {
132 qCWarning(QT_BT_DARWIN) << "IOBluetooth works only on the main thread or a"
133 << "thread with a running CFRunLoop";
134 }
135}
136
137#endif // !QT_IOS_BLUETOOTH
138
139// Apple has: CBUUID, NSUUID, CFUUID, IOBluetoothSDPUUID
140// and it's handy to have several converters:
141
143{
144 // Apples' docs say "128 bit" and "16-bit UUIDs are implicitly
145 // pre-filled with the Bluetooth Base UUID."
146 // But Core Bluetooth can return CBUUID objects of length 2, 4, and 16.
147
148 if (!uuid)
149 return QBluetoothUuid();
150
152
153 if (uuid.data.length == 2) {
154 // CBUUID's docs say nothing about byte-order.
155 // Seems to be in big-endian.
156 const uchar *const src = static_cast<const uchar *>(uuid.data.bytes);
157 return QBluetoothUuid(qFromBigEndian<quint16>(src));
158 } else if (uuid.data.length == 4) {
159 const uchar *const src = static_cast<const uchar *>(uuid.data.bytes);
160 return QBluetoothUuid(qFromBigEndian<quint32>(src));
161 } else if (uuid.data.length == 16) {
162 QUuid::Id128Bytes qtUuidData = {};
163 const quint8 *const source = static_cast<const quint8 *>(uuid.data.bytes);
164 std::copy(source, source + 16, qtUuidData.data);
165
166 return QBluetoothUuid(qtUuidData);
167 }
168
169 qCDebug(QT_BT_DARWIN) << "qt_uuid, invalid CBUUID, 2, 4, or 16 bytes expected, but got "
170 << uuid.data.length << " bytes length";
171 return QBluetoothUuid();
172}
173
174ObjCStrongReference<CBUUID> cb_uuid(const QBluetoothUuid &qtUuid)
175{
176 bool ok = false;
177 const auto asUInt16 = qToBigEndian(qtUuid.toUInt16(&ok));
178 const auto asUInt128 = qtUuid.toBytes();
179
180 const NSUInteger length = ok ? sizeof asUInt16 : sizeof asUInt128;
181 const void *bytes = &asUInt128;
182 if (ok)
183 bytes = &asUInt16;
184
185 NSData *uuidData = [NSData dataWithBytes:bytes length:length];
186 ObjCStrongReference<CBUUID> cbUuid([CBUUID UUIDWithData:uuidData], RetainPolicy::doInitialRetain);
187 return cbUuid;
188}
189
190bool equal_uuids(const QBluetoothUuid &qtUuid, CBUUID *cbUuid)
191{
192 const QBluetoothUuid qtUuid2(qt_uuid(cbUuid));
193 return qtUuid == qtUuid2;
194}
195
196bool equal_uuids(CBUUID *cbUuid, const QBluetoothUuid &qtUuid)
197{
198 return equal_uuids(qtUuid, cbUuid);
199}
200
202{
204 if (!data || !data.length)
205 return value;
206
207 value.resize(data.length);
208 const char *const src = static_cast<const char *>(data.bytes);
209 std::copy(src, src + data.length, value.data());
210
211 return value;
212}
213
214template<class Integer>
216{
218 value.resize(sizeof n);
219 const char *const src = reinterpret_cast<char *>(&n);
220 std::copy(src, src + sizeof n, value.data());
221
222 return value;
223}
224
225QByteArray qt_bytearray(NSString *string)
226{
227 if (!string)
228 return QByteArray();
229
231 NSData *const utf8Data = [string dataUsingEncoding:NSUTF8StringEncoding];
232
233 return qt_bytearray(utf8Data);
234}
235
237{
238 // descriptor.value has type 'id'.
239 // While the Apple's docs say this about descriptors:
240 //
241 // - CBUUIDCharacteristicExtendedPropertiesString
242 // The string representation of the UUID for the extended properties descriptor.
243 // The corresponding value for this descriptor is an NSNumber object.
244 //
245 // - CBUUIDCharacteristicUserDescriptionString
246 // The string representation of the UUID for the user description descriptor.
247 // The corresponding value for this descriptor is an NSString object.
248 //
249 // ... etc.
250 //
251 // This is not true. On OS X, they all seem to be NSData (or derived from NSData),
252 // and they can be something else on iOS (NSNumber, NSString, etc.)
253 if (!obj)
254 return QByteArray();
255
257
258 if ([obj isKindOfClass:[NSData class]]) {
259 return qt_bytearray(static_cast<NSData *>(obj));
260 } else if ([obj isKindOfClass:[NSString class]]) {
261 return qt_bytearray(static_cast<NSString *>(obj));
262 } else if ([obj isKindOfClass:[NSNumber class]]) {
263 NSNumber *const nsNumber = static_cast<NSNumber *>(obj);
264 return qt_bytearray([nsNumber unsignedShortValue]);
265 }
266 // TODO: Where can be more types, but Core Bluetooth does not support them,
267 // or at least it's not documented.
268
269 return QByteArray();
270}
271
272ObjCStrongReference<NSData> data_from_bytearray(const QByteArray & qtData)
273{
274 if (!qtData.size())
275 return ObjCStrongReference<NSData>([[NSData alloc] init], RetainPolicy::noInitialRetain);
276
277 ObjCStrongReference<NSData> result([NSData dataWithBytes:qtData.constData() length:qtData.size()], RetainPolicy::doInitialRetain);
278 return result;
279}
280
281ObjCStrongReference<NSMutableData> mutable_data_from_bytearray(const QByteArray &qtData)
282{
283 using MutableData = ObjCStrongReference<NSMutableData>;
284
285 if (!qtData.size())
286 return MutableData([[NSMutableData alloc] init], RetainPolicy::noInitialRetain);
287
288 MutableData result([[NSMutableData alloc] initWithLength:qtData.size()], RetainPolicy::noInitialRetain);
289 [result replaceBytesInRange:NSMakeRange(0, qtData.size())
290 withBytes:qtData.constData()];
291 return result;
292}
293
294// A small RAII class for a dispatch queue.
296{
297public:
298 explicit SerialDispatchQueue(const char *label)
299 {
301
302 queue = dispatch_queue_create(label, DISPATCH_QUEUE_SERIAL);
303 if (!queue) {
304 qCCritical(QT_BT_DARWIN) << "failed to create dispatch queue with label"
305 << label;
306 }
307 }
309 {
310 if (queue)
311 dispatch_release(queue);
312 }
313
314 dispatch_queue_t data() const
315 {
316 return queue;
317 }
318private:
319 dispatch_queue_t queue;
320
321 Q_DISABLE_COPY(SerialDispatchQueue)
322};
323
324dispatch_queue_t qt_LE_queue()
325{
326 static const SerialDispatchQueue leQueue("qt-bluetooth-LE-queue");
327 return leQueue.data();
328}
329
330} // namespace DarwinBluetooth
331
#define QT_BT_MAC_AUTORELEASEPOOL
Definition btutility_p.h:78
SerialDispatchQueue(const char *label)
Definition btutility.mm:298
dispatch_queue_t data() const
Definition btutility.mm:314
\inmodule QtBluetooth
\inmodule QtBluetooth
\inmodule QtCore
Definition qbytearray.h:57
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QChar * data()
Returns a pointer to the data stored in the QString.
Definition qstring.h:1240
Id128Bytes toBytes(QSysInfo::Endian order=QSysInfo::BigEndian) const noexcept
Definition quuid.h:232
ObjCStrongReference< CBUUID > cb_uuid(const QBluetoothUuid &qtUuid)
Definition btutility.mm:174
const int defaultLEScanTimeoutMS
Definition btutility.mm:30
bool equal_uuids(const QBluetoothUuid &qtUuid, CBUUID *cbUuid)
Definition btutility.mm:190
ObjCStrongReference< NSMutableData > mutable_data_from_bytearray(const QByteArray &qtData)
Definition btutility.mm:281
BluetoothDeviceAddress iobluetooth_address(const QBluetoothAddress &qAddress)
Definition btutility.mm:65
QByteArray qt_bytearray(NSData *data)
Definition btutility.mm:201
QBluetoothUuid qt_uuid(NSUUID *nsUuid)
QString qt_address(NSString *address)
Definition btutility.mm:36
void qt_test_iobluetooth_runloop()
Definition btutility.mm:125
const int maxValueLength
Definition btutility.mm:32
ObjCStrongReference< NSData > data_from_bytearray(const QByteArray &qtData)
Definition btutility.mm:272
ObjCStrongReference< IOBluetoothSDPUUID > iobluetooth_uuid(const QBluetoothUuid &uuid)
Definition btutility.mm:81
dispatch_queue_t qt_LE_queue()
Definition btutility.mm:324
const int defaultMtu
Definition btutility.mm:34
Combined button and popup list for selecting options.
unsigned long NSUInteger
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
constexpr T qToBigEndian(T source)
Definition qendian.h:172
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define Q_LOGGING_CATEGORY(name,...)
#define qCCritical(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLenum GLsizei length
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum src
GLuint GLsizei const GLchar * label
[43]
GLfloat n
GLsizei GLsizei GLchar * source
GLhandleARB obj
[2]
GLuint GLfloat * val
GLuint GLuint64EXT address
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
unsigned char uchar
Definition qtypes.h:32
unsigned long long quint64
Definition qtypes.h:61
long long qint64
Definition qtypes.h:60
unsigned char quint8
Definition qtypes.h:46
QQueue< int > queue
[0]
\inmodule QtCore
Definition quuid.h:58