summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/qlowenergycontroller_osx.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth/qlowenergycontroller_osx.mm')
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm785
1 files changed, 586 insertions, 199 deletions
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index 396f82bb..a2923d81 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -1,42 +1,51 @@
/****************************************************************************
**
-** Copyright (C) 2015 The Qt Company Ltd.
-** Copyright (C) 2013 Javier S. Pedro <maemo@javispedro.com>
-** Contact: http://www.qt.io/licensing/
+** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Javier S. Pedro <maemo@javispedro.com>
+** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
**
-** $QT_BEGIN_LICENSE:LGPL21$
+** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see http://www.qt.io/terms-conditions. For further
-** information use the contact form at http://www.qt.io/contact-us.
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 2.1 or version 3 as published by the Free
-** Software Foundation and appearing in the file LICENSE.LGPLv21 and
-** LICENSE.LGPLv3 included in the packaging of this file. Please review the
-** following information to ensure the GNU Lesser General Public License
-** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
-** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
-** As a special exception, The Qt Company gives you certain additional
-** rights. These rights are described in The Qt Company LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
+#include "osx/osxbtnotifier_p.h"
#include "osx/osxbtutility_p.h"
#include "osx/uistrings_p.h"
+
#include "qlowenergyserviceprivate_p.h"
#include "qlowenergycontroller_osx_p.h"
+#include "qlowenergyservicedata.h"
#include "qbluetoothlocaldevice.h"
#include "qbluetoothdeviceinfo.h"
#include "qlowenergycontroller.h"
@@ -45,7 +54,6 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qbytearray.h>
-#include <QtCore/qsysinfo.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
@@ -62,6 +70,8 @@ static void registerQLowEnergyControllerMetaType()
if (!initDone) {
qRegisterMetaType<QLowEnergyController::ControllerState>();
qRegisterMetaType<QLowEnergyController::Error>();
+ qRegisterMetaType<QLowEnergyHandle>("QLowEnergyHandle");
+ qRegisterMetaType<QSharedPointer<QLowEnergyServicePrivate> >();
initDone = true;
}
}
@@ -76,8 +86,7 @@ ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CB
CBUUID *const cbUuid = cbService.UUID;
if (!cbUuid) {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "invalid service, "
- "UUID is nil";
+ qCDebug(QT_BT_OSX) << "invalid service, UUID is nil";
return ServicePrivate();
}
@@ -94,18 +103,12 @@ ServicePrivate qt_createLEService(QLowEnergyControllerPrivateOSX *controller, CB
// TODO: isPrimary is ... always 'NO' - to be investigated.
/*
- #if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_9, __IPHONE_6_0)
- using OSXBluetooth::qt_OS_limit;
- if (QSysInfo::MacintoshVersion >= qt_OS_limit(QSysInfo::MV_10_9, QSysInfo::MV_IOS_6_0)) {
- if (!cbService.isPrimary) {
- // Our guess included/not was probably wrong.
- newService->type &= ~QLowEnergyService::PrimaryService;
- newService->type |= QLowEnergyService::IncludedService;
- }
+ if (!cbService.isPrimary) {
+ // Our guess included/not was probably wrong.
+ newService->type &= ~QLowEnergyService::PrimaryService;
+ newService->type |= QLowEnergyService::IncludedService;
}
- #endif
*/
- // No such property before 10_9/6_0.
return newService;
}
@@ -128,31 +131,12 @@ UUIDList qt_servicesUuids(NSArray *services)
}
-QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController *q)
- : q_ptr(q),
- isConnecting(false),
- lastError(QLowEnergyController::NoError),
- controllerState(QLowEnergyController::UnconnectedState),
- addressType(QLowEnergyController::PublicAddress)
-{
- registerQLowEnergyControllerMetaType();
-
- // This is the "wrong" constructor - no valid device UUID to connect later.
- Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)");
- // We still create a manager, to simplify error handling later.
- centralManager.reset([[ObjCCentralManager alloc] initWithDelegate:this]);
- if (!centralManager) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO
- << "failed to initialize central manager";
- }
-}
-
-QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController *q,
+QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController::Role r,
+ QLowEnergyController *q,
const QBluetoothDeviceInfo &deviceInfo)
: q_ptr(q),
deviceUuid(deviceInfo.deviceUuid()),
deviceName(deviceInfo.name()),
- isConnecting(false),
lastError(QLowEnergyController::NoError),
controllerState(QLowEnergyController::UnconnectedState),
addressType(QLowEnergyController::PublicAddress)
@@ -160,46 +144,86 @@ QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyControl
registerQLowEnergyControllerMetaType();
Q_ASSERT_X(q, Q_FUNC_INFO, "invalid q_ptr (null)");
- centralManager.reset([[ObjCCentralManager alloc] initWithDelegate:this]);
- if (!centralManager) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO
- << "failed to initialize central manager";
+
+ using OSXBluetooth::LECBManagerNotifier;
+
+ role = r;
+
+ QScopedPointer<LECBManagerNotifier> notifier(new LECBManagerNotifier);
+ if (role == QLowEnergyController::PeripheralRole) {
+#ifndef Q_OS_TVOS
+ peripheralManager.reset([[ObjCPeripheralManager alloc] initWith:notifier.data()]);
+ if (!peripheralManager) {
+ qCWarning(QT_BT_OSX) << "failed to initialize peripheral manager";
+ return;
+ }
+#else
+ qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform";
+ return;
+#endif
+ } else {
+ centralManager.reset([[ObjCCentralManager alloc] initWith:notifier.data()]);
+ if (!centralManager) {
+ qCWarning(QT_BT_OSX) << "failed to initialize central manager";
+ return;
+ }
+ }
+
+ if (!connectSlots(notifier.data())) {
+ qCWarning(QT_BT_OSX) << "failed to connect to notifier's signal(s)";
}
+ // Ownership was taken by central manager.
+ notifier.take();
}
QLowEnergyControllerPrivateOSX::~QLowEnergyControllerPrivateOSX()
{
+ if (const auto leQueue = OSXBluetooth::qt_LE_queue()) {
+ if (role == QLowEnergyController::CentralRole) {
+ const auto manager = centralManager.data();
+ dispatch_sync(leQueue, ^{
+ [manager detach];
+ });
+ } else {
+#ifndef Q_OS_TVOS
+ const auto manager = peripheralManager.data();
+ dispatch_sync(leQueue, ^{
+ [manager detach];
+ });
+#endif
+ }
+ }
}
bool QLowEnergyControllerPrivateOSX::isValid() const
{
- // isValid means only "was able to allocate all resources",
- // nothing more.
+#ifdef Q_OS_TVOS
return centralManager;
+#else
+ return centralManager || peripheralManager;
+#endif
}
-void QLowEnergyControllerPrivateOSX::LEnotSupported()
+void QLowEnergyControllerPrivateOSX::_q_connected()
{
- // Report as an error. But this should not be possible
- // actually: before connecting to any device, we have
- // to discover it, if it was discovered ... LE _must_
- // be supported.
+ controllerState = QLowEnergyController::ConnectedState;
+
+ emit q_ptr->stateChanged(QLowEnergyController::ConnectedState);
+ emit q_ptr->connected();
}
-void QLowEnergyControllerPrivateOSX::connectSuccess()
+void QLowEnergyControllerPrivateOSX::_q_disconnected()
{
- Q_ASSERT_X(controllerState == QLowEnergyController::ConnectingState,
- Q_FUNC_INFO, "invalid state");
+ controllerState = QLowEnergyController::UnconnectedState;
- controllerState = QLowEnergyController::ConnectedState;
+ if (role == QLowEnergyController::CentralRole)
+ invalidateServices();
- if (!isConnecting) {
- emit q_ptr->stateChanged(QLowEnergyController::ConnectedState);
- emit q_ptr->connected();
- }
+ emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState);
+ emit q_ptr->disconnected();
}
-void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices services)
+void QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished()
{
Q_ASSERT_X(controllerState == QLowEnergyController::DiscoveringState,
Q_FUNC_INFO, "invalid state");
@@ -208,6 +232,7 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
QT_BT_MAC_AUTORELEASEPOOL;
+ NSArray *const services = centralManager.data()->peripheral.services;
// Now we have to traverse the discovered services tree.
// Essentially it's an iterative version of more complicated code from the
// OSXBTCentralManager's code.
@@ -218,13 +243,13 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
// during the pass 2); we also ignore duplicates (== services with the same UUID)
// - since we do not have a way to distinguish them later
// (our API is using uuids when creating QLowEnergyServices).
- for (CBService *cbService in services.data()) {
+ for (CBService *cbService in services) {
const ServicePrivate newService(qt_createLEService(this, cbService, false));
if (!newService.data())
continue;
if (discoveredServices.contains(newService->uuid)) {
// It's a bit stupid we first created it ...
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "discovered service with a duplicated UUID "
+ qCDebug(QT_BT_OSX) << "discovered service with a duplicated UUID"
<< newService->uuid;
continue;
}
@@ -276,12 +301,11 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
toVisitNext.resetWithoutRetain([[NSMutableArray alloc] init]);
}
} else {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no services found";
+ qCDebug(QT_BT_OSX) << "no services found";
}
for (ServiceMap::const_iterator it = discoveredServices.constBegin(); it != discoveredServices.constEnd(); ++it) {
const QBluetoothUuid &uuid = it.key();
-
QMetaObject::invokeMethod(q_ptr, "serviceDiscovered", Qt::QueuedConnection,
Q_ARG(QBluetoothUuid, uuid));
}
@@ -292,14 +316,14 @@ void QLowEnergyControllerPrivateOSX::serviceDiscoveryFinished(LEServices service
QMetaObject::invokeMethod(q_ptr, "discoveryFinished", Qt::QueuedConnection);
}
-void QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(LEService service)
+void QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished(QSharedPointer<QLowEnergyServicePrivate> service)
{
- Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
-
QT_BT_MAC_AUTORELEASEPOOL;
+ Q_ASSERT(service);
+
if (!discoveredServices.contains(service->uuid)) {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid: "
+ qCDebug(QT_BT_OSX) << "unknown service uuid:"
<< service->uuid;
return;
}
@@ -313,8 +337,8 @@ void QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(LEService s
qtService->setState(QLowEnergyService::ServiceDiscovered);
}
-void QLowEnergyControllerPrivateOSX::characteristicReadNotification(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_characteristicRead(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)");
@@ -324,7 +348,7 @@ void QLowEnergyControllerPrivateOSX::characteristicReadNotification(QLowEnergyHa
QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle));
if (!characteristic.isValid()) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown characteristic";
+ qCWarning(QT_BT_OSX) << "unknown characteristic";
return;
}
@@ -334,21 +358,21 @@ void QLowEnergyControllerPrivateOSX::characteristicReadNotification(QLowEnergyHa
emit service->characteristicRead(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::characteristicWriteNotification(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_characteristicWritten(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle(0)");
ServicePrivate service(serviceForHandle(charHandle));
if (service.isNull()) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "can not find service for characteristic handle "
+ qCWarning(QT_BT_OSX) << "can not find service for characteristic handle"
<< charHandle;
return;
}
QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle));
if (!characteristic.isValid()) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown characteristic";
+ qCWarning(QT_BT_OSX) << "unknown characteristic";
return;
}
@@ -358,8 +382,8 @@ void QLowEnergyControllerPrivateOSX::characteristicWriteNotification(QLowEnergyH
emit service->characteristicWritten(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::characteristicUpdateNotification(QLowEnergyHandle charHandle,
- const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_characteristicUpdated(QLowEnergyHandle charHandle,
+ const QByteArray &value)
{
// TODO: write/update notifications are quite similar (except asserts/warnings messages
// and different signals emitted). Merge them into one function?
@@ -377,7 +401,7 @@ void QLowEnergyControllerPrivateOSX::characteristicUpdateNotification(QLowEnergy
QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle));
if (!characteristic.isValid()) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown characteristic";
+ qCWarning(QT_BT_OSX) << "unknown characteristic";
return;
}
@@ -387,13 +411,14 @@ void QLowEnergyControllerPrivateOSX::characteristicUpdateNotification(QLowEnergy
emit service->characteristicChanged(characteristic, value);
}
-void QLowEnergyControllerPrivateOSX::descriptorReadNotification(QLowEnergyHandle dHandle, const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_descriptorRead(QLowEnergyHandle dHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)");
const QLowEnergyDescriptor qtDescriptor(descriptorForHandle(dHandle));
if (!qtDescriptor.isValid()) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown descriptor " << dHandle;
+ qCWarning(QT_BT_OSX) << "unknown descriptor" << dHandle;
return;
}
@@ -402,13 +427,14 @@ void QLowEnergyControllerPrivateOSX::descriptorReadNotification(QLowEnergyHandle
emit service->descriptorRead(qtDescriptor, value);
}
-void QLowEnergyControllerPrivateOSX::descriptorWriteNotification(QLowEnergyHandle dHandle, const QByteArray &value)
+void QLowEnergyControllerPrivateOSX::_q_descriptorWritten(QLowEnergyHandle dHandle,
+ const QByteArray &value)
{
Q_ASSERT_X(dHandle, Q_FUNC_INFO, "invalid descriptor handle (0)");
const QLowEnergyDescriptor qtDescriptor(descriptorForHandle(dHandle));
if (!qtDescriptor.isValid()) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown descriptor " << dHandle;
+ qCWarning(QT_BT_OSX) << "unknown descriptor" << dHandle;
return;
}
@@ -418,28 +444,60 @@ void QLowEnergyControllerPrivateOSX::descriptorWriteNotification(QLowEnergyHandl
emit service->descriptorWritten(qtDescriptor, value);
}
-void QLowEnergyControllerPrivateOSX::disconnected()
+void QLowEnergyControllerPrivateOSX::_q_notificationEnabled(QLowEnergyHandle charHandle,
+ bool enabled)
{
- controllerState = QLowEnergyController::UnconnectedState;
+ // CoreBluetooth in peripheral role does not allow mutable descriptors,
+ // in central we can only call setNotification:enabled/disabled.
+ // But from Qt API's point of view, a central has to write into
+ // client characteristic configuration descriptor. So here we emulate
+ // such a write (we cannot say if it's a notification or indication and
+ // report as both).
+
+ Q_ASSERT_X(role == QLowEnergyController::PeripheralRole, Q_FUNC_INFO,
+ "controller has an invalid role, 'peripheral' expected");
+ Q_ASSERT_X(charHandle, Q_FUNC_INFO, "invalid characteristic handle (0)");
+
+ const QLowEnergyCharacteristic qtChar(characteristicForHandle(charHandle));
+ if (!qtChar.isValid()) {
+ qCWarning(QT_BT_OSX) << "unknown characteristic" << charHandle;
+ return;
+ }
+
+ const QLowEnergyDescriptor qtDescriptor =
+ qtChar.descriptor(QBluetoothUuid::ClientCharacteristicConfiguration);
+ if (!qtDescriptor.isValid()) {
+ qCWarning(QT_BT_OSX) << "characteristic" << charHandle
+ << "does not have a client characteristic "
+ "descriptor";
+ return;
+ }
- if (!isConnecting) {
- emit q_ptr->stateChanged(QLowEnergyController::UnconnectedState);
- emit q_ptr->disconnected();
+ ServicePrivate service(serviceForHandle(charHandle));
+ if (service.data()) {
+ // It's a 16-bit value, the least significant bit is for notifications,
+ // the next one - for indications (thus 1 means notifications enabled,
+ // 2 - indications enabled).
+ // 3 is the maximum value and it means both enabled.
+ QByteArray value(2, 0);
+ if (enabled)
+ value[0] = 3;
+ updateValueOfDescriptor(charHandle, qtDescriptor.handle(), value, false);
+ emit service->descriptorWritten(qtDescriptor, value);
}
}
-void QLowEnergyControllerPrivateOSX::error(QLowEnergyController::Error errorCode)
+void QLowEnergyControllerPrivateOSX::_q_LEnotSupported()
{
- // Errors reported during connect and general errors.
+ // Report as an error. But this should not be possible
+ // actually: before connecting to any device, we have
+ // to discover it, if it was discovered ... LE _must_
+ // be supported.
+}
- // We're still in connectToDevice,
- // some error was reported synchronously.
- // Return, the error will be correctly set later
- // by connectToDevice.
- if (isConnecting) {
- lastError = errorCode;
- return;
- }
+void QLowEnergyControllerPrivateOSX::_q_CBManagerError(QLowEnergyController::Error errorCode)
+{
+ // Errors reported during connect and general errors.
setErrorDescription(errorCode);
emit q_ptr->error(lastError);
@@ -454,8 +512,8 @@ void QLowEnergyControllerPrivateOSX::error(QLowEnergyController::Error errorCode
// a service/characteristic - related error.
}
-void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
- QLowEnergyController::Error errorCode)
+void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid,
+ QLowEnergyController::Error errorCode)
{
// Errors reported while discovering service details etc.
Q_UNUSED(errorCode) // TODO: setError?
@@ -465,16 +523,16 @@ void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
ServicePrivate qtService(discoveredServices.value(serviceUuid));
qtService->setState(QLowEnergyService::InvalidService);
} else {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "error reported for unknown service "
+ qCDebug(QT_BT_OSX) << "error reported for unknown service"
<< serviceUuid;
}
}
-void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
- QLowEnergyService::ServiceError errorCode)
+void QLowEnergyControllerPrivateOSX::_q_CBManagerError(const QBluetoothUuid &serviceUuid,
+ QLowEnergyService::ServiceError errorCode)
{
if (!discoveredServices.contains(serviceUuid)) {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "unknown service uuid: "
+ qCDebug(QT_BT_OSX) << "unknown service uuid:"
<< serviceUuid;
return;
}
@@ -490,33 +548,24 @@ void QLowEnergyControllerPrivateOSX::connectToDevice()
Q_FUNC_INFO, "invalid state");
Q_ASSERT_X(!deviceUuid.isNull(), Q_FUNC_INFO,
"invalid private controller (no device uuid)");
- Q_ASSERT_X(!isConnecting, Q_FUNC_INFO,
- "recursive connectToDevice call");
+ Q_ASSERT_X(role != QLowEnergyController::PeripheralRole,
+ Q_FUNC_INFO, "invalid role (peripheral)");
- setErrorDescription(QLowEnergyController::NoError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ setErrorDescription(QLowEnergyController::UnknownError);
+ return;
+ }
- isConnecting = true;// Do not emit signals if some callback is executed synchronously.
+ setErrorDescription(QLowEnergyController::NoError);
controllerState = QLowEnergyController::ConnectingState;
- const QLowEnergyController::Error status = [centralManager connectToDevice:deviceUuid];
- isConnecting = false;
- if (status == QLowEnergyController::NoError && lastError == QLowEnergyController::NoError) {
- emit q_ptr->stateChanged(controllerState);
- if (controllerState == QLowEnergyController::ConnectedState) {
- // If a peripheral is connected already from the Core Bluetooth's
- // POV:
- emit q_ptr->connected();
- } else if (controllerState == QLowEnergyController::UnconnectedState) {
- // Ooops, tried to connect, got peripheral disconnect instead -
- // this happens with Core Bluetooth.
- emit q_ptr->disconnected();
- }
- } else if (status != QLowEnergyController::NoError) {
- error(status);
- } else {
- // Re-set the error/description and emit.
- error(lastError);
- }
+ const QBluetoothUuid deviceUuidCopy(deviceUuid);
+ ObjCCentralManager *manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager connectToDevice:deviceUuidCopy];
+ });
}
void QLowEnergyControllerPrivateOSX::discoverServices()
@@ -524,10 +573,23 @@ void QLowEnergyControllerPrivateOSX::discoverServices()
Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller");
Q_ASSERT_X(controllerState != QLowEnergyController::UnconnectedState,
Q_FUNC_INFO, "not connected to peripheral");
+ Q_ASSERT_X(role != QLowEnergyController::PeripheralRole,
+ Q_FUNC_INFO, "invalid role (peripheral)");
+
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ setErrorDescription(QLowEnergyController::UnknownError);
+ return;
+ }
controllerState = QLowEnergyController::DiscoveringState;
emit q_ptr->stateChanged(QLowEnergyController::DiscoveringState);
- [centralManager discoverServices];
+
+ ObjCCentralManager *manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager discoverServices];
+ });
}
void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid &serviceUuid)
@@ -535,25 +597,32 @@ void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid
Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid private controller");
if (controllerState != QLowEnergyController::DiscoveredState) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO
- << "can not discover service details in the current state, "
- << "QLowEnergyController::DiscoveredState is expected";
+ // This will also exclude peripheral role, since controller
+ // can never be in discovered state ...
+ qCWarning(QT_BT_OSX) << "can not discover service details in the current state, "
+ "QLowEnergyController::DiscoveredState is expected";
return;
}
if (!discoveredServices.contains(serviceUuid)) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "unknown service: " << serviceUuid;
+ qCWarning(QT_BT_OSX) << "unknown service: " << serviceUuid;
return;
}
- ServicePrivate qtService(discoveredServices.value(serviceUuid));
- if ([centralManager discoverServiceDetails:serviceUuid]) {
- qtService->setState(QLowEnergyService::DiscoveringServices);
- } else {
- // The error is returned by CentralManager - no
- // service with a given UUID found on a peripheral.
- qtService->setState(QLowEnergyService::InvalidService);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ return;
}
+
+ ServicePrivate qtService(discoveredServices.value(serviceUuid));
+ qtService->setState(QLowEnergyService::DiscoveringServices);
+ // Copy objects ...
+ ObjCCentralManager *manager = centralManager.data();
+ const QBluetoothUuid serviceUuidCopy(serviceUuid);
+ dispatch_async(leQueue, ^{
+ [manager discoverServiceDetails:serviceUuidCopy];
+ });
}
void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergyServicePrivate> service,
@@ -563,31 +632,47 @@ void QLowEnergyControllerPrivateOSX::setNotifyValue(QSharedPointer<QLowEnergySer
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
+ if (role == QLowEnergyController::PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
+ service->setError(QLowEnergyService::DescriptorWriteError);
+ return;
+ }
+
if (newValue.size() > 2) {
// Qt's API requires an error on such write.
// With Core Bluetooth we do not write any descriptor,
// but instead call a special method. So it's better to
// intercept wrong data size here:
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "client characteristic configuration descriptor "
+ qCWarning(QT_BT_OSX) << "client characteristic configuration descriptor"
"is 2 bytes, but value size is: " << newValue.size();
service->setError(QLowEnergyService::DescriptorWriteError);
return;
}
if (!discoveredServices.contains(service->uuid)) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid: "
- << service->uuid << " found";
+ qCWarning(QT_BT_OSX) << "no service with uuid:" << service->uuid << "found";
return;
}
if (!service->characteristicList.contains(charHandle)) {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no characteristic with handle: "
- << charHandle << " found";
+ qCDebug(QT_BT_OSX) << "no characteristic with handle:"
+ << charHandle << "found";
return;
}
- if (![centralManager setNotifyValue:newValue forCharacteristic:charHandle])
- service->setError(QLowEnergyService::DescriptorWriteError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ return;
+ }
+ ObjCCentralManager *manager = centralManager.data();
+ const QBluetoothUuid serviceUuid(service->uuid);
+ const QByteArray newValueCopy(newValue);
+ dispatch_async(leQueue, ^{
+ [manager setNotifyValue:newValueCopy
+ forCharacteristic:charHandle
+ onService:serviceUuid];
+ });
}
void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
@@ -596,49 +681,84 @@ void QLowEnergyControllerPrivateOSX::readCharacteristic(QSharedPointer<QLowEnerg
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
+ if (role == QLowEnergyController::PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
+ return;
+ }
+
if (!discoveredServices.contains(service->uuid)) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid:"
+ qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << "found";
return;
}
if (!service->characteristicList.contains(charHandle)) {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no characteristic with handle:"
+ qCDebug(QT_BT_OSX) << "no characteristic with handle:"
<< charHandle << "found";
return;
}
- if (![centralManager readCharacteristic:charHandle])
- service->setError(QLowEnergyService::CharacteristicReadError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ return;
+ }
+ // Attention! We have to copy UUID.
+ ObjCCentralManager *manager = centralManager.data();
+ const QBluetoothUuid serviceUuid(service->uuid);
+ dispatch_async(leQueue, ^{
+ [manager readCharacteristic:charHandle onService:serviceUuid];
+ });
}
void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
QLowEnergyHandle charHandle, const QByteArray &newValue,
- bool writeWithResponse)
+ QLowEnergyService::WriteMode mode)
{
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
- // We can work only with services, found on a given peripheral
- // (== created by the given LE controller),
- // otherwise we can not write anything at all.
+ // We can work only with services found on a given peripheral
+ // (== created by the given LE controller).
+
if (!discoveredServices.contains(service->uuid)) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid: "
+ qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << " found";
return;
}
if (!service->characteristicList.contains(charHandle)) {
- qCDebug(QT_BT_OSX) << Q_FUNC_INFO << "no characteristic with handle: "
+ qCDebug(QT_BT_OSX) << "no characteristic with handle:"
<< charHandle << " found";
return;
}
- const bool result = [centralManager write:newValue
- charHandle:charHandle
- withResponse:writeWithResponse];
- if (!result)
- service->setError(QLowEnergyService::CharacteristicWriteError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ return;
+ }
+ // Attention! We have to copy objects!
+ const QByteArray newValueCopy(newValue);
+ if (role == QLowEnergyController::CentralRole) {
+ const QBluetoothUuid serviceUuid(service->uuid);
+ const auto manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager write:newValueCopy
+ charHandle:charHandle
+ onService:serviceUuid
+ withResponse:mode == QLowEnergyService::WriteWithResponse];
+ });
+ } else {
+#ifndef Q_OS_TVOS
+ const auto manager = peripheralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager write:newValueCopy charHandle:charHandle];
+ });
+#else
+ qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform";
+#endif
+ }
}
quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle,
@@ -650,7 +770,6 @@ quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHa
CharacteristicDataMap::iterator charIt = service->characteristicList.find(charHandle);
if (charIt != service->characteristicList.end()) {
QLowEnergyServicePrivate::CharData &charData = charIt.value();
-
if (appendValue)
charData.value += value;
else
@@ -669,14 +788,29 @@ void QLowEnergyControllerPrivateOSX::readDescriptor(QSharedPointer<QLowEnergySer
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
+ if (role == QLowEnergyController::PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
+ return;
+ }
+
if (!discoveredServices.contains(service->uuid)) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid:"
+ qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << "found";
return;
}
- if (![centralManager readDescriptor:descriptorHandle])
- service->setError(QLowEnergyService::DescriptorReadError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ return;
+ }
+ // Attention! Copy objects!
+ const QBluetoothUuid serviceUuid(service->uuid);
+ ObjCCentralManager * const manager = centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager readDescriptor:descriptorHandle
+ onService:serviceUuid];
+ });
}
void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
@@ -686,17 +820,34 @@ void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergySe
Q_ASSERT_X(!service.isNull(), Q_FUNC_INFO, "invalid service (null)");
Q_ASSERT_X(isValid(), Q_FUNC_INFO, "invalid controller");
+ if (role == QLowEnergyController::PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
+ return;
+ }
+
// We can work only with services found on a given peripheral
// (== created by the given LE controller),
// otherwise we can not write anything at all.
if (!discoveredServices.contains(service->uuid)) {
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "no service with uuid: "
+ qCWarning(QT_BT_OSX) << "no service with uuid:"
<< service->uuid << " found";
return;
}
- if (![centralManager write:newValue descHandle:descriptorHandle])
- service->setError(QLowEnergyService::DescriptorWriteError);
+ dispatch_queue_t leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ return;
+ }
+ // Attention! Copy objects!
+ const QBluetoothUuid serviceUuid(service->uuid);
+ ObjCCentralManager * const manager = centralManager.data();
+ const QByteArray newValueCopy(newValue);
+ dispatch_async(leQueue, ^{
+ [manager write:newValueCopy
+ descHandle:descriptorHandle
+ onService:serviceUuid];
+ });
}
quint16 QLowEnergyControllerPrivateOSX::updateValueOfDescriptor(QLowEnergyHandle charHandle, QLowEnergyHandle descHandle,
@@ -787,17 +938,23 @@ void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::E
errorString.clear();
break;
case QLowEnergyController::UnknownRemoteDeviceError:
- errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_RDEV_NO_FOUND);
+ errorString = QLowEnergyController::tr("Remote device cannot be found");
break;
case QLowEnergyController::InvalidBluetoothAdapterError:
- errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_NO_LOCAL_DEV);
+ errorString = QLowEnergyController::tr("Cannot find local adapter");
break;
case QLowEnergyController::NetworkError:
- errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_IO_ERROR);
+ errorString = QLowEnergyController::tr("Error occurred during connection I/O");
+ break;
+ case QLowEnergyController::ConnectionError:
+ errorString = QLowEnergyController::tr("Error occurred trying to connect to remote device.");
+ break;
+ case QLowEnergyController::AdvertisingError:
+ errorString = QLowEnergyController::tr("Error occurred trying to start advertising");
break;
case QLowEnergyController::UnknownError:
default:
- errorString = QCoreApplication::translate(LE_CONTROLLER, LEC_UNKNOWN_ERROR);
+ errorString = QLowEnergyController::tr("Unknown Error");
break;
}
}
@@ -812,24 +969,65 @@ void QLowEnergyControllerPrivateOSX::invalidateServices()
discoveredServices.clear();
}
+bool QLowEnergyControllerPrivateOSX::connectSlots(OSXBluetooth::LECBManagerNotifier *notifier)
+{
+ using OSXBluetooth::LECBManagerNotifier;
+
+ Q_ASSERT_X(notifier, Q_FUNC_INFO, "invalid notifier object (null)");
+
+ bool ok = connect(notifier, &LECBManagerNotifier::connected,
+ this, &QLowEnergyControllerPrivateOSX::_q_connected);
+ ok = ok && connect(notifier, &LECBManagerNotifier::disconnected,
+ this, &QLowEnergyControllerPrivateOSX::_q_disconnected);
+ ok = ok && connect(notifier, &LECBManagerNotifier::serviceDiscoveryFinished,
+ this, &QLowEnergyControllerPrivateOSX::_q_serviceDiscoveryFinished);
+ ok = ok && connect(notifier, &LECBManagerNotifier::serviceDetailsDiscoveryFinished,
+ this, &QLowEnergyControllerPrivateOSX::_q_serviceDetailsDiscoveryFinished);
+ ok = ok && connect(notifier, &LECBManagerNotifier::characteristicRead,
+ this, &QLowEnergyControllerPrivateOSX::_q_characteristicRead);
+ ok = ok && connect(notifier, &LECBManagerNotifier::characteristicWritten,
+ this, &QLowEnergyControllerPrivateOSX::_q_characteristicWritten);
+ ok = ok && connect(notifier, &LECBManagerNotifier::characteristicUpdated,
+ this, &QLowEnergyControllerPrivateOSX::_q_characteristicUpdated);
+ ok = ok && connect(notifier, &LECBManagerNotifier::descriptorRead,
+ this, &QLowEnergyControllerPrivateOSX::_q_descriptorRead);
+ ok = ok && connect(notifier, &LECBManagerNotifier::descriptorWritten,
+ this, &QLowEnergyControllerPrivateOSX::_q_descriptorWritten);
+ ok = ok && connect(notifier, &LECBManagerNotifier::notificationEnabled,
+ this, &QLowEnergyControllerPrivateOSX::_q_notificationEnabled);
+ ok = ok && connect(notifier, &LECBManagerNotifier::LEnotSupported,
+ this, &QLowEnergyControllerPrivateOSX::_q_LEnotSupported);
+ ok = ok && connect(notifier, SIGNAL(CBManagerError(QLowEnergyController::Error)),
+ this, SLOT(_q_CBManagerError(QLowEnergyController::Error)));
+ ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error)),
+ this, SLOT(_q_CBManagerError(const QBluetoothUuid &, QLowEnergyController::Error)));
+ ok = ok && connect(notifier, SIGNAL(CBManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError)),
+ this, SLOT(_q_CBManagerError(const QBluetoothUuid &, QLowEnergyService::ServiceError)));
+
+ if (!ok)
+ notifier->disconnect();
+
+ return ok;
+}
+
QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddress,
QObject *parent)
: QObject(parent),
- d_ptr(new QLowEnergyControllerPrivateOSX(this))
+ d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this))
{
OSX_D_PTR;
osx_d_ptr->remoteAddress = remoteAddress;
osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "construction with remote address "
+ qCWarning(QT_BT_OSX) << "construction with remote address "
"is not supported!";
}
QLowEnergyController::QLowEnergyController(const QBluetoothDeviceInfo &remoteDevice,
QObject *parent)
: QObject(parent),
- d_ptr(new QLowEnergyControllerPrivateOSX(this, remoteDevice))
+ d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this, remoteDevice))
{
OSX_D_PTR;
@@ -842,23 +1040,50 @@ QLowEnergyController::QLowEnergyController(const QBluetoothAddress &remoteAddres
const QBluetoothAddress &localAddress,
QObject *parent)
: QObject(parent),
- d_ptr(new QLowEnergyControllerPrivateOSX(this))
+ d_ptr(new QLowEnergyControllerPrivateOSX(CentralRole, this))
{
OSX_D_PTR;
osx_d_ptr->remoteAddress = remoteAddress;
osx_d_ptr->localAddress = localAddress;
- qCWarning(QT_BT_OSX) << Q_FUNC_INFO << "construction with remote/local "
+ qCWarning(QT_BT_OSX) << "construction with remote/local "
"addresses is not supported!";
}
+QLowEnergyController::QLowEnergyController(QObject *parent)
+ : QObject(parent),
+ d_ptr(new QLowEnergyControllerPrivateOSX(PeripheralRole, this))
+{
+ OSX_D_PTR;
+
+ osx_d_ptr->localAddress = QBluetoothLocalDevice().address();
+}
+
+QLowEnergyController *QLowEnergyController::createCentral(const QBluetoothDeviceInfo &remoteDevice,
+ QObject *parent)
+{
+ return new QLowEnergyController(remoteDevice, parent);
+}
+
+QLowEnergyController *QLowEnergyController::createPeripheral(QObject *parent)
+{
+ return new QLowEnergyController(parent);
+}
+
QLowEnergyController::~QLowEnergyController()
{
// Deleting a peripheral will also disconnect.
delete d_ptr;
}
+QLowEnergyController::Role QLowEnergyController::role() const
+{
+ OSX_D_PTR;
+
+ return osx_d_ptr->role;
+}
+
QBluetoothAddress QLowEnergyController::localAddress() const
{
OSX_D_PTR;
@@ -873,6 +1098,13 @@ QBluetoothAddress QLowEnergyController::remoteAddress() const
return osx_d_ptr->remoteAddress;
}
+QBluetoothUuid QLowEnergyController::remoteDeviceUuid() const
+{
+ OSX_D_PTR;
+
+ return osx_d_ptr->deviceUuid;
+}
+
QString QLowEnergyController::remoteName() const
{
OSX_D_PTR;
@@ -909,11 +1141,16 @@ void QLowEnergyController::connectToDevice()
// A memory allocation problem.
if (!osx_d_ptr->isValid())
- return osx_d_ptr->error(UnknownError);
+ return osx_d_ptr->_q_CBManagerError(UnknownError);
+
+ if (role() == PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "can not connect in peripheral role";
+ return osx_d_ptr->_q_CBManagerError(ConnectionError);
+ }
// No QBluetoothDeviceInfo provided during construction.
if (osx_d_ptr->deviceUuid.isNull())
- return osx_d_ptr->error(UnknownRemoteDeviceError);
+ return osx_d_ptr->_q_CBManagerError(UnknownRemoteDeviceError);
if (osx_d_ptr->controllerState != UnconnectedState)
return;
@@ -928,27 +1165,49 @@ void QLowEnergyController::disconnectFromDevice()
OSX_D_PTR;
+ if (role() == PeripheralRole) {
+ // CoreBluetooth API intentionally does not provide any way of closing
+ // a connection. All we can do here is to stop the advertisement.
+ stopAdvertising();
+ return;
+ }
+
if (osx_d_ptr->isValid()) {
const ControllerState oldState = osx_d_ptr->controllerState;
- osx_d_ptr->controllerState = ClosingState;
- emit stateChanged(ClosingState);
- osx_d_ptr->invalidateServices();
- [osx_d_ptr->centralManager disconnectFromDevice];
-
- if (oldState == ConnectingState) {
- // With a pending connect attempt there is no
- // guarantee we'll ever have didDisconnect callback,
- // set the state here and now to make sure we still
- // can connect.
- osx_d_ptr->controllerState = UnconnectedState;
- emit stateChanged(UnconnectedState);
+ if (dispatch_queue_t leQueue = OSXBluetooth::qt_LE_queue()) {
+ osx_d_ptr->controllerState = ClosingState;
+ emit stateChanged(ClosingState);
+ osx_d_ptr->invalidateServices();
+
+ QT_MANGLE_NAMESPACE(OSXBTCentralManager) *manager
+ = osx_d_ptr->centralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager disconnectFromDevice];
+ });
+
+ if (oldState == ConnectingState) {
+ // With a pending connect attempt there is no
+ // guarantee we'll ever have didDisconnect callback,
+ // set the state here and now to make sure we still
+ // can connect.
+ osx_d_ptr->controllerState = UnconnectedState;
+ emit stateChanged(UnconnectedState);
+ }
+ } else {
+ qCCritical(QT_BT_OSX) << "qt LE queue is nil, "
+ "can not dispatch 'disconnect'";
}
}
}
void QLowEnergyController::discoverServices()
{
+ if (role() == PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "invalid role (peripheral)";
+ return;
+ }
+
if (state() != ConnectedState)
return;
@@ -995,4 +1254,132 @@ QString QLowEnergyController::errorString() const
return osx_d_ptr->errorString;
}
+void QLowEnergyController::startAdvertising(const QLowEnergyAdvertisingParameters &params,
+ const QLowEnergyAdvertisingData &advertisingData,
+ const QLowEnergyAdvertisingData &scanResponseData)
+{
+#ifdef Q_OS_TVOS
+ Q_UNUSED(params)
+ Q_UNUSED(advertisingData)
+ Q_UNUSED(scanResponseData)
+ qCWarning(QT_BT_OSX) << "advertising is not supported on your platform";
+#else
+ OSX_D_PTR;
+
+ if (!osx_d_ptr->isValid())
+ return osx_d_ptr->_q_CBManagerError(UnknownError);
+
+ if (role() != PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "invalid role";
+ return;
+ }
+
+ if (state() != UnconnectedState) {
+ qCWarning(QT_BT_OSX) << "invalid state" << state();
+ return;
+ }
+
+ auto leQueue(OSXBluetooth::qt_LE_queue());
+ if (!leQueue) {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ osx_d_ptr->setErrorDescription(QLowEnergyController::UnknownError);
+ return;
+ }
+
+ [osx_d_ptr->peripheralManager setParameters:params
+ data:advertisingData
+ scanResponse:scanResponseData];
+
+ osx_d_ptr->controllerState = AdvertisingState;
+ emit stateChanged(AdvertisingState);
+
+ const auto manager = osx_d_ptr->peripheralManager.data();
+ dispatch_async(leQueue, ^{
+ [manager startAdvertising];
+ });
+#endif
+}
+
+void QLowEnergyController::stopAdvertising()
+{
+#ifdef Q_OS_TVOS
+ qCWarning(QT_BT_OSX) << "advertising is not supported on your platform";
+#else
+ OSX_D_PTR;
+
+ if (!osx_d_ptr->isValid())
+ return osx_d_ptr->_q_CBManagerError(UnknownError);
+
+ if (state() != AdvertisingState) {
+ qCDebug(QT_BT_OSX) << "cannot stop advertising, called in state" << state();
+ return;
+ }
+
+ if (const auto leQueue = OSXBluetooth::qt_LE_queue()) {
+ const auto manager = osx_d_ptr->peripheralManager.data();
+ dispatch_sync(leQueue, ^{
+ [manager stopAdvertising];
+ });
+
+ osx_d_ptr->controllerState = UnconnectedState;
+ emit stateChanged(UnconnectedState);
+ } else {
+ qCWarning(QT_BT_OSX) << "no LE queue found";
+ osx_d_ptr->setErrorDescription(QLowEnergyController::UnknownError);
+ return;
+ }
+#endif
+}
+
+QLowEnergyService *QLowEnergyController::addService(const QLowEnergyServiceData &data,
+ QObject *parent)
+{
+#ifdef Q_OS_TVOS
+ Q_UNUSED(data)
+ Q_UNUSED(parent)
+ qCWarning(QT_BT_OSX) << "peripheral role is not supported on your platform";
+#else
+ OSX_D_PTR;
+
+ if (!osx_d_ptr->isValid()) {
+ osx_d_ptr->_q_CBManagerError(UnknownError);
+ return nullptr;
+ }
+
+ if (role() != PeripheralRole) {
+ qCWarning(QT_BT_OSX) << "not in peripheral role";
+ return nullptr;
+ }
+
+ if (state() != UnconnectedState) {
+ qCWarning(QT_BT_OSX) << "invalid state";
+ return nullptr;
+ }
+
+ if (!data.isValid()) {
+ qCWarning(QT_BT_OSX) << "invalid service";
+ return nullptr;
+ }
+
+ for (auto includedService : data.includedServices())
+ includedService->d_ptr->type |= QLowEnergyService::IncludedService;
+
+ if (const auto servicePrivate = [osx_d_ptr->peripheralManager addService:data]) {
+ servicePrivate->setController(osx_d_ptr);
+ osx_d_ptr->discoveredServices.insert(servicePrivate->uuid, servicePrivate);
+ return new QLowEnergyService(servicePrivate, parent);
+ }
+#endif
+
+ return nullptr;
+}
+
+void QLowEnergyController::requestConnectionUpdate(const QLowEnergyConnectionParameters &params)
+{
+ Q_UNUSED(params);
+ qCWarning(QT_BT_OSX) << "Connection update not implemented on your platform";
+}
+
QT_END_NAMESPACE
+
+#include "moc_qlowenergycontroller_osx_p.cpp"