summaryrefslogtreecommitdiffstats
path: root/src/bluetooth
diff options
context:
space:
mode:
authorTimur Pocheptsov <Timur.Pocheptsov@digia.com>2014-11-23 18:02:09 +0100
committerTimur Pocheptsov <Timur.Pocheptsov@digia.com>2014-11-26 13:00:34 +0100
commitbe750788c7f5276b0a8686e1f11bf4ca12ee9231 (patch)
tree17d95e86ba063fad3193f5291fe0fd9db0bfb9f3 /src/bluetooth
parent9966c69e301a041ad224ef08bc3da45ca249041d (diff)
QLowEnergyService::writeCharacteristic - implementation for OS X and iOS
writeCharacteristic - implemented with Core Bluetooth for OS X and iOS. Change-Id: Ia228ff451e1e6d7b6fb7de6cad29198aa9257602 Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
Diffstat (limited to 'src/bluetooth')
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager.mm114
-rw-r--r--src/bluetooth/osx/osxbtcentralmanager_p.h15
-rw-r--r--src/bluetooth/osx/osxbtutility.mm9
-rw-r--r--src/bluetooth/osx/osxbtutility_p.h1
-rw-r--r--src/bluetooth/qlowenergycharacteristic.h1
-rw-r--r--src/bluetooth/qlowenergycontroller_osx.mm186
-rw-r--r--src/bluetooth/qlowenergycontroller_osx_p.h24
-rw-r--r--src/bluetooth/qlowenergyservice_osx.mm133
8 files changed, 468 insertions, 15 deletions
diff --git a/src/bluetooth/osx/osxbtcentralmanager.mm b/src/bluetooth/osx/osxbtcentralmanager.mm
index 71622b1c..025313d0 100644
--- a/src/bluetooth/osx/osxbtcentralmanager.mm
+++ b/src/bluetooth/osx/osxbtcentralmanager.mm
@@ -78,6 +78,8 @@ using namespace QT_NAMESPACE;
- (CBCharacteristic *)nextCharacteristicForService:(CBService*)service
startingFrom:(CBCharacteristic *)from
withProperties:(CBCharacteristicProperties)properties;
+- (CBCharacteristic *)characteristicForService:(CBService *)service
+ withIndex:(NSUInteger)index;
- (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic
startingFrom:(CBDescriptor *)descriptor;
@@ -437,6 +439,60 @@ using namespace QT_NAMESPACE;
delegate->serviceDetailsDiscoveryFinished(ObjCStrongReference<CBService>(service, true));
}
+- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
+ characteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+ serviceUuid:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid
+ serviceHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))serviceHandle
+ withResponse:(bool)withResponse
+{
+ using namespace OSXBluetooth;
+
+ Q_ASSERT_X(!serviceUuid.isNull(), "-write:characteristic:serviceUuid:serviceHandle:withResponse:",
+ "invalid service uuid");
+ Q_ASSERT_X(serviceHandle, "-write:characteristic:serviceUuid:serviceHandle:withResponse:",
+ "invalid service handle (0)");
+ Q_ASSERT_X(serviceHandle < charHandle, "-write:characteristic:serviceUuid:serviceHandle:withResponse:",
+ "invalid characteristic handle (<= serviceHandle)");
+
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ CBService *const service = [self serviceForUUID:serviceUuid];
+ if (!service) {
+ qCWarning(QT_BT_OSX) << "-write:characteristic:serviceUuid:serviceHandle:withResponse:, "
+ "service with uuid: " << serviceUuid << " not found";
+ return false;
+ }
+
+ // 'Convert' charHandle into the 'index' and find a characteristic.
+ CBCharacteristic *const ch = [self characteristicForService:service
+ withIndex:charHandle - serviceHandle - 1];
+
+ if (!ch) {
+ qCWarning(QT_BT_OSX) << "-write:characteristic:serviceUuid:serviceHandle:withResponse:, "
+ "characteristic with handle: " << charHandle << " not "
+ "found on service: " << serviceUuid;
+ return false;
+ }
+
+ Q_ASSERT_X(peripheral, "-write:characteristic:serviceUuid:serviceHandle:withResponse:",
+ "invalid peripheral (nil)");
+
+ ObjCStrongReference<NSData> data(data_from_bytearray(value));
+ if (!data) {
+ // Even if qtData.size() == 0, we still need NSData object.
+ qCWarning(QT_BT_OSX) << "-write:characteristic:serviceUuid:serviceHandle:withResponse:, "
+ "failed to allocate NSData object";
+ return false;
+ }
+
+ // TODO: check what happens if I'm using NSData with length 0.
+ [peripheral writeValue:data.data() forCharacteristic:ch
+ type: withResponse? CBCharacteristicWriteWithResponse : CBCharacteristicWriteWithoutResponse];
+
+ return true;
+}
+
// Aux. methods:
- (CBService *)serviceForUUID:(const QBluetoothUuid &)qtUuid
@@ -560,6 +616,30 @@ using namespace QT_NAMESPACE;
return nil;
}
+- (CBCharacteristic *)characteristicForService:(CBService *)service
+ withIndex:(NSUInteger)index
+{
+ Q_ASSERT_X(service, "-characteristicForService:withIndex:",
+ "invalid service (nil)");
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ NSArray *const chars = service.characteristics;
+
+ if (!chars || !chars.count)
+ return nil;
+
+ for (NSUInteger i = 0, j = 0, e = chars.count; i < e; ++i) {
+ CBCharacteristic *const ch = [chars objectAtIndex:i];
+ if (j == index)
+ return ch;
+ if (ch.descriptors)
+ j += ch.descriptors.count + 1; // + 1 for characteristic itself.
+ }
+
+ return nil;
+}
+
- (CBDescriptor *)nextDescriptorForCharacteristic:(CBCharacteristic *)characteristic
startingFrom:(CBDescriptor *)descriptor
{
@@ -1003,4 +1083,38 @@ using namespace QT_NAMESPACE;
}
}
+- (void)peripheral:(CBPeripheral *)aPeripheral
+ didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
+ error:(NSError *)error
+{
+ // From docs:
+ //
+ // "This method is invoked only when your app calls the writeValue:forCharacteristic:type:
+ // method with the CBCharacteristicWriteWithResponse constant specified as the write type.
+ // If successful, the error parameter is nil. If unsuccessful,
+ // the error parameter returns the cause of the failure."
+
+ using namespace OSXBluetooth;
+
+ Q_UNUSED(aPeripheral)
+ Q_UNUSED(characteristic)
+ Q_UNUSED(error)
+
+ Q_ASSERT_X(delegate, "-peripheral:didWriteValueForCharacteristic:error",
+ "invalid delegate (null)");
+
+ if (error) {
+ // Use NSLog to log the actual error:
+ NSLog(@"-peripheral:didWriteValueForCharacteristic:error:, failed with error: %@",
+ error);
+ // TODO: no char handle at the moment, have to change to char index instead
+ // and calculate the right handle in the LE controller.
+ delegate->error(qt_uuid(characteristic.service.UUID), 0,
+ QLowEnergyService::CharacteristicWriteError);
+ } else {
+ ObjCStrongReference<CBCharacteristic> ch(characteristic, true);
+ delegate->characteristicWriteNotification(ch);
+ }
+}
+
@end
diff --git a/src/bluetooth/osx/osxbtcentralmanager_p.h b/src/bluetooth/osx/osxbtcentralmanager_p.h
index 25faab78..1e3028d6 100644
--- a/src/bluetooth/osx/osxbtcentralmanager_p.h
+++ b/src/bluetooth/osx/osxbtcentralmanager_p.h
@@ -43,6 +43,7 @@
#define OSXBTCENTRALMANAGER_P_H
#include "qlowenergycontroller.h"
+#include "qlowenergyservice.h"
#include "qbluetoothuuid.h"
#include "osxbtutility_p.h"
@@ -66,6 +67,7 @@ public:
typedef QT_MANGLE_NAMESPACE(OSXBTCentralManager) ObjCCentralManager;
typedef ObjCStrongReference<NSArray> LEServices;
typedef ObjCStrongReference<CBService> LEService;
+ typedef ObjCStrongReference<CBCharacteristic> LECharacteristic;
virtual ~CentralManagerDelegate();
@@ -73,6 +75,7 @@ public:
virtual void connectSuccess() = 0;
virtual void serviceDiscoveryFinished(LEServices services) = 0;
virtual void serviceDetailsDiscoveryFinished(LEService service) = 0;
+ virtual void characteristicWriteNotification(LECharacteristic ch) = 0;
virtual void disconnected() = 0;
// General errors.
@@ -80,6 +83,10 @@ public:
// Service related errors.
virtual void error(const QBluetoothUuid &serviceUuid,
QLowEnergyController::Error error) = 0;
+ // Characteristics related errors.
+ virtual void error(const QBluetoothUuid &serviceUuid,
+ QLowEnergyHandle charHandle,
+ QLowEnergyService::ServiceError error) = 0;
};
enum CentralManagerState
@@ -133,6 +140,14 @@ QT_END_NAMESPACE
- (void)discoverServices;
- (bool)discoverServiceDetails:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid;
+// Characteristic's handle here is a 'relative' == valueHandle - service->startHandle
+// to simplify mapping between Qt's handles and Core Bluetooth's data structures.
+- (bool)write:(const QT_PREPEND_NAMESPACE(QByteArray) &)value
+ characteristic:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))charHandle
+ serviceUuid:(const QT_PREPEND_NAMESPACE(QBluetoothUuid) &)serviceUuid
+ serviceHandle:(QT_PREPEND_NAMESPACE(QLowEnergyHandle))serviceHandle
+ withResponse:(bool)writeWithResponse;
+
@end
#endif
diff --git a/src/bluetooth/osx/osxbtutility.mm b/src/bluetooth/osx/osxbtutility.mm
index 1b07e2cb..26f33e6e 100644
--- a/src/bluetooth/osx/osxbtutility.mm
+++ b/src/bluetooth/osx/osxbtutility.mm
@@ -246,6 +246,15 @@ QByteArray qt_bytearray(NSData *data)
return value;
}
+ObjCStrongReference<NSData> data_from_bytearray(const QByteArray & qtData)
+{
+ if (!qtData.size())
+ return ObjCStrongReference<NSData>([[NSData alloc] init], false);
+
+ ObjCStrongReference<NSData> result([NSData dataWithBytes:qtData.constData() length:qtData.size()], true);
+ return result;
+}
+
}
QT_END_NAMESPACE
diff --git a/src/bluetooth/osx/osxbtutility_p.h b/src/bluetooth/osx/osxbtutility_p.h
index 6e226a25..d30ee93c 100644
--- a/src/bluetooth/osx/osxbtutility_p.h
+++ b/src/bluetooth/osx/osxbtutility_p.h
@@ -288,6 +288,7 @@ ObjCStrongReference<CBUUID> cb_uuid(const QBluetoothUuid &qtUuid);
bool equal_uuids(const QBluetoothUuid &qtUuid, CBUUID *cbUuid);
bool equal_uuids(CBUUID *cbUuid, const QBluetoothUuid &qtUuid);
QByteArray qt_bytearray(NSData *data);
+ObjCStrongReference<NSData> data_from_bytearray(const QByteArray & qtData);
} // namespace OSXBluetooth
diff --git a/src/bluetooth/qlowenergycharacteristic.h b/src/bluetooth/qlowenergycharacteristic.h
index e0ed2976..256aea68 100644
--- a/src/bluetooth/qlowenergycharacteristic.h
+++ b/src/bluetooth/qlowenergycharacteristic.h
@@ -91,6 +91,7 @@ protected:
friend class QLowEnergyService;
friend class QLowEnergyControllerPrivate;
+ friend class QLowEnergyControllerPrivateOSX;
QLowEnergyCharacteristicPrivate *data;
QLowEnergyCharacteristic(QSharedPointer<QLowEnergyServicePrivate> p,
QLowEnergyHandle handle);
diff --git a/src/bluetooth/qlowenergycontroller_osx.mm b/src/bluetooth/qlowenergycontroller_osx.mm
index 9a86c392..f41423d8 100644
--- a/src/bluetooth/qlowenergycontroller_osx.mm
+++ b/src/bluetooth/qlowenergycontroller_osx.mm
@@ -122,6 +122,36 @@ UUIDList qt_servicesUuids(NSArray *services)
return uuids;
}
+QLowEnergyHandle qt_findCharacteristicHandle(QLowEnergyHandle serviceHandle,
+ CBService *service, CBCharacteristic *ch)
+{
+ // This mapping from CB -> Qt Qt -> CB is quite verbose and annoying,
+ // but duplicating data structures (CB char-tree, Qt char-tree, etc.)
+ // is even more annoying.
+
+ Q_ASSERT_X(serviceHandle, "qt_findCharacteristicHandle",
+ "invalid service handle (0)");
+ Q_ASSERT_X(service, "qt_findCharacteristicHandle",
+ "invalid service (nil)");
+ Q_ASSERT_X(ch, "qt_findCharacteristicHandle",
+ "invalid characteristic (nil)");
+
+ NSArray *const chars = service.characteristics;
+ if (!chars || !chars.count)
+ return 0; // Invalid handle, to be .. handled by the caller.
+
+ QLowEnergyHandle handle = serviceHandle + 1;
+ for (CBCharacteristic *candidate in chars) {
+ if (candidate == ch)
+ return handle;
+ NSArray *const ds = candidate.descriptors;
+ if (ds && ds.count)
+ handle += ds.count + 1; // + 1 is for char itself.
+ }
+
+ return 0;
+}
+
}
QLowEnergyControllerPrivateOSX::QLowEnergyControllerPrivateOSX(QLowEnergyController *q)
@@ -345,6 +375,46 @@ void QLowEnergyControllerPrivateOSX::serviceDetailsDiscoveryFinished(LEService s
qtService->stateChanged(QLowEnergyService::ServiceDiscovered);
}
+void QLowEnergyControllerPrivateOSX::characteristicWriteNotification(LECharacteristic ch)
+{
+ Q_ASSERT_X(ch, "characteristicWriteNotification", "invalid characteristic (nil)");
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ CBService *const cbService = [ch service];
+ const QBluetoothUuid serviceUuid(OSXBluetooth::qt_uuid(cbService.UUID));
+ if (!discoveredServices.contains(serviceUuid)) {
+ qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::characteristicWriteNotification(), "
+ "unknown service uuid: " << serviceUuid;
+ return;
+ }
+
+ ServicePrivate service(discoveredServices.value(serviceUuid));
+ Q_ASSERT_X(service->startHandle, "characteristicWriteNotification",
+ "invalid service handle (0)");
+
+ const QLowEnergyHandle charHandle =
+ qt_findCharacteristicHandle(service->startHandle, cbService, ch);
+
+ if (!charHandle) {
+ qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::characteristicWriteNotification(), "
+ "unknown characteristic";
+ return;
+ }
+
+ QLowEnergyCharacteristic characteristic(characteristicForHandle(charHandle));
+ if (!characteristic.isValid()) {
+ qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::characteristicWriteNotification(), "
+ "unknown characteristic";
+ return;
+ }
+
+ // TODO: check that this 'value' is what we need!
+ const QByteArray data(OSXBluetooth::qt_bytearray([ch value]));
+ updateValueOfCharacteristic(charHandle, data, false);
+ emit service->characteristicWritten(characteristic, data);
+}
+
void QLowEnergyControllerPrivateOSX::disconnected()
{
controllerState = QLowEnergyController::UnconnectedState;
@@ -397,6 +467,22 @@ void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
}
}
+void QLowEnergyControllerPrivateOSX::error(const QBluetoothUuid &serviceUuid,
+ QLowEnergyHandle charHandle,
+ QLowEnergyService::ServiceError errorCode)
+{
+ Q_UNUSED(charHandle)
+
+ if (!discoveredServices.contains(serviceUuid)) {
+ qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::error(), "
+ "unknown service uuid: " << serviceUuid;
+ return;
+ }
+
+ ServicePrivate service(discoveredServices.value(serviceUuid));
+ service->setError(errorCode);
+}
+
void QLowEnergyControllerPrivateOSX::connectToDevice()
{
Q_ASSERT_X(isValid(), "connectToDevice", "invalid private controller");
@@ -471,6 +557,106 @@ void QLowEnergyControllerPrivateOSX::discoverServiceDetails(const QBluetoothUuid
}
}
+void QLowEnergyControllerPrivateOSX::writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
+ QLowEnergyHandle charHandle, const QByteArray &newValue,
+ bool writeWithResponse)
+{
+ Q_ASSERT_X(!service.isNull(), "writeCharacteristic", "invalid service (null)");
+
+ if (!isValid()) {
+ qCWarning(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), "
+ "invalid controller";
+ 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) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), "
+ "no service with uuid: " << service << "found";
+ return;
+ }
+
+ if (!service->characteristicList.contains(charHandle)) {
+ qCDebug(QT_BT_OSX) << "QLowEnergyControllerPrivateOSX::writeCharacteristic(), "
+ "no characteristic with handle: " << charHandle << "found";
+ return;
+ }
+
+ const bool result = [centralManager write:newValue
+ characteristic:charHandle
+ serviceUuid:service->uuid
+ serviceHandle:service->startHandle
+ withResponse:writeWithResponse];
+ if (!result)
+ service->setError(QLowEnergyService::CharacteristicWriteError);
+}
+
+quint16 QLowEnergyControllerPrivateOSX::updateValueOfCharacteristic(QLowEnergyHandle charHandle,
+ const QByteArray &value,
+ bool appendValue)
+{
+ QSharedPointer<QLowEnergyServicePrivate> service = serviceForHandle(charHandle);
+ if (!service.isNull() && service->characteristicList.contains(charHandle)) {
+ if (appendValue)
+ service->characteristicList[charHandle].value += value;
+ else
+ service->characteristicList[charHandle].value = value;
+
+ return service->characteristicList[charHandle].value.size();
+ }
+
+ return 0;
+}
+
+void QLowEnergyControllerPrivateOSX::writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
+ QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue)
+{
+ Q_UNUSED(service)
+ Q_UNUSED(charHandle)
+ Q_UNUSED(descriptorHandle)
+ Q_UNUSED(newValue)
+}
+
+QSharedPointer<QLowEnergyServicePrivate> QLowEnergyControllerPrivateOSX::serviceForHandle(QLowEnergyHandle handle)
+{
+ foreach (QSharedPointer<QLowEnergyServicePrivate> service, discoveredServices.values()) {
+ if (service->startHandle <= handle && handle <= service->endHandle)
+ return service;
+ }
+
+ return QSharedPointer<QLowEnergyServicePrivate>();
+}
+
+QLowEnergyCharacteristic QLowEnergyControllerPrivateOSX::characteristicForHandle(QLowEnergyHandle charHandle)
+{
+ QSharedPointer<QLowEnergyServicePrivate> service(serviceForHandle(charHandle));
+ if (service.isNull())
+ return QLowEnergyCharacteristic();
+
+ if (service->characteristicList.isEmpty())
+ return QLowEnergyCharacteristic();
+
+ // Check whether it is the handle of a characteristic header
+ if (service->characteristicList.contains(charHandle))
+ return QLowEnergyCharacteristic(service, charHandle);
+
+ // Check whether it is the handle of the characteristic value or its descriptors
+ QList<QLowEnergyHandle> charHandles(service->characteristicList.keys());
+ std::sort(charHandles.begin(), charHandles.end());
+
+ for (int i = charHandles.size() - 1; i >= 0; --i) {
+ if (charHandles.at(i) > charHandle)
+ continue;
+
+ return QLowEnergyCharacteristic(service, charHandles.at(i));
+ }
+
+ return QLowEnergyCharacteristic();
+}
+
void QLowEnergyControllerPrivateOSX::setErrorDescription(QLowEnergyController::Error errorCode)
{
// This function does not emit!
diff --git a/src/bluetooth/qlowenergycontroller_osx_p.h b/src/bluetooth/qlowenergycontroller_osx_p.h
index f9764c03..a8f8aaa0 100644
--- a/src/bluetooth/qlowenergycontroller_osx_p.h
+++ b/src/bluetooth/qlowenergycontroller_osx_p.h
@@ -34,6 +34,7 @@
#ifndef QLOWENERGYCONTROLLER_OSX_P_H
#define QLOWENERGYCONTROLLER_OSX_P_H
+#include "qlowenergyserviceprivate_p.h"
#include "osx/osxbtcentralmanager_p.h"
#include "qlowenergycontroller_p.h"
#include "qlowenergycontroller.h"
@@ -41,6 +42,7 @@
#include "qbluetoothaddress.h"
#include "qbluetoothuuid.h"
+#include <QtCore/qsharedpointer.h>
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qmap.h>
@@ -52,6 +54,7 @@ class QLowEnergyControllerPrivateOSX : public QLowEnergyControllerPrivate,
public OSXBluetooth::CentralManagerDelegate
{
friend class QLowEnergyController;
+ friend class QLowEnergyService;
public:
QLowEnergyControllerPrivateOSX(QLowEnergyController *q);
QLowEnergyControllerPrivateOSX(QLowEnergyController *q,
@@ -67,15 +70,36 @@ private:
void serviceDiscoveryFinished(LEServices services) Q_DECL_OVERRIDE;
void serviceDetailsDiscoveryFinished(LEService service) Q_DECL_OVERRIDE;
+ void characteristicWriteNotification(LECharacteristic ch) Q_DECL_OVERRIDE;
void disconnected() Q_DECL_OVERRIDE;
void error(QLowEnergyController::Error errorCode) Q_DECL_OVERRIDE;
void error(const QBluetoothUuid &serviceUuid,
QLowEnergyController::Error errorCode) Q_DECL_OVERRIDE;
+ void error(const QBluetoothUuid &serviceUuid,
+ QLowEnergyHandle charHandle,
+ QLowEnergyService::ServiceError error) Q_DECL_OVERRIDE;
void connectToDevice();
void discoverServices();
void discoverServiceDetails(const QBluetoothUuid &serviceUuid);
+ void writeCharacteristic(QSharedPointer<QLowEnergyServicePrivate> service,
+ QLowEnergyHandle charHandle, const QByteArray &newValue,
+ bool writeWithResponse);
+
+ quint16 updateValueOfCharacteristic(QLowEnergyHandle charHandle,
+ const QByteArray &value,
+ bool appendValue);
+
+ void writeDescriptor(QSharedPointer<QLowEnergyServicePrivate> service,
+ QLowEnergyHandle charHandle, const QLowEnergyHandle descriptorHandle,
+ const QByteArray &newValue);
+
+
+ // 'Lookup' functions:
+ QSharedPointer<QLowEnergyServicePrivate> serviceForHandle(QLowEnergyHandle serviceHandle);
+ QLowEnergyCharacteristic characteristicForHandle(QLowEnergyHandle charHandle);
+
void setErrorDescription(QLowEnergyController::Error errorCode);
void invalidateServices();
diff --git a/src/bluetooth/qlowenergyservice_osx.mm b/src/bluetooth/qlowenergyservice_osx.mm
index e2f5d35f..f9c124e4 100644
--- a/src/bluetooth/qlowenergyservice_osx.mm
+++ b/src/bluetooth/qlowenergyservice_osx.mm
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2013 Javier S. Pedro <maemo@javispedro.com>
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtBluetooth module of the Qt Toolkit.
@@ -31,18 +32,50 @@
**
****************************************************************************/
+#include "qlowenergycontroller_osx_p.h"
+#include "qlowenergyserviceprivate_p.h"
#include "qlowenergycharacteristic.h"
#include "qlowenergydescriptor.h"
#include "qlowenergyservice.h"
#include "qbluetoothuuid.h"
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+QLowEnergyControllerPrivateOSX *qt_mac_le_controller(QSharedPointer<QLowEnergyServicePrivate> d_ptr)
+{
+ if (d_ptr.isNull())
+ return Q_NULLPTR;
+
+ return static_cast<QLowEnergyControllerPrivateOSX *>(d_ptr->controller.data());
+}
+
+}
+
QLowEnergyService::QLowEnergyService(QSharedPointer<QLowEnergyServicePrivate> d, QObject *parent)
: QObject(parent),
d_ptr(d)
{
+ qRegisterMetaType<QLowEnergyService::ServiceState>("QLowEnergyService::ServiceState");
+ qRegisterMetaType<QLowEnergyService::ServiceError>("QLowEnergyService::ServiceError");
+
+ connect(d.data(), SIGNAL(error(QLowEnergyService::ServiceError)),
+ this, SIGNAL(error(QLowEnergyService::ServiceError)));
+ connect(d.data(), SIGNAL(stateChanged(QLowEnergyService::ServiceState)),
+ this, SIGNAL(stateChanged(QLowEnergyService::ServiceState)));
+ connect(d.data(), SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)),
+ this, SIGNAL(characteristicChanged(QLowEnergyCharacteristic, QByteArray)));
+ connect(d.data(), SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)),
+ this, SIGNAL(characteristicWritten(QLowEnergyCharacteristic, QByteArray)));
+ connect(d.data(), SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)),
+ this, SIGNAL(descriptorWritten(QLowEnergyDescriptor, QByteArray)));
}
QLowEnergyService::~QLowEnergyService()
@@ -51,69 +84,137 @@ QLowEnergyService::~QLowEnergyService()
QList<QBluetoothUuid> QLowEnergyService::includedServices() const
{
- return QList<QBluetoothUuid>();
+ return d_ptr->includedServices;
}
QLowEnergyService::ServiceTypes QLowEnergyService::type() const
{
- return PrimaryService;
+ return d_ptr->type;
}
QLowEnergyService::ServiceState QLowEnergyService::state() const
{
- return InvalidService;
+ return d_ptr->state;
}
QLowEnergyCharacteristic QLowEnergyService::characteristic(const QBluetoothUuid &uuid) const
{
- Q_UNUSED(uuid)
+ foreach (const QLowEnergyHandle handle, d_ptr->characteristicList.keys()) {
+ if (d_ptr->characteristicList[handle].uuid == uuid)
+ return QLowEnergyCharacteristic(d_ptr, handle);
+ }
return QLowEnergyCharacteristic();
}
QList<QLowEnergyCharacteristic> QLowEnergyService::characteristics() const
{
- return QList<QLowEnergyCharacteristic>();
+ QList<QLowEnergyCharacteristic> result;
+ QList<QLowEnergyHandle> handles(d_ptr->characteristicList.keys());
+
+ std::sort(handles.begin(), handles.end());
+
+ foreach (const QLowEnergyHandle &handle, handles) {
+ QLowEnergyCharacteristic characteristic(d_ptr, handle);
+ result.append(characteristic);
+ }
+
+ return result;
}
QBluetoothUuid QLowEnergyService::serviceUuid() const
{
- return QBluetoothUuid();
+ return d_ptr->uuid;
}
QString QLowEnergyService::serviceName() const
{
- return QString();
+ bool ok = false;
+ const quint16 clsId = d_ptr->uuid.toUInt16(&ok);
+ if (ok) {
+ QBluetoothUuid::ServiceClassUuid uuid
+ = static_cast<QBluetoothUuid::ServiceClassUuid>(clsId);
+ const QString name = QBluetoothUuid::serviceClassToString(uuid);
+ if (!name.isEmpty())
+ return name;
+ }
+
+ return qApp ? qApp->translate("QBluetoothServiceDiscoveryAgent", "Unknown Service") :
+ QStringLiteral("Unknown Service");
}
void QLowEnergyService::discoverDetails()
{
+ QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
+
+ if (!controller || d_ptr->state == InvalidService) {
+ d_ptr->setError(OperationError);
+ return;
+ }
+
+ if (d_ptr->state != DiscoveryRequired)
+ return;
+
+ d_ptr->setState(QLowEnergyService::DiscoveringServices);
+ controller->discoverServiceDetails(d_ptr->uuid);
}
QLowEnergyService::ServiceError QLowEnergyService::error() const
{
- return NoError;
+ return d_ptr->lastError;
}
bool QLowEnergyService::contains(const QLowEnergyCharacteristic &characteristic) const
{
- Q_UNUSED(characteristic)
+ if (characteristic.d_ptr.isNull() || !characteristic.data)
+ return false;
+
+ if (d_ptr == characteristic.d_ptr
+ && d_ptr->characteristicList.contains(characteristic.attributeHandle())) {
+ return true;
+ }
return false;
}
-void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &characteristic,
- const QByteArray &newValue,
+void QLowEnergyService::writeCharacteristic(const QLowEnergyCharacteristic &ch, const QByteArray &newValue,
WriteMode mode)
{
- Q_UNUSED(characteristic)
- Q_UNUSED(newValue)
- Q_UNUSED(mode)
+ // Not a characteristic of this service
+ if (!contains(ch))
+ return;
+
+ if (state() != ServiceDiscovered)
+ d_ptr->setError(QLowEnergyService::OperationError);
+
+ QLowEnergyControllerPrivateOSX *const controller = qt_mac_le_controller(d_ptr);
+ if (!controller)
+ return;
+
+ // Don't write if properties don't permit it
+ if (mode == WriteWithResponse && (ch.properties() & QLowEnergyCharacteristic::Write))
+ controller->writeCharacteristic(ch.d_ptr, ch.attributeHandle(), newValue, true);
+ else if (mode == WriteWithoutResponse && (ch.properties() & QLowEnergyCharacteristic::WriteNoResponse))
+ controller->writeCharacteristic(ch.d_ptr, ch.attributeHandle(), newValue, false);
+ else
+ d_ptr->setError(QLowEnergyService::OperationError);
}
bool QLowEnergyService::contains(const QLowEnergyDescriptor &descriptor) const
{
- Q_UNUSED(descriptor)
+ if (descriptor.d_ptr.isNull() || !descriptor.data)
+ return false;
+
+ const QLowEnergyHandle charHandle = descriptor.characteristicHandle();
+ if (!charHandle)
+ return false;
+
+ if (d_ptr == descriptor.d_ptr && d_ptr->characteristicList.contains(charHandle)
+ && d_ptr->characteristicList[charHandle].descriptorList.contains(descriptor.handle()))
+ {
+ return true;
+ }
+
return false;
}
@@ -123,3 +224,5 @@ void QLowEnergyService::writeDescriptor(const QLowEnergyDescriptor &descriptor,
Q_UNUSED(descriptor)
Q_UNUSED(newValue)
}
+
+QT_END_NAMESPACE