diff options
author | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-09-10 12:54:18 +0200 |
---|---|---|
committer | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-09-26 15:41:09 +0200 |
commit | d1d77c8210ecf7a89a81dad69d21b91e06bf129e (patch) | |
tree | 94d7b91c64478720eee9c9afd82acfc496c383e1 /src/bluetooth/qbluetoothserviceinfo_osx.mm | |
parent | c1aabdba2e839ba525a4918eebfbf6fbbe96d34d (diff) |
Port QBluetoothSocket to OS X.
Implement QBluetoothSocket using IOBluetooth framework on OS X
(will implement Qt's API as close as possible with a given Apple's API).
Update 0: add (empty for now) delegate classes (L2CAP/RFCOMM).
Update 1: add service discovery (called doDeviceDiscovery though).
Update 2: implement the public class' logic (QBluetoothSocket, connectToService).
Update 3: more public logic implemented (since it's easy :) )
Update 4: L2CAP delegate - initial logic.
Update 5: connectToService - L2CAP "socket".
Update 6: fix pivate header files.
Update 7: fix dependency after the previous patch was merged.
Update 8: writeData - initial version for L2CAP.
Update 9: since RFCOM/L2CAP delegates have the same interface,
no need in duplicating the same class - add a "generic"
ChannelDelegate instead.
Update 10: more RFCOMM logic.
Update 11: function to build a service description from
QBluetoothServiceInfo (to be registered on SDP server).
Update 12: QBluetoothSocket::close/abort.
Update 13: Create a dictioinary out of QBluetoothServiceInfo to register a service.
Update 14: Add service registration.
Update 15: Convert attributes (sequences and 'scalars') from QBluetoothServiceInfor
into NSDictionary.
Update 16: Update QBluetoothServiceInfo with a real PSM/ChannelID
after a service was registered.
Update 17: Move a private class (bluetooth socket) into the separate private
header file (to make it visible for bluetooth_server_osx)
Update 18: Add an interface to create a bluetooth socket (private class)
from a channel, reported by a notification (found by a listening
server).
Update 19: Fix an invalid assert - any state (Inactive/ServiceDiscovery/DeviceDiscovery)
is possible, not only Inactive.
Implement the missing 'readData' and 'writeData' for RFCOMM.
Set SDP query as non-active after query finished.
Temporary (!) workaround - can not invokeMethod on a private socket (d_ptr).
Update 20: When creating a socket wrapper from an incoming notification/channel, set:
socket type + channel's delegate.
Change-Id: Idd6d5478597206ed759f49e282baed948d105ddf
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
Diffstat (limited to 'src/bluetooth/qbluetoothserviceinfo_osx.mm')
-rw-r--r-- | src/bluetooth/qbluetoothserviceinfo_osx.mm | 106 |
1 files changed, 101 insertions, 5 deletions
diff --git a/src/bluetooth/qbluetoothserviceinfo_osx.mm b/src/bluetooth/qbluetoothserviceinfo_osx.mm index b8463a52..621aab35 100644 --- a/src/bluetooth/qbluetoothserviceinfo_osx.mm +++ b/src/bluetooth/qbluetoothserviceinfo_osx.mm @@ -39,19 +39,26 @@ ** ****************************************************************************/ +#include "osx/osxbtservicerecord_p.h" #include "qbluetoothserviceinfo.h" #include "qbluetoothdeviceinfo.h" +#include "osx/osxbtutility_p.h" +#include <QtCore/qloggingcategory.h> +#include <QtCore/qvariant.h> #include <QtCore/qglobal.h> #include <QtCore/qmap.h> #include <QtCore/qurl.h> +// Import, it's Objective-C header (no inclusion guards). +#import <IOBluetooth/objc/IOBluetoothSDPServiceRecord.h> + QT_BEGIN_NAMESPACE class QBluetoothServiceInfoPrivate { public: - QBluetoothServiceInfoPrivate(); + QBluetoothServiceInfoPrivate(QBluetoothServiceInfo *q); ~QBluetoothServiceInfoPrivate(); bool registerService(const QBluetoothAddress &localAdapter = QBluetoothAddress()); @@ -65,29 +72,118 @@ public: QBluetoothServiceInfo::Sequence protocolDescriptor(QBluetoothUuid::ProtocolUuid protocol) const; int serverChannel() const; + +private: + QBluetoothServiceInfo *q_ptr; + bool registered; + + typedef OSXBluetooth::ObjCScopedPointer<IOBluetoothSDPServiceRecord> SDPRecord; + SDPRecord serviceRecord; }; -QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate() +QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate(QBluetoothServiceInfo *q) + : q_ptr(q), + registered(false) { + Q_ASSERT_X(q, "QBluetoothServiceInfoPrivate()", "invalid q_ptr (null)"); } QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate() { + // TODO: should it unregister? } bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAdapter) { Q_UNUSED(localAdapter) - return false; + + if (registered) + return false; + + Q_ASSERT_X(!serviceRecord, "QBluetoothServiceInfoPrivate::registerService()", + "not registered, but serviceRecord is not nil"); + + // TODO: create a service description (as NSDictionary) and add to the + // local SDP server via IOBluetoothSDPServiceRecord and its methods. + using namespace OSXBluetooth; + + ObjCStrongReference<NSMutableDictionary> + serviceDict(iobluetooth_service_dictionary(*q_ptr)); + + if (!serviceDict) { + qCWarning(QT_BT_OSX) << "QBluetoothServiceInfoPrivate::registerService(), " + "failed to create a service dictionary"; + return false; + } + + serviceRecord.reset([[IOBluetoothSDPServiceRecord + publishedServiceRecordWithDictionary:serviceDict] retain]); + + if (!serviceRecord) { + qCWarning(QT_BT_OSX) << "QBluetoothServiceInfoPrivate::registerService(), " + "failed to create register a service record"; + return false; + } + + QBluetoothServiceInfo::Sequence protocolDescriptorList; + bool updatePDL = false; + + if (q_ptr->socketProtocol() == QBluetoothServiceInfo::L2capProtocol) { + // + BluetoothL2CAPPSM psm = 0; + if ([serviceRecord getL2CAPPSM:&psm] == kIOReturnSuccess) { + if (psm != q_ptr->protocolServiceMultiplexer()) { + // Update with a real PSM assigned by IOBluetooth! + updatePDL = true; + QBluetoothServiceInfo::Sequence protocol; + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap)); + protocol << QVariant::fromValue(qint16(psm)); + protocolDescriptorList.append(QVariant::fromValue(protocol)); + } + } + } else if (q_ptr->socketProtocol() == QBluetoothServiceInfo::RfcommProtocol) { + // + BluetoothRFCOMMChannelID channelID = 0; + if ([serviceRecord getRFCOMMChannelID:&channelID] == kIOReturnSuccess) { + if (channelID != q_ptr->serverChannel()) { + updatePDL = true; + QBluetoothServiceInfo::Sequence protocol; + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap)); + protocolDescriptorList.append(QVariant::fromValue(protocol)); + protocol.clear(); + protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm)) + << QVariant::fromValue(quint8(channelID)); + protocolDescriptorList.append(QVariant::fromValue(protocol)); + } + } + } + + if (updatePDL) + q_ptr->setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList); + + // TODO - check ServiceRecordHandle + error handling - if we failed to obtain a port. + + registered = true; + + return true; } bool QBluetoothServiceInfoPrivate::isRegistered() const { - return false; + return registered; } bool QBluetoothServiceInfoPrivate::unregisterService() { + if (!registered) + return false; + + Q_ASSERT_X(serviceRecord, "QBluetoothServiceInfoPrivate::unregisterService()", + "service registered, but serviceRecord is nil"); + + [serviceRecord removeServiceRecord]; + serviceRecord.reset(nil); + return false; } @@ -107,7 +203,7 @@ bool QBluetoothServiceInfo::unregisterService() } QBluetoothServiceInfo::QBluetoothServiceInfo() - : d_ptr(QSharedPointer<QBluetoothServiceInfoPrivate>(new QBluetoothServiceInfoPrivate)) + : d_ptr(QSharedPointer<QBluetoothServiceInfoPrivate>(new QBluetoothServiceInfoPrivate(this))) { } |