4#include <private/qbluetoothutils_winrt_p.h>
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>
19#include <QtCore/QCoreApplication>
20#include <QtCore/QElapsedTimer>
21#include <QtCore/QLoggingCategory>
22#include <QtCore/QMutex>
23#include <QtCore/QPointer>
24#include <QtCore/QTimer>
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;
34Q_DECLARE_LOGGING_CATEGORY(QT_BT_WINDOWS)
37static bool await(IAsyncOperation<T> &&asyncInfo, T &result, uint timeout = 0)
39 using WinRtAsyncStatus = winrt::Windows::Foundation::AsyncStatus;
40 WinRtAsyncStatus status;
45 QCoreApplication::processEvents();
46 status = asyncInfo.Status();
47 }
while (status == WinRtAsyncStatus::Started && (!timeout || !timer.hasExpired(timeout)));
48 if (status == WinRtAsyncStatus::Completed) {
49 result = asyncInfo.GetResults();
56static QBluetoothLocalDevice::HostMode adjustHostMode(QBluetoothLocalDevice::HostMode mode)
60 return (mode == QBluetoothLocalDevice::HostPoweredOff)
61 ? mode : QBluetoothLocalDevice::HostConnectable;
64static QBluetoothLocalDevice::HostMode modeFromWindowsBluetoothState(RadioState state)
66 return (state == RadioState::On) ? QBluetoothLocalDevice::HostConnectable
67 : QBluetoothLocalDevice::HostPoweredOff;
70static RadioState windowsStateFromMode(QBluetoothLocalDevice::HostMode mode)
72 return (mode == QBluetoothLocalDevice::HostPoweredOff) ? RadioState::Off : RadioState::On;
86 template<
typename AddedSlot,
typename RemovedSlot>
87 void init(AdapterManager *manager, AddedSlot onAdded, RemovedSlot onRemoved)
90 qWarning(
"QBluetoothLocalDevice: failed to create device watcher!");
94 QObject::connect(mWatcher.get(), &QBluetoothDeviceWatcherWinRT::deviceAdded,
95 manager, onAdded, Qt::QueuedConnection);
96 QObject::connect(mWatcher.get(), &QBluetoothDeviceWatcherWinRT::deviceRemoved,
97 manager, onRemoved, Qt::QueuedConnection);
100 if (mWatcher->init())
105 std::shared_ptr<QBluetoothDeviceWatcherWinRT> mWatcher =
nullptr;
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
136 void updateMode(winrt::hstring adapterId, QBluetoothLocalDevice::HostMode mode);
141 void modeChanged(winrt::hstring id, QBluetoothLocalDevice::HostMode mode);
147 Radio radio =
nullptr;
148 winrt::event_token stateToken;
150 RadioState currentState = RadioState::Unknown;
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);
161 Q_SLOT
void onDeviceAdded(winrt::hstring id);
162 Q_SLOT
void onDeviceRemoved(winrt::hstring id);
164 std::unique_ptr<WatcherWrapper> mAdapterWatcher =
nullptr;
165 std::unique_ptr<WatcherWrapper> mLeDevicesWatcher =
nullptr;
166 std::unique_ptr<WatcherWrapper> mClassicDevicesWatcher =
nullptr;
169 QMap<winrt::hstring, RadioInfo> mRadios;
170 QList<QBluetoothAddress> mConnectedDevices;
175 const auto adapterSelector = BluetoothAdapter::GetDeviceSelector();
176 mAdapterWatcher = std::make_unique<WatcherWrapper>(adapterSelector);
177 mAdapterWatcher->init(
this, &AdapterManager::onAdapterAdded, &AdapterManager::onAdapterRemoved);
182 const auto leSelector = BluetoothLEDevice::GetDeviceSelectorFromConnectionStatus(
183 BluetoothConnectionStatus::Connected);
184 mLeDevicesWatcher = std::make_unique<WatcherWrapper>(leSelector);
185 mLeDevicesWatcher->init(
this, &AdapterManager::onDeviceAdded, &AdapterManager::onDeviceRemoved);
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);
200 QMutexLocker locker(&mMutex);
201 return mConnectedDevices;
206 connect(
this, &AdapterManager::modeChanged, client,
207 &QBluetoothLocalDevicePrivate::radioModeChanged, Qt::QueuedConnection);
208 connect(client, &QBluetoothLocalDevicePrivate::updateMode,
this, &AdapterManager::updateMode,
209 Qt::QueuedConnection);
210 connect(
this, &AdapterManager::adapterAdded, client,
211 &QBluetoothLocalDevicePrivate::onAdapterAdded, Qt::QueuedConnection);
212 connect(
this, &AdapterManager::adapterRemoved, client,
213 &QBluetoothLocalDevicePrivate::onAdapterRemoved, Qt::QueuedConnection);
214 connect(
this, &AdapterManager::deviceAdded, client,
215 &QBluetoothLocalDevicePrivate::onDeviceAdded, Qt::QueuedConnection);
216 connect(
this, &AdapterManager::deviceRemoved, client,
217 &QBluetoothLocalDevicePrivate::onDeviceRemoved, Qt::QueuedConnection);
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());
230 Radio r = getRadioFromAdapterId(adapterId);
236 info.currentState = r.State();
237 subscribeToStateChanges(info);
238 mRadios.insert(adapterId, info);
239 return modeFromWindowsBluetoothState(info.currentState);
242 qCWarning(QT_BT_WINDOWS,
"Failed to subscribe to adapter state changes");
243 return QBluetoothLocalDevice::HostPoweredOff;
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);
256 qCWarning(QT_BT_WINDOWS) <<
"Removing client for an unknown adapter id"
257 << QString::fromStdString(winrt::to_string(adapterId));
263 QMutexLocker locker(&mMutex);
264 if (mRadios.contains(adapterId)) {
265 RadioAccessStatus status = RadioAccessStatus::Unspecified;
266 auto radio = mRadios[adapterId].radio;
269 const bool res = await(radio.SetStateAsync(windowsStateFromMode(mode)), status);
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";
284 BluetoothAdapter a(
nullptr);
285 bool res = await(BluetoothAdapter::FromIdAsync(id), a);
288 res = await(a.GetRadioAsync(), r);
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();
303 emit modeChanged(key, modeFromWindowsBluetoothState(info.currentState));
317void AdapterManager::tryResubscribeToStateChanges(winrt::hstring id,
int numAttempts)
319 QMutexLocker locker(&mMutex);
320 if (mRadios.contains(id)) {
323 if (mRadios[id].radio !=
nullptr)
326 Radio r = getRadioFromAdapterId(id);
331 if (mRadios.contains(id)) {
332 auto &info = mRadios[id];
334 info.currentState = r.State();
335 subscribeToStateChanges(info);
336 emit modeChanged(id, modeFromWindowsBluetoothState(info.currentState));
340 qCDebug(QT_BT_WINDOWS,
"Trying to resubscribe for the state changes");
341 QPointer<AdapterManager> thisPtr(
this);
342 QTimer::singleShot(100, [thisPtr, id, numAttempts]() {
344 thisPtr->tryResubscribeToStateChanges(id, numAttempts);
347 qCWarning(QT_BT_WINDOWS,
348 "Failed to resubscribe to the state changes after %d attempts!",
364 BluetoothDevice device(
nullptr);
365 bool res = await(BluetoothDevice::FromIdAsync(id), device, 5000);
367 return { QBluetoothAddress(device.BluetoothAddress()),
368 device.ConnectionStatus() == BluetoothConnectionStatus::Connected };
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 };
390 QMutexLocker locker(&mMutex);
391 found = mConnectedDevices.contains(info.address);
393 mConnectedDevices.push_back(info.address);
397 emit deviceAdded(info.address);
407 QMutexLocker locker(&mMutex);
408 found = mConnectedDevices.removeOne(info.address);
411 emit deviceRemoved(info.address);
417 emit adapterAdded(id);
420 QMetaObject::invokeMethod(
this,
"tryResubscribeToStateChanges", Qt::QueuedConnection,
421 Q_ARG(winrt::hstring, id), Q_ARG(
int, 0));
426 emit adapterRemoved(id);
427 QMutexLocker locker(&mMutex);
428 if (mRadios.contains(id)) {
432 mRadios[id].radio =
nullptr;
438 QPointer<AdapterManager> thisPtr(
this);
439 info.stateToken = info.radio.StateChanged([thisPtr](Radio r,
const auto &) {
443 thisPtr->onStateChange(r);
452 info.radio.StateChanged(info.stateToken);
459 const auto btSelector = BluetoothAdapter::GetDeviceSelector();
460 DeviceInformationCollection deviceInfoCollection(
nullptr);
461 await(DeviceInformation::FindAllAsync(btSelector), deviceInfoCollection);
462 return deviceInfoCollection;
467 const quint64 addr64 = address.toUInt64();
468 BluetoothLEDevice leDevice(
nullptr);
469 bool res = await(BluetoothLEDevice::FromBluetoothAddressAsync(addr64), leDevice, 5000);
471 return leDevice.DeviceInformation().Pairing();
473 BluetoothDevice device(
nullptr);
474 res = await(BluetoothDevice::FromBluetoothAddressAsync(addr64), device, 5000);
476 return device.DeviceInformation().Pairing();
487 void pairAsync(
const QBluetoothAddress &addr, QBluetoothLocalDevice::Pairing pairing);
490 QPointer<QBluetoothLocalDevice> q;
491 void onPairingRequested(DeviceInformationCustomPairing
const&,
492 DevicePairingRequestedEventArgs args);
502 auto ref = get_strong();
503 DeviceInformationPairing pairingInfo = pairingInfoFromAddress(addr);
505 case QBluetoothLocalDevice::Paired:
506 case QBluetoothLocalDevice::AuthorizedPaired:
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) {
516 emit q->errorOccurred(QBluetoothLocalDevice::PairingError);
522 const auto resultingPairingStatus = q->pairingStatus(addr);
525 emit q->pairingFinished(addr, resultingPairingStatus);
529 case QBluetoothLocalDevice::Unpaired:
530 DeviceUnpairingResult unpairingResult{
nullptr};
531 bool res = await(pairingInfo.UnpairAsync(), unpairingResult, 10000);
532 if (!res || unpairingResult.Status() != DeviceUnpairingResultStatus::Unpaired) {
534 emit q->errorOccurred(QBluetoothLocalDevice::PairingError);
538 emit q->pairingFinished(addr, QBluetoothLocalDevice::Unpaired);
543void PairingWorker::onPairingRequested(
const DeviceInformationCustomPairing &,
544 DevicePairingRequestedEventArgs args)
546 if (args.PairingKind() != DevicePairingKinds::ConfirmOnly) {
554QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
556 d_ptr(
new QBluetoothLocalDevicePrivate(
this, QBluetoothAddress()))
558 registerQBluetoothLocalDeviceMetaType();
561QBluetoothLocalDevice::QBluetoothLocalDevice(
const QBluetoothAddress &address, QObject *parent) :
563 d_ptr(
new QBluetoothLocalDevicePrivate(
this, address))
565 registerQBluetoothLocalDeviceMetaType();
569 QBluetoothAddress address)
574 mPairingWorker = winrt::make_self<PairingWorker>(q);
575 if (address.isNull()) {
577 bool res = await(BluetoothAdapter::GetDefaultAsync(), mAdapter);
578 if (res && mAdapter) {
580 mDeviceId = mAdapter.DeviceId();
581 DeviceInformation devInfo(
nullptr);
582 res = await(DeviceInformation::CreateFromIdAsync(mDeviceId), devInfo);
584 mAdapterName = QString::fromStdString(winrt::to_string(devInfo.Name()));
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) {
596 mDeviceId = adapter.DeviceId();
597 mAdapterName = QString::fromStdString(winrt::to_string(devInfo.Name()));
604 mMode = adapterManager->addClient(
this);
606 if (address.isNull()) {
607 qCWarning(QT_BT_WINDOWS,
"Failed to create QBluetoothLocalDevice - no adapter found");
609 qCWarning(QT_BT_WINDOWS) <<
"Failed to create QBluetoothLocalDevice for address"
617 adapterManager->removeClient(mDeviceId);
623 return mAdapter !=
nullptr;
629 qCWarning(QT_BT_WINDOWS,
"Trying to update state for an uninitialized adapter");
632 const auto desiredMode = adjustHostMode(mode);
633 if (desiredMode != mMode) {
639 RadioAccessStatus status = RadioAccessStatus::Unspecified;
640 bool res = await(Radio::RequestAccessAsync(), status);
641 if (res && status == RadioAccessStatus::Allowed) {
644 emit updateMode(mDeviceId, desiredMode);
646 qCWarning(QT_BT_WINDOWS,
"Failed to update adapter state: operation denied!");
653 if (id == mDeviceId) {
654 qCDebug(QT_BT_WINDOWS,
"Current adapter is removed");
656 if (mMode != QBluetoothLocalDevice::HostPoweredOff) {
657 mMode = QBluetoothLocalDevice::HostPoweredOff;
658 emit q_ptr->hostModeStateChanged(mMode);
665 if (id == mDeviceId && !mAdapter) {
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");
676 if (id == mDeviceId && mAdapter) {
679 emit q_ptr->hostModeStateChanged(mMode);
687 emit q_ptr->deviceConnected(address);
693 emit q_ptr->deviceDisconnected(address);
696void QBluetoothLocalDevice::requestPairing(
const QBluetoothAddress &address, Pairing pairing)
698 Q_D(QBluetoothLocalDevice);
699 if (!isValid() || address.isNull()) {
700 QMetaObject::invokeMethod(
this,
"errorOccurred", Qt::QueuedConnection,
701 Q_ARG(QBluetoothLocalDevice::Error,
702 QBluetoothLocalDevice::PairingError));
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,
712 Q_ARG(QBluetoothAddress, address),
713 Q_ARG(QBluetoothLocalDevice::Pairing,
714 currentPairingStatus));
718 qCDebug(QT_BT_WINDOWS) <<
"requestPairing() initiating (un)pairing" << address << pairing;
719 d->mPairingWorker->pairAsync(address, pairing);
722QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(
723 const QBluetoothAddress &address)
const
725 if (!isValid() || address.isNull())
728 const DeviceInformationPairing pairingInfo = pairingInfoFromAddress(address);
729 if (!pairingInfo || !pairingInfo.IsPaired())
732 const DevicePairingProtectionLevel protection = pairingInfo.ProtectionLevel();
733 if (protection == DevicePairingProtectionLevel::Encryption
734 || protection == DevicePairingProtectionLevel::EncryptionAndAuthentication)
735 return AuthorizedPaired;
739void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
741 Q_D(QBluetoothLocalDevice);
742 d->updateAdapterState(mode);
745QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode()
const
747 Q_D(
const QBluetoothLocalDevice);
751QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices()
const
756 Q_D(
const QBluetoothLocalDevice);
757 return d->isValid() ? adapterManager->connectedDevices() : QList<QBluetoothAddress>();
760void QBluetoothLocalDevice::powerOn()
762 setHostMode(HostConnectable);
765QString QBluetoothLocalDevice::name()
const
767 Q_D(
const QBluetoothLocalDevice);
768 return d->mAdapterName;
771QBluetoothAddress QBluetoothLocalDevice::address()
const
773 Q_D(
const QBluetoothLocalDevice);
774 return d->mAdapter ? QBluetoothAddress(d->mAdapter.BluetoothAddress()) : QBluetoothAddress();
777QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
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) {
786 QBluetoothHostInfo info;
787 info.setName(QString::fromStdString(winrt::to_string(devInfo.Name())));
788 info.setAddress(QBluetoothAddress(adapter.BluetoothAddress()));
789 devices.push_back(std::move(info));
798#include "qbluetoothlocaldevice_winrt.moc"
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)
void removeClient(winrt::hstring adapterId)
QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *=nullptr, QBluetoothAddress=QBluetoothAddress())
void init(AdapterManager *manager, AddedSlot onAdded, RemovedSlot onRemoved)
WatcherWrapper(winrt::hstring selector)
static DeviceInformationCollection getAvailableAdapters()
DeviceInformationPairing pairingInfoFromAddress(const QBluetoothAddress &address)
static const int kMaximumAttempts
static BluetoothInfo getBluetoothInfo(winrt::hstring id)
QBluetoothAddress address
PairingWorker(QBluetoothLocalDevice *device)
void pairAsync(const QBluetoothAddress &addr, QBluetoothLocalDevice::Pairing pairing)