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
qbluetoothlocaldevice_winrt.cpp
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
4#include <private/qbluetoothutils_winrt_p.h>
5
7#include "qbluetoothaddress.h"
8
12
13#include <winrt/Windows.Foundation.h>
14#include <winrt/Windows.Foundation.Collections.h>
15#include <winrt/Windows.Devices.Enumeration.h>
16#include <winrt/Windows.Devices.Bluetooth.h>
17#include <winrt/Windows.Devices.Radios.h>
18
19#include <QtCore/QCoreApplication>
20#include <QtCore/QElapsedTimer>
21#include <QtCore/QLoggingCategory>
22#include <QtCore/QMutex>
23#include <QtCore/QPointer>
24#include <QtCore/QTimer>
25
26using namespace winrt::Windows::Foundation;
27using namespace winrt::Windows::Foundation::Collections;
28using namespace winrt::Windows::Devices::Enumeration;
29using namespace winrt::Windows::Devices::Bluetooth;
30using namespace winrt::Windows::Devices::Radios;
31
33
34Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
35
36template <typename T>
37static bool await(IAsyncOperation<T> &&asyncInfo, T &result, uint timeout = 0)
38{
39 using WinRtAsyncStatus = winrt::Windows::Foundation::AsyncStatus;
40 WinRtAsyncStatus status;
42 if (timeout)
43 timer.start();
44 do {
46 status = asyncInfo.Status();
47 } while (status == WinRtAsyncStatus::Started && (!timeout || !timer.hasExpired(timeout)));
48 if (status == WinRtAsyncStatus::Completed) {
49 result = asyncInfo.GetResults();
50 return true;
51 } else {
52 return false;
53 }
54}
55
57{
58 // Windows APIs do not seem to support HostDiscoverable and
59 // HostDiscoverableLimitedInquiry, so we just treat them as HostConnectable
62}
63
69
71{
72 return (mode == QBluetoothLocalDevice::HostPoweredOff) ? RadioState::Off : RadioState::On;
73}
74
75class AdapterManager;
76
78{
79public:
80 // we do not really care about unique ids here
81 WatcherWrapper(winrt::hstring selector) :
82 mWatcher(std::make_shared<QBluetoothDeviceWatcherWinRT>(0, selector))
83 {
84 }
85
86 template<typename AddedSlot, typename RemovedSlot>
87 void init(AdapterManager *manager, AddedSlot onAdded, RemovedSlot onRemoved)
88 {
89 if (!mWatcher) {
90 qWarning("QBluetoothLocalDevice: failed to create device watcher!");
91 return;
92 }
93
97 manager, onRemoved, Qt::QueuedConnection);
98
99 // This will also print a warning on failure.
100 if (mWatcher->init())
101 mWatcher->start();
102 }
103
104private:
105 std::shared_ptr<QBluetoothDeviceWatcherWinRT> mWatcher = nullptr;
106};
107
108/*
109 This class is supposed to manage winrt::Windows::Devices::Radios::Radio
110 instances. It looks like Windows behaves incorrectly when there are multiple
111 instances representing the same physical device. So this class will be a
112 single point for keeping track of all used Radios.
113 At the same time this class takes care of monitoring adapter connections
114 and disconnections.
115 Note that access to internal structs should be protected, because all
116 Windows callbacks come in separate threads. We also can't use
117 Qt::QueuedConnection to "forward" the execution to the right thread, because
118 Windows' IUnknown-based classes have a deleted operator new(), and so can't
119 be used in QMetaType.
120 However, we will still mostly use signals/slots to communicate with actual
121 QBluetoothLocalDevice instances, so we do not need any protection on that
122 side.
123*/
125{
127public:
130
131 QList<QBluetoothAddress> connectedDevices();
132
133public slots:
135 void removeClient(winrt::hstring adapterId);
136 void updateMode(winrt::hstring adapterId, QBluetoothLocalDevice::HostMode mode);
137
138signals:
139 void adapterAdded(winrt::hstring id);
140 void adapterRemoved(winrt::hstring id);
144
145private:
146 struct RadioInfo {
147 Radio radio = nullptr;
148 winrt::event_token stateToken;
149 int numClients = 0;
150 RadioState currentState = RadioState::Unknown;
151 };
152
153 Radio getRadioFromAdapterId(winrt::hstring id);
154 Q_SLOT void onAdapterAdded(winrt::hstring id);
155 Q_SLOT void onAdapterRemoved(winrt::hstring id);
156 void subscribeToStateChanges(RadioInfo &info);
157 void unsubscribeFromStateChanges(RadioInfo &info);
158 void onStateChange(Radio radio);
159 Q_SLOT void tryResubscribeToStateChanges(winrt::hstring id, int numAttempts);
160
161 Q_SLOT void onDeviceAdded(winrt::hstring id);
162 Q_SLOT void onDeviceRemoved(winrt::hstring id);
163
164 std::unique_ptr<WatcherWrapper> mAdapterWatcher = nullptr;
165 std::unique_ptr<WatcherWrapper> mLeDevicesWatcher = nullptr;
166 std::unique_ptr<WatcherWrapper> mClassicDevicesWatcher = nullptr;
167 QMutex mMutex;
168 // Key for this map is BluetoothAdapter Id, *not* Radio Id.
169 QMap<winrt::hstring, RadioInfo> mRadios;
170 QList<QBluetoothAddress> mConnectedDevices;
171};
172
174{
175 const auto adapterSelector = BluetoothAdapter::GetDeviceSelector();
176 mAdapterWatcher = std::make_unique<WatcherWrapper>(adapterSelector);
177 mAdapterWatcher->init(this, &AdapterManager::onAdapterAdded, &AdapterManager::onAdapterRemoved);
178
179 // Once created, device watchers will also populate the initial list of
180 // connected devices.
181
182 const auto leSelector = BluetoothLEDevice::GetDeviceSelectorFromConnectionStatus(
183 BluetoothConnectionStatus::Connected);
184 mLeDevicesWatcher = std::make_unique<WatcherWrapper>(leSelector);
185 mLeDevicesWatcher->init(this, &AdapterManager::onDeviceAdded, &AdapterManager::onDeviceRemoved);
186
187 const auto classicSelector = BluetoothDevice::GetDeviceSelectorFromConnectionStatus(
188 BluetoothConnectionStatus::Connected);
189 mClassicDevicesWatcher = std::make_unique<WatcherWrapper>(classicSelector);
190 mClassicDevicesWatcher->init(this, &AdapterManager::onDeviceAdded,
191 &AdapterManager::onDeviceRemoved);
192}
193
197
198QList<QBluetoothAddress> AdapterManager::connectedDevices()
199{
200 QMutexLocker locker(&mMutex);
201 return mConnectedDevices;
202}
203
205{
207 &QBluetoothLocalDevicePrivate::radioModeChanged, Qt::QueuedConnection);
208 connect(client, &QBluetoothLocalDevicePrivate::updateMode, this, &AdapterManager::updateMode,
211 &QBluetoothLocalDevicePrivate::onAdapterAdded, Qt::QueuedConnection);
213 &QBluetoothLocalDevicePrivate::onAdapterRemoved, Qt::QueuedConnection);
215 &QBluetoothLocalDevicePrivate::onDeviceAdded, Qt::QueuedConnection);
217 &QBluetoothLocalDevicePrivate::onDeviceRemoved, Qt::QueuedConnection);
218
219 QMutexLocker locker(&mMutex);
220 const auto adapterId = client->mDeviceId;
221 if (mRadios.contains(adapterId)) {
222 auto &radioInfo = mRadios[adapterId];
223 radioInfo.numClients++;
224 return modeFromWindowsBluetoothState(radioInfo.radio.State());
225 } else {
226 // Note that when we use await(), we need to unlock the mutex, because
227 // it calls processEvents(), so other methods that demand the mutex can
228 // be invoked.
229 locker.unlock();
230 Radio r = getRadioFromAdapterId(adapterId);
231 if (r) {
232 locker.relock();
233 RadioInfo info;
234 info.radio = r;
235 info.numClients = 1;
236 info.currentState = r.State();
237 subscribeToStateChanges(info);
238 mRadios.insert(adapterId, info);
239 return modeFromWindowsBluetoothState(info.currentState);
240 }
241 }
242 qCWarning(QT_BT_WINDOWS, "Failed to subscribe to adapter state changes");
244}
245
246void AdapterManager::removeClient(winrt::hstring adapterId)
247{
248 QMutexLocker locker(&mMutex);
249 if (mRadios.contains(adapterId)) {
250 auto &radioInfo = mRadios[adapterId];
251 if (--radioInfo.numClients == 0) {
252 unsubscribeFromStateChanges(radioInfo);
253 mRadios.remove(adapterId);
254 }
255 } else {
256 qCWarning(QT_BT_WINDOWS) << "Removing client for an unknown adapter id"
257 << QString::fromStdString(winrt::to_string(adapterId));
258 }
259}
260
262{
263 QMutexLocker locker(&mMutex);
264 if (mRadios.contains(adapterId)) {
265 RadioAccessStatus status = RadioAccessStatus::Unspecified;
266 auto radio = mRadios[adapterId].radio; // can be nullptr
267 locker.unlock();
268 if (radio) {
269 const bool res = await(radio.SetStateAsync(windowsStateFromMode(mode)), status);
270 // If operation succeeds, we will update the state in the event handler.
271 if (!res || status != RadioAccessStatus::Allowed) {
272 qCWarning(QT_BT_WINDOWS, "Failed to update adapter state: SetStateAsync() failed!");
273 if (status == RadioAccessStatus::DeniedBySystem) {
274 qCWarning(QT_BT_WINDOWS) << "Check that the user has permissions to manipulate"
275 " the selected Bluetooth device";
276 }
277 }
278 }
279 }
280}
281
282Radio AdapterManager::getRadioFromAdapterId(winrt::hstring id)
283{
284 BluetoothAdapter a(nullptr);
285 bool res = await(BluetoothAdapter::FromIdAsync(id), a);
286 if (res && a) {
287 Radio r(nullptr);
288 res = await(a.GetRadioAsync(), r);
289 if (res && r)
290 return r;
291 }
292 return nullptr;
293}
294
295void AdapterManager::onStateChange(Radio radio)
296{
297 QMutexLocker locker(&mMutex);
298 for (const auto &key : mRadios.keys()) {
299 auto &info = mRadios[key];
300 if (info.radio == radio) {
301 if (info.currentState != radio.State()) {
302 info.currentState = radio.State();
304 }
305 break;
306 }
307 }
308}
309
310static const int kMaximumAttempts = 5;
311
312// In practice when the adapter is reconnected, the Radio object can't be
313// retrieved immediately. I'm not sure if such behavior is normal, or specific
314// to my machine only. I also do not know what is the proper time to wait before
315// the Radio instance can be retrieved. So we introduce a helper method, which
316// tries to resubscribe several times with a 100ms interval between retries.
317void AdapterManager::tryResubscribeToStateChanges(winrt::hstring id, int numAttempts)
318{
319 QMutexLocker locker(&mMutex);
320 if (mRadios.contains(id)) {
321 // The Added event can come when we first create and use adapter. Such
322 // event should not be handled.
323 if (mRadios[id].radio != nullptr)
324 return;
325 locker.unlock();
326 Radio r = getRadioFromAdapterId(id);
327 if (r) {
328 locker.relock();
329 // We have to check once again because the record could be deleted
330 // while we were await'ing in getRadioFromAdapterId().
331 if (mRadios.contains(id)) {
332 auto &info = mRadios[id];
333 info.radio = r;
334 info.currentState = r.State();
335 subscribeToStateChanges(info);
337 }
338 } else {
339 if (++numAttempts < kMaximumAttempts) {
340 qCDebug(QT_BT_WINDOWS, "Trying to resubscribe for the state changes");
341 QPointer<AdapterManager> thisPtr(this);
342 QTimer::singleShot(100, [thisPtr, id, numAttempts]() {
343 if (thisPtr)
344 thisPtr->tryResubscribeToStateChanges(id, numAttempts);
345 });
346 } else {
347 qCWarning(QT_BT_WINDOWS,
348 "Failed to resubscribe to the state changes after %d attempts!",
349 numAttempts);
350 }
351 }
352 }
353}
354
360
361static BluetoothInfo getBluetoothInfo(winrt::hstring id)
362{
363 // We do not know if it's a BT classic or BTLE device, so we try both.
364 BluetoothDevice device(nullptr);
365 bool res = await(BluetoothDevice::FromIdAsync(id), device, 5000);
366 if (res && device) {
367 return { QBluetoothAddress(device.BluetoothAddress()),
368 device.ConnectionStatus() == BluetoothConnectionStatus::Connected };
369 }
370
371 BluetoothLEDevice leDevice(nullptr);
372 res = await(BluetoothLEDevice::FromIdAsync(id), leDevice, 5000);
373 if (res && leDevice) {
374 return { QBluetoothAddress(leDevice.BluetoothAddress()),
375 leDevice.ConnectionStatus() == BluetoothConnectionStatus::Connected };
376 }
377
378 return {};
379}
380
381void AdapterManager::onDeviceAdded(winrt::hstring id)
382{
384 // In practice this callback might come even for disconnected device.
385 // So check status explicitly.
386 if (!info.address.isNull() && info.isConnected) {
387 bool found = false;
388 {
389 // A scope is needed, because we need to emit a signal when mutex is already unlocked
390 QMutexLocker locker(&mMutex);
391 found = mConnectedDevices.contains(info.address);
392 if (!found) {
393 mConnectedDevices.push_back(info.address);
394 }
395 }
396 if (!found)
397 emit deviceAdded(info.address);
398 }
399}
400
401void AdapterManager::onDeviceRemoved(winrt::hstring id)
402{
404 if (!info.address.isNull() && !info.isConnected) {
405 bool found = false;
406 {
407 QMutexLocker locker(&mMutex);
408 found = mConnectedDevices.removeOne(info.address);
409 }
410 if (found)
411 emit deviceRemoved(info.address);
412 }
413}
414
415void AdapterManager::onAdapterAdded(winrt::hstring id)
416{
417 emit adapterAdded(id);
418 // We need to invoke the method from a Qt thread, so that we could start a
419 // timer there.
420 QMetaObject::invokeMethod(this, "tryResubscribeToStateChanges", Qt::QueuedConnection,
421 Q_ARG(winrt::hstring, id), Q_ARG(int, 0));
422}
423
424void AdapterManager::onAdapterRemoved(winrt::hstring id)
425{
427 QMutexLocker locker(&mMutex);
428 if (mRadios.contains(id)) {
429 // here we can't simply remove the record from the map, because the
430 // same adapter can later be reconnected, and we need to keep track of
431 // the created clients.
432 mRadios[id].radio = nullptr;
433 }
434}
435
436void AdapterManager::subscribeToStateChanges(AdapterManager::RadioInfo &info)
437{
438 QPointer<AdapterManager> thisPtr(this);
439 info.stateToken = info.radio.StateChanged([thisPtr](Radio r, const auto &) {
440 // This callback fires twice. Looks like an MS bug.
441 // This callback comes in a separate thread
442 if (thisPtr) {
443 thisPtr->onStateChange(r);
444 }
445 });
446}
447
448void AdapterManager::unsubscribeFromStateChanges(AdapterManager::RadioInfo &info)
449{
450 // This method can be called after the radio is disconnected
451 if (info.radio)
452 info.radio.StateChanged(info.stateToken);
453}
454
455Q_GLOBAL_STATIC(AdapterManager, adapterManager)
456
457static DeviceInformationCollection getAvailableAdapters()
458{
459 const auto btSelector = BluetoothAdapter::GetDeviceSelector();
460 DeviceInformationCollection deviceInfoCollection(nullptr);
461 await(DeviceInformation::FindAllAsync(btSelector), deviceInfoCollection);
462 return deviceInfoCollection;
463}
464
465DeviceInformationPairing pairingInfoFromAddress(const QBluetoothAddress &address)
466{
467 const quint64 addr64 = address.toUInt64();
468 BluetoothLEDevice leDevice(nullptr);
469 bool res = await(BluetoothLEDevice::FromBluetoothAddressAsync(addr64), leDevice, 5000);
470 if (res && leDevice)
471 return leDevice.DeviceInformation().Pairing();
472
473 BluetoothDevice device(nullptr);
474 res = await(BluetoothDevice::FromBluetoothAddressAsync(addr64), device, 5000);
475 if (res && device)
476 return device.DeviceInformation().Pairing();
477
478 return nullptr;
479}
480
482 : public winrt::implements<PairingWorker, winrt::Windows::Foundation::IInspectable>
483{
485 ~PairingWorker() = default;
486
488
489private:
490 QPointer<QBluetoothLocalDevice> q;
491 void onPairingRequested(DeviceInformationCustomPairing const&,
492 DevicePairingRequestedEventArgs args);
493};
494
496{
497 // Note: some of the functions called here use await() in their implementation.
498 // Hence we need to verify that the 'q' is still valid after such calls, as
499 // it may have been destroyed. In that scenario the ComPtr pointing to this
500 // object is destroyed, but to protect this function's possible execution at that time,
501 // the get_strong() call below will ensure we can execute until the end.
502 auto ref = get_strong();
503 DeviceInformationPairing pairingInfo = pairingInfoFromAddress(addr);
504 switch (pairing) {
507 {
508 DeviceInformationCustomPairing customPairing = pairingInfo.Custom();
509 auto token = customPairing.PairingRequested(
510 { get_weak(), &PairingWorker::onPairingRequested });
511 DevicePairingResult result{nullptr};
512 bool res = await(customPairing.PairAsync(DevicePairingKinds::ConfirmOnly), result, 30000);
513 customPairing.PairingRequested(token);
514 if (!res || result.Status() != DevicePairingResultStatus::Paired) {
515 if (q)
517 return;
518 }
519
520 // Check the actual protection level used and signal the success
521 if (q) {
522 const auto resultingPairingStatus = q->pairingStatus(addr);
523 // pairingStatus() uses await/spins eventloop => check 'q' validity again
524 if (q)
525 emit q->pairingFinished(addr, resultingPairingStatus);
526 }
527 return;
528 }
530 DeviceUnpairingResult unpairingResult{nullptr};
531 bool res = await(pairingInfo.UnpairAsync(), unpairingResult, 10000);
532 if (!res || unpairingResult.Status() != DeviceUnpairingResultStatus::Unpaired) {
533 if (q)
535 return;
536 }
537 if (q)
538 emit q->pairingFinished(addr, QBluetoothLocalDevice::Unpaired);
539 return;
540 }
541}
542
543void PairingWorker::onPairingRequested(const DeviceInformationCustomPairing &,
544 DevicePairingRequestedEventArgs args)
545{
546 if (args.PairingKind() != DevicePairingKinds::ConfirmOnly) {
547 Q_ASSERT(false);
548 return;
549 }
550
551 args.Accept();
552}
553
555 QObject(parent),
557{
559}
560
562 QObject(parent),
564{
566}
567
570 : q_ptr(q),
571 mAdapter(nullptr),
572 mMode(QBluetoothLocalDevice::HostPoweredOff)
573{
574 mPairingWorker = winrt::make_self<PairingWorker>(q);
575 if (address.isNull()) {
576 // use default adapter
577 bool res = await(BluetoothAdapter::GetDefaultAsync(), mAdapter);
578 if (res && mAdapter) {
579 // get adapter name
580 mDeviceId = mAdapter.DeviceId();
581 DeviceInformation devInfo(nullptr);
582 res = await(DeviceInformation::CreateFromIdAsync(mDeviceId), devInfo);
583 if (res && devInfo)
584 mAdapterName = QString::fromStdString(winrt::to_string(devInfo.Name()));
585 }
586 } else {
587 // try to select a proper device
588 const auto deviceInfoCollection = getAvailableAdapters();
589 for (const auto &devInfo : deviceInfoCollection) {
590 BluetoothAdapter adapter(nullptr);
591 const bool res = await(BluetoothAdapter::FromIdAsync(devInfo.Id()), adapter);
592 if (res && adapter) {
593 QBluetoothAddress adapterAddress(adapter.BluetoothAddress());
594 if (adapterAddress == address) {
595 mAdapter = adapter;
596 mDeviceId = adapter.DeviceId();
597 mAdapterName = QString::fromStdString(winrt::to_string(devInfo.Name()));
598 break;
599 }
600 }
601 }
602 }
603 if (mAdapter) {
604 mMode = adapterManager->addClient(this);
605 } else {
606 if (address.isNull()) {
607 qCWarning(QT_BT_WINDOWS, "Failed to create QBluetoothLocalDevice - no adapter found");
608 } else {
609 qCWarning(QT_BT_WINDOWS) << "Failed to create QBluetoothLocalDevice for address"
610 << address;
611 }
612 }
613}
614
616{
617 adapterManager->removeClient(mDeviceId);
618 mAdapter = nullptr;
619}
620
622{
623 return mAdapter != nullptr;
624}
625
626void QBluetoothLocalDevicePrivate::updateAdapterState(QBluetoothLocalDevice::HostMode mode)
627{
628 if (!mAdapter) {
629 qCWarning(QT_BT_WINDOWS, "Trying to update state for an uninitialized adapter");
630 return;
631 }
632 const auto desiredMode = adjustHostMode(mode);
633 if (desiredMode != mMode) {
634 // From the MS docs: Note that your code should call RequestAccessAsync
635 // at least once, from the UI thread, before trying to call
636 // SetStateAsync. This is because in some regions, with some user
637 // settings choices, attempting to change radio state requires user
638 // permission.
639 RadioAccessStatus status = RadioAccessStatus::Unspecified;
640 bool res = await(Radio::RequestAccessAsync(), status);
641 if (res && status == RadioAccessStatus::Allowed) {
642 // Now send a signal to the AdapterWatcher. That class will manage
643 // the actual state change.
644 emit updateMode(mDeviceId, desiredMode);
645 } else {
646 qCWarning(QT_BT_WINDOWS, "Failed to update adapter state: operation denied!");
647 }
648 }
649}
650
651void QBluetoothLocalDevicePrivate::onAdapterRemoved(winrt::hstring id)
652{
653 if (id == mDeviceId) {
654 qCDebug(QT_BT_WINDOWS, "Current adapter is removed");
655 mAdapter = nullptr;
658 emit q_ptr->hostModeStateChanged(mMode);
659 }
660 }
661}
662
663void QBluetoothLocalDevicePrivate::onAdapterAdded(winrt::hstring id)
664{
665 if (id == mDeviceId && !mAdapter) {
666 // adapter was reconnected - try to recreate the internals
667 qCDebug(QT_BT_WINDOWS, "Adapter reconnected - trying to restore QBluetoothLocalDevice");
668 const bool res = await(BluetoothAdapter::FromIdAsync(mDeviceId), mAdapter);
669 if (!res || !mAdapter)
670 qCWarning(QT_BT_WINDOWS, "Failed to restore adapter");
671 }
672}
673
674void QBluetoothLocalDevicePrivate::radioModeChanged(winrt::hstring id, QBluetoothLocalDevice::HostMode mode)
675{
676 if (id == mDeviceId && mAdapter) {
677 if (mode != mMode) {
678 mMode = mode;
679 emit q_ptr->hostModeStateChanged(mMode);
680 }
681 }
682}
683
684void QBluetoothLocalDevicePrivate::onDeviceAdded(const QBluetoothAddress &address)
685{
686 if (isValid())
688}
689
690void QBluetoothLocalDevicePrivate::onDeviceRemoved(const QBluetoothAddress &address)
691{
692 if (isValid())
694}
695
697{
699 if (!isValid() || address.isNull()) {
700 QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
703 return;
704 }
705
706 // Check current pairing status and determine if there is a need to do anything
707 const Pairing currentPairingStatus = pairingStatus(address);
708 if ((currentPairingStatus == Unpaired && pairing == Unpaired)
709 || (currentPairingStatus != Unpaired && pairing != Unpaired)) {
710 qCDebug(QT_BT_WINDOWS) << "requestPairing() no change needed to pairing" << address;
711 QMetaObject::invokeMethod(this, "pairingFinished", Qt::QueuedConnection,
714 currentPairingStatus));
715 return;
716 }
717
718 qCDebug(QT_BT_WINDOWS) << "requestPairing() initiating (un)pairing" << address << pairing;
719 d->mPairingWorker->pairAsync(address, pairing);
720}
721
723 const QBluetoothAddress &address) const
724{
725 if (!isValid() || address.isNull())
726 return Unpaired;
727
728 const DeviceInformationPairing pairingInfo = pairingInfoFromAddress(address);
729 if (!pairingInfo || !pairingInfo.IsPaired())
730 return Unpaired;
731
732 const DevicePairingProtectionLevel protection = pairingInfo.ProtectionLevel();
733 if (protection == DevicePairingProtectionLevel::Encryption
734 || protection == DevicePairingProtectionLevel::EncryptionAndAuthentication)
735 return AuthorizedPaired;
736 return Paired;
737}
738
740{
742 d->updateAdapterState(mode);
743}
744
746{
747 Q_D(const QBluetoothLocalDevice);
748 return d->mMode;
749}
750
751QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
752{
753 // On windows we have only one Bluetooth adapter, but generally can create multiple
754 // QBluetoothLocalDevice instances (both valid and invalid). Invalid instances shouldn't return
755 // any connected devices, while all valid instances can use information from a global static.
756 Q_D(const QBluetoothLocalDevice);
757 return d->isValid() ? adapterManager->connectedDevices() : QList<QBluetoothAddress>();
758}
759
761{
763}
764
766{
767 Q_D(const QBluetoothLocalDevice);
768 return d->mAdapterName;
769}
770
772{
773 Q_D(const QBluetoothLocalDevice);
774 return d->mAdapter ? QBluetoothAddress(d->mAdapter.BluetoothAddress()) : QBluetoothAddress();
775}
776
777QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
778{
779 QList<QBluetoothHostInfo> devices;
780 const auto deviceInfoCollection = getAvailableAdapters();
781 if (deviceInfoCollection) {
782 for (const auto &devInfo : deviceInfoCollection) {
783 BluetoothAdapter adapter(nullptr);
784 const bool res = await(BluetoothAdapter::FromIdAsync(devInfo.Id()), adapter);
785 if (res && adapter) {
787 info.setName(QString::fromStdString(winrt::to_string(devInfo.Name())));
788 info.setAddress(QBluetoothAddress(adapter.BluetoothAddress()));
789 devices.push_back(std::move(info));
790 }
791 }
792 }
793 return devices;
794}
795
797
798#include "qbluetoothlocaldevice_winrt.moc"
IOBluetoothDevice * device
void adapterAdded(winrt::hstring id)
void deviceAdded(const QBluetoothAddress &address)
void deviceRemoved(const QBluetoothAddress &address)
void updateMode(winrt::hstring adapterId, QBluetoothLocalDevice::HostMode mode)
void modeChanged(winrt::hstring id, QBluetoothLocalDevice::HostMode mode)
QList< QBluetoothAddress > connectedDevices()
void adapterRemoved(winrt::hstring id)
QBluetoothLocalDevice::HostMode addClient(QBluetoothLocalDevicePrivate *client)
void removeClient(winrt::hstring adapterId)
\inmodule QtBluetooth
void deviceAdded(winrt::hstring deviceId, int id)
void deviceRemoved(winrt::hstring deviceId, int id)
\inmodule QtBluetooth
QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *, const QBluetoothAddress &=QBluetoothAddress())
\inmodule QtBluetooth
void powerOn()
Powers on the device after returning it to the hostMode() state, if it was powered off.
Pairing
This enum describes the pairing state between the two Bluetooth devices.
void requestPairing(const QBluetoothAddress &address, Pairing pairing)
Set the pairing status with address.
HostMode
This enum describes the most of the local Bluetooth device.
HostMode hostMode() const
Returns the current host mode of this local Bluetooth device.
void deviceConnected(const QBluetoothAddress &address)
Error
This enum describes errors that maybe returned.
QList< QBluetoothAddress > connectedDevices() const
QBluetoothLocalDevice(QObject *parent=nullptr)
Constructs a QBluetoothLocalDevice with parent.
QString name() const
Returns the name assgined by the user to this Bluetooth device.
static QList< QBluetoothHostInfo > allDevices()
Returns a list of all available local Bluetooth devices.
Pairing pairingStatus(const QBluetoothAddress &address) const
Returns the current bluetooth pairing status of address, if it's unpaired, paired,...
QBluetoothAddress address() const
Returns the MAC address of this Bluetooth device.
void deviceDisconnected(const QBluetoothAddress &address)
void setHostMode(QBluetoothLocalDevice::HostMode mode)
Sets the host mode of this local Bluetooth device to mode.
void hostModeStateChanged(QBluetoothLocalDevice::HostMode state)
The state of the host has transitioned to a different HostMode.
static void processEvents(QEventLoop::ProcessEventsFlags flags=QEventLoop::AllEvents)
Processes some pending events for the calling thread according to the specified flags.
\inmodule QtCore
bool removeOne(const AT &t)
Definition qlist.h:598
void push_back(parameter_type t)
Definition qlist.h:675
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
bool contains(const Key &key) const
Definition qmap.h:341
size_type remove(const Key &key)
Definition qmap.h:300
\inmodule QtCore
Definition qmutex.h:313
void unlock() noexcept
Unlocks this mutex locker.
Definition qmutex.h:319
void relock() noexcept
Relocks an unlocked mutex locker.
Definition qmutex.h:320
\inmodule QtCore
Definition qmutex.h:281
\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
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromStdString(const std::string &s)
Definition qstring.h:1447
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
bool singleShot
whether the timer is a single-shot timer
Definition qtimer.h:22
void init(AdapterManager *manager, AddedSlot onAdded, RemovedSlot onRemoved)
WatcherWrapper(winrt::hstring selector)
#define this
Definition dialogs.cpp:9
else opt state
[0]
Token token
Definition keywords.cpp:444
Combined button and popup list for selecting options.
@ QueuedConnection
void registerQBluetoothLocalDeviceMetaType()
static const int kMaximumAttempts
static RadioState windowsStateFromMode(QBluetoothLocalDevice::HostMode mode)
static DeviceInformationCollection getAvailableAdapters()
DeviceInformationPairing pairingInfoFromAddress(const QBluetoothAddress &address)
static QT_BEGIN_NAMESPACE bool await(IAsyncOperation< T > &&asyncInfo, T &result, uint timeout=0)
static QBluetoothLocalDevice::HostMode adjustHostMode(QBluetoothLocalDevice::HostMode mode)
static QBluetoothLocalDevice::HostMode modeFromWindowsBluetoothState(RadioState state)
static BluetoothInfo getBluetoothInfo(winrt::hstring id)
EGLDeviceEXT * devices
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLenum mode
GLuint64 key
GLboolean GLboolean GLboolean GLboolean a
[7]
GLboolean r
[2]
GLenum GLuint id
[7]
GLbitfield GLuint64 timeout
[4]
GLint ref
GLuint res
GLenum const void * addr
GLuint GLuint64EXT address
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_OBJECT
#define slots
#define signals
#define emit
#define Q_SLOT
unsigned long long quint64
Definition qtypes.h:61
unsigned int uint
Definition qtypes.h:34
QFileSelector selector
[1]
QStringList keys
QObject::connect nullptr
QTimer * timer
[3]
QNetworkAccessManager manager
QHostInfo info
[0]
QJSValueList args
~PairingWorker()=default
PairingWorker(QBluetoothLocalDevice *device)
void pairAsync(const QBluetoothAddress &addr, QBluetoothLocalDevice::Pairing pairing)
bool contains(const AT &t) const noexcept
Definition qlist.h:45
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...