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
bluetoothmanagement.cpp
Go to the documentation of this file.
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
4#include <QtCore/qloggingcategory.h>
5#include <QtCore/qsocketnotifier.h>
6#include <QtCore/qtimer.h>
7
9#include "bluez_data_p.h"
11
12#include <unistd.h>
13#include <sys/prctl.h>
14#include <sys/syscall.h>
15#include <sys/types.h>
16#include <linux/capability.h>
17
18#include <cerrno>
19
21
22// Packet data structures for Mgmt API bluez.git/doc/mgmt-api.txt
23
29
38
39
40/*
41 * This class encapsulates access to the Bluetooth Management API as introduced by
42 * Linux kernel 3.4. Some Bluetooth information is not exposed via the usual DBus
43 * API (e.g. the random/public address type info). In those cases we have to fall back
44 * to this mgmt API.
45 *
46 * Note that opening such a Bluetooth mgmt socket requires CAP_NET_ADMIN (root) capability.
47 *
48 * Documentation can be found in bluez-git/doc/mgmt-api.txt
49 */
50
52
53// These structs and defines come straight from linux/capability.h.
54// To avoid missing definitions we re-define them if not existing.
55// In addition, we don't want to pull in a libcap2 dependency
60
66
67#ifndef _LINUX_CAPABILITY_VERSION_3
68#define _LINUX_CAPABILITY_VERSION_3 0x20080522
69#endif
70
71#ifndef _LINUX_CAPABILITY_U32S_3
72#define _LINUX_CAPABILITY_U32S_3 2
73#endif
74
75#ifndef CAP_NET_ADMIN
76#define CAP_NET_ADMIN 12
77#endif
78
79#ifndef CAP_TO_INDEX
80#define CAP_TO_INDEX(x) ((x) >> 5) /* 1 << 5 == bits in __u32 */
81#endif
82
83#ifndef CAP_TO_MASK
84#define CAP_TO_MASK(x) (1 << ((x) & 31)) /* mask for indexed __u32 */
85#endif
86
87const int msecInADay = 1000*60*60*24;
88
90{
91 return syscall(__NR_capget, header, data);
92}
93
94/*
95 * Checks that the current process has the effective CAP_NET_ADMIN permission.
96 */
98{
99 // We only care for cap version 3 introduced by kernel 2.6.26
100 // because the new BlueZ management API only exists since kernel 3.4.
101
102 struct capHdr header = {};
105 header.pid = getpid();
106
107 if (sysCallCapGet(&header, data) < 0) {
108 qCWarning(QT_BT_BLUEZ, "BluetoothManangement: getCap failed with %s",
110 return false;
111 }
112
114}
115
117{
118 bool hasPermission = hasBtMgmtPermission();
119 if (!hasPermission) {
120 qCInfo(QT_BT_BLUEZ, "Missing CAP_NET_ADMIN permission. Cannot determine whether "
121 "a found address is of random or public type.");
122 return;
123 }
124
125 sockaddr_hci hciAddr;
126
127 fd = ::socket(AF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);
128 if (fd < 0) {
129 qCWarning(QT_BT_BLUEZ, "Cannot open Bluetooth Management socket: %s",
131 return;
132 }
133
134 memset(&hciAddr, 0, sizeof(hciAddr));
135 hciAddr.hci_dev = HCI_DEV_NONE;
136 hciAddr.hci_channel = HCI_CHANNEL_CONTROL;
137 hciAddr.hci_family = AF_BLUETOOTH;
138
139 if (::bind(fd, (struct sockaddr *)(&hciAddr), sizeof(hciAddr)) < 0) {
140 qCWarning(QT_BT_BLUEZ, "Cannot bind Bluetooth Management socket: %s",
142 ::close(fd);
143 fd = -1;
144 return;
145 }
146
148 connect(notifier, &QSocketNotifier::activated, this, &BluetoothManagement::_q_readNotifier);
149
150 // ensure cache is regularly cleaned (once every 24h)
151 QTimer* timer = new QTimer(this);
154 connect(timer, &QTimer::timeout, this, &BluetoothManagement::cleanupOldAddressFlags);
155 timer->start();
156}
157
158Q_GLOBAL_STATIC(BluetoothManagement, bluetoothKernelManager)
159
161{
162 return bluetoothKernelManager();
163}
164
165void BluetoothManagement::_q_readNotifier()
166{
168 const auto readCount = ::read(fd, dst, QPRIVATELINEARBUFFER_BUFFERSIZE);
169 buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE - (readCount < 0 ? 0 : readCount));
170 if (readCount < 0) {
171 qCWarning(QT_BT_BLUEZ, "Management Control read error %s", qPrintable(qt_error_string(errno)));
172 return;
173 }
174
175 // do we have at least one complete mgmt header?
176 if (size_t(buffer.size()) < sizeof(MgmtHdr))
177 return;
178
179 QByteArray data = buffer.readAll();
180
181 while (true) {
182 if (size_t(data.size()) < sizeof(MgmtHdr))
183 break;
184
185 const MgmtHdr *hdr = reinterpret_cast<const MgmtHdr*>(data.constData());
186 const auto nextPackageSize = qsizetype(qFromLittleEndian(hdr->length) + sizeof(MgmtHdr));
187 const qsizetype remainingPackageSize = data.size() - nextPackageSize;
188
189 if (data.size() < nextPackageSize)
190 break; // not a complete event header -> wait for next notifier
191
192 switch (static_cast<EventCode>(qFromLittleEndian(hdr->cmdCode))) {
194 {
195 const MgmtEventDeviceFound *event = reinterpret_cast<const MgmtEventDeviceFound*>
196 (data.constData() + sizeof(MgmtHdr));
197
198 if (event->type == BDADDR_LE_RANDOM) {
199 const bdaddr_t address = event->bdaddr;
201
203 const QBluetoothAddress qtAddress(bdaddr);
204 qCDebug(QT_BT_BLUEZ) << "BluetoothManagement: found random device"
205 << qtAddress;
206 processRandomAddressFlagInformation(qtAddress);
207 }
208
209 break;
210 }
211 default:
212 qCDebug(QT_BT_BLUEZ) << "BluetoothManagement: Ignored event:"
214 break;
215 }
216
217 if (data.size() > nextPackageSize)
218 data = data.right(remainingPackageSize);
219 else
220 data.clear();
221
222 if (data.isEmpty())
223 break;
224 }
225
226 if (!data.isEmpty())
227 buffer.ungetBlock(data.constData(), data.size());
228}
229
230void BluetoothManagement::processRandomAddressFlagInformation(const QBluetoothAddress &address)
231{
232 // insert or update
233 QMutexLocker locker(&accessLock);
234 privateFlagAddresses[address] = QDateTime::currentDateTimeUtc();
235}
236
237/*
238 * Ensure that private address cache is not older than 24h.
239 */
240void BluetoothManagement::cleanupOldAddressFlags()
241{
242 const auto cutOffTime = QDateTime::currentDateTimeUtc().addDays(-1);
243
244 QMutexLocker locker(&accessLock);
245
246 auto i = privateFlagAddresses.begin();
247 while (i != privateFlagAddresses.end()) {
248 if (i.value() < cutOffTime)
249 i = privateFlagAddresses.erase(i);
250 else
251 i++;
252 }
253}
254
256{
257 if (fd == -1 || address.isNull())
258 return false;
259
260 QMutexLocker locker(&accessLock);
261 return privateFlagAddresses.contains(address);
262}
263
265{
266 return (fd == -1) ? false : true;
267}
268
269
271
272#include "moc_bluetoothmanagement_p.cpp"
#define _LINUX_CAPABILITY_U32S_3
#define CAP_TO_INDEX(x)
#define _LINUX_CAPABILITY_VERSION_3
struct capHdr __attribute__
#define CAP_TO_MASK(x)
static int sysCallCapGet(capHdr *header, capData *data)
#define CAP_NET_ADMIN
bdaddr_t bdaddr
const int msecInADay
static bool hasBtMgmtPermission()
#define QPRIVATELINEARBUFFER_BUFFERSIZE
#define HCI_CHANNEL_CONTROL
#define BTPROTO_HCI
#define HCI_DEV_NONE
#define BDADDR_LE_RANDOM
DarwinBluetooth::LECBManagerNotifier * notifier
bool isAddressRandom(const QBluetoothAddress &address) const
BluetoothManagement(QObject *parent=nullptr)
\inmodule QtBluetooth
\inmodule QtCore
Definition qbytearray.h:57
static QDateTime currentDateTimeUtc()
iterator begin()
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the first item in the hash.
Definition qhash.h:1212
iterator erase(const_iterator it)
Definition qhash.h:1233
bool contains(const Key &key) const noexcept
Returns true if the hash contains an item with the key; otherwise returns false.
Definition qhash.h:1007
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
Definition qhash.h:1216
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
\inmodule QtCore
void activated(QSocketDescriptor socket, QSocketNotifier::Type activationEvent, QPrivateSignal)
\inmodule QtCore
Definition qtimer.h:20
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
void setInterval(int msec)
Definition qtimer.cpp:579
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
void setTimerType(Qt::TimerType atype)
Definition qtimer.cpp:651
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
@ VeryCoarseTimer
static void convertAddress(const quint64 from, quint8(&to)[6])
static QString header(const QString &name)
constexpr T qFromLittleEndian(T source)
Definition qendian.h:178
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLuint buffer
GLenum GLenum dst
GLuint64 GLenum GLint fd
struct _cl_event * event
GLuint GLuint64EXT address
GLuint GLenum GLsizei GLsizei GLint GLint GLboolean packed
#define qPrintable(string)
Definition qstring.h:1531
unsigned int quint32
Definition qtypes.h:50
unsigned short quint16
Definition qtypes.h:48
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned char quint8
Definition qtypes.h:46
ReturnedValue read(const char *data)
QTcpSocket * socket
[1]
QTimer * timer
[3]
socketLayer bind(QHostAddress::Any, 4000)
quint16 controllerIndex