summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bluetooth/bluetooth.pro8
-rw-r--r--src/bluetooth/osx/osxbt.pri6
-rw-r--r--src/bluetooth/osx/osxbtsocketlistener.mm142
-rw-r--r--src/bluetooth/osx/osxbtsocketlistener_p.h92
-rw-r--r--src/bluetooth/qbluetoothserver_osx.mm501
-rw-r--r--src/bluetooth/qbluetoothserver_osx_p.h110
-rw-r--r--src/bluetooth/qbluetoothserver_p.h4
-rw-r--r--src/bluetooth/qbluetoothserviceinfo_osx.mm72
8 files changed, 897 insertions, 38 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index 428afa87..16ae481f 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -152,18 +152,20 @@ config_bluez:qtHaveModule(dbus) {
qbluetoothdevicediscoveryagent_osx.mm \
qbluetoothserviceinfo_osx.mm \
qbluetoothservicediscoveryagent_osx.mm \
- qbluetoothsocket_osx.mm
+ qbluetoothsocket_osx.mm \
+ qbluetoothserver_osx.mm
SOURCES += \
- qbluetoothserver_p.cpp \
qlowenergycontroller_p.cpp
- PRIVATE_HEADERS += qbluetoothsocket_osx_p.h
+ PRIVATE_HEADERS += qbluetoothsocket_osx_p.h \
+ qbluetoothserver_osx_p.h
SOURCES -= qbluetoothdevicediscoveryagent.cpp
SOURCES -= qbluetoothserviceinfo.cpp
SOURCES -= qbluetoothservicediscoveryagent.cpp
SOURCES -= qbluetoothsocket.cpp
+ SOURCES -= qbluetoothserver.cpp
} else {
message("Unsupported Bluetooth platform, will not build a working QtBluetooth library.")
message("Either no Qt D-Bus found or no BlueZ headers.")
diff --git a/src/bluetooth/osx/osxbt.pri b/src/bluetooth/osx/osxbt.pri
index 59b928db..c1382484 100644
--- a/src/bluetooth/osx/osxbt.pri
+++ b/src/bluetooth/osx/osxbt.pri
@@ -6,7 +6,8 @@ PRIVATE_HEADERS += osx/osxbtutility_p.h \
osx/osxbtrfcommchannel_p.h \
osx/osxbtl2capchannel_p.h \
osx/osxbtchanneldelegate_p.h \
- osx/osxbtservicerecord_p.h
+ osx/osxbtservicerecord_p.h \
+ osx/osxbtsocketlistener_p.h
OBJECTIVE_SOURCES += osx/osxbtutility.mm \
osx/osxbtdevicepair.mm \
@@ -16,4 +17,5 @@ OBJECTIVE_SOURCES += osx/osxbtutility.mm \
osx/osxbtrfcommchannel.mm \
osx/osxbtl2capchannel.mm \
osx/osxbtchanneldelegate.mm \
- osx/osxbtservicerecord.mm
+ osx/osxbtservicerecord.mm \
+ osx/osxbtsocketlistener.mm
diff --git a/src/bluetooth/osx/osxbtsocketlistener.mm b/src/bluetooth/osx/osxbtsocketlistener.mm
new file mode 100644
index 00000000..125f910a
--- /dev/null
+++ b/src/bluetooth/osx/osxbtsocketlistener.mm
@@ -0,0 +1,142 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "osxbtsocketlistener_p.h"
+#include "osxbtutility_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qdebug.h>
+
+// Imports, since these are Objective-C headers and
+// they do not have inclusion guards.
+#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
+#import <IOBluetooth/objc/IOBluetoothL2CAPChannel.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+SocketListener::~SocketListener()
+{
+}
+
+}
+
+QT_END_NAMESPACE
+
+#ifdef QT_NAMESPACE
+
+using namespace QT_NAMESPACE;
+
+#endif
+
+@implementation QT_MANGLE_NAMESPACE(OSXBTSocketListener)
+
+- (id)initWithListener:(OSXBluetooth::SocketListener *)aDelegate
+{
+ Q_ASSERT_X(aDelegate, "-initWithListener:", "invalid delegate (null)");
+ if (self = [super init]) {
+ connectionNotification = nil;
+ delegate = aDelegate;
+ port = 0;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [connectionNotification unregister];
+ [connectionNotification release];
+
+ [super dealloc];
+}
+
+- (bool)listenRFCOMMConnectionsWithChannelID:(BluetoothRFCOMMChannelID)channelID
+{
+ Q_ASSERT_X(!connectionNotification, "-listenRFCOMMConnectionsWithChannelID",
+ "already listening");
+
+ connectionNotification = [IOBluetoothRFCOMMChannel registerForChannelOpenNotifications:self
+ selector:@selector(rfcommOpenNotification:channel:)
+ withChannelID:channelID
+ direction:kIOBluetoothUserNotificationChannelDirectionIncoming];
+ connectionNotification = [connectionNotification retain];
+ if (connectionNotification)
+ port = channelID;
+
+ return connectionNotification;
+}
+
+- (bool)listenL2CAPConnectionsWithPSM:(BluetoothL2CAPPSM)psm
+{
+ Q_ASSERT_X(!connectionNotification, "-listenL2CAPConnectionsWithPSM:",
+ "already listening");
+
+ connectionNotification = [IOBluetoothL2CAPChannel registerForChannelOpenNotifications:self
+ selector:@selector(l2capOpenNotification:channel:)
+ withPSM:psm
+ direction:kIOBluetoothUserNotificationChannelDirectionIncoming];
+ connectionNotification = [connectionNotification retain];
+ if (connectionNotification)
+ port = psm;
+
+ return connectionNotification;
+}
+
+- (void)rfcommOpenNotification:(IOBluetoothUserNotification *)notification
+ channel:(IOBluetoothRFCOMMChannel *)newChannel
+{
+ Q_UNUSED(notification)
+
+ Q_ASSERT_X(delegate, "-rfcommOpenNotification:channel:",
+ "invalid delegate (null)");
+ delegate->openNotify(newChannel);
+}
+
+- (void)l2capOpenNotification:(IOBluetoothUserNotification *)notification
+ channel:(IOBluetoothL2CAPChannel *)newChannel
+{
+ Q_UNUSED(notification)
+
+ Q_ASSERT_X(delegate, "-l2capOpenNotification:channel:",
+ "invalid delegate (null)");
+ delegate->openNotify(newChannel);
+}
+
+- (quint16)port
+{
+ return port;
+}
+
+@end
diff --git a/src/bluetooth/osx/osxbtsocketlistener_p.h b/src/bluetooth/osx/osxbtsocketlistener_p.h
new file mode 100644
index 00000000..0f82ba37
--- /dev/null
+++ b/src/bluetooth/osx/osxbtsocketlistener_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef OSXBTSOCKETLISTENER_P_H
+#define OSXBTSOCKETLISTENER_P_H
+
+#include <QtCore/qglobal.h>
+
+#include <Foundation/Foundation.h>
+#include <IOBluetooth/Bluetooth.h>
+
+@class IOBluetoothUserNotification;
+@class IOBluetoothRFCOMMChannel;
+@class IOBluetoothL2CAPChannel;
+@class QT_MANGLE_NAMESPACE(OSXBTSocketListener);
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+class SocketListener
+{
+public:
+ typedef QT_MANGLE_NAMESPACE(OSXBTSocketListener) ObjCListener;
+
+ virtual ~SocketListener();
+
+ virtual void openNotify(IOBluetoothRFCOMMChannel *channel) = 0;
+ virtual void openNotify(IOBluetoothL2CAPChannel *channel) = 0;
+};
+
+}
+
+QT_END_NAMESPACE
+
+// A single OSXBTSocketListener can be started only once with
+// RFCOMM or L2CAP protocol. It must be deleted to stop listening.
+
+@interface QT_MANGLE_NAMESPACE(OSXBTSocketListener) : NSObject
+{
+ IOBluetoothUserNotification *connectionNotification;
+ QT_PREPEND_NAMESPACE(OSXBluetooth::SocketListener) *delegate;
+ quint16 port;
+}
+
+- (id)initWithListener:(QT_PREPEND_NAMESPACE(OSXBluetooth::SocketListener) *)aDelegate;
+- (void)dealloc;
+
+- (bool)listenRFCOMMConnectionsWithChannelID:(BluetoothRFCOMMChannelID)channelID;
+- (bool)listenL2CAPConnectionsWithPSM:(BluetoothL2CAPPSM)psm;
+
+- (void)rfcommOpenNotification:(IOBluetoothUserNotification *)notification
+ channel:(IOBluetoothRFCOMMChannel *)newChannel;
+
+- (void)l2capOpenNotification:(IOBluetoothUserNotification *)notification
+ channel:(IOBluetoothL2CAPChannel *)newChannel;
+
+- (quint16)port;
+
+@end
+
+#endif
diff --git a/src/bluetooth/qbluetoothserver_osx.mm b/src/bluetooth/qbluetoothserver_osx.mm
new file mode 100644
index 00000000..412fb28c
--- /dev/null
+++ b/src/bluetooth/qbluetoothserver_osx.mm
@@ -0,0 +1,501 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt 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 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "osx/osxbtsocketlistener_p.h"
+#include "qbluetoothserver_osx_p.h"
+#include "qbluetoothsocket_osx_p.h"
+#include "qbluetoothlocaldevice.h"
+#include "osx/osxbtutility_p.h"
+#include "qbluetoothserver.h"
+#include "qbluetoothsocket.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qmutex.h>
+
+// Import, since Obj-C headers do not have inclusion guards.
+#import <IOBluetooth/objc/IOBluetoothRFCOMMChannel.h>
+#import <IOBluetooth/objc/IOBluetoothL2CAPChannel.h>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+namespace {
+
+typedef QBluetoothServiceInfo QSInfo;
+
+QMap<quint16, QBluetoothServerPrivate *> &busyPSMs()
+{
+ static QMap<quint16, QBluetoothServerPrivate *> psms;
+ return psms;
+}
+
+QMap<quint16, QBluetoothServerPrivate *> &busyChannels()
+{
+ static QMap<quint16, QBluetoothServerPrivate *> channels;
+ return channels;
+}
+
+typedef QMap<quint16, QBluetoothServerPrivate *>::iterator ServerMapIterator;
+
+}
+
+
+QBluetoothServerPrivate::QBluetoothServerPrivate(QSInfo::Protocol type, QBluetoothServer *q)
+ : serverType(type),
+ q_ptr(q),
+ lastError(QBluetoothServer::NoError),
+ port(0),
+ maxPendingConnections(1)
+{
+ Q_ASSERT_X(q_ptr, "QBluetoothServerPrivate", "invalid q_ptr (null)");
+ if (serverType == QSInfo::UnknownProtocol) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServerPrivate::QBluetoothServerPrivate(), "
+ "unknown protocol";
+ }
+}
+
+QBluetoothServerPrivate::~QBluetoothServerPrivate()
+{
+ // Actually, not good, but lock must be acquired.
+ // TODO: test this.
+ const QMutexLocker lock(&channelMapMutex());
+ unregisterServer(this);
+}
+
+void QBluetoothServerPrivate::_q_newConnection()
+{
+ // Noop, we have openNotify for this.
+}
+
+bool QBluetoothServerPrivate::startListener(quint16 realPort)
+{
+ Q_ASSERT_X(realPort, "startListener", "invalid port");
+
+ if (serverType == QSInfo::UnknownProtocol) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServerPrivate::startListener(), "
+ "invalid protocol";
+ return false;
+ }
+
+ if (!listener)
+ listener.reset([[ObjCListener alloc] initWithListener:this]);
+
+ bool result = false;
+ if (serverType == QSInfo::RfcommProtocol)
+ result = [listener listenRFCOMMConnectionsWithChannelID:realPort];
+ else
+ result = [listener listenL2CAPConnectionsWithPSM:realPort];
+
+ if (!result)
+ listener.reset(nil);
+
+ return result;
+}
+
+void QBluetoothServerPrivate::stopListener()
+{
+ listener.reset(nil);
+}
+
+void QBluetoothServerPrivate::openNotify(IOBluetoothRFCOMMChannel *channel)
+{
+ Q_ASSERT_X(listener, "openNotify", "invalid listener (nil)");
+ Q_ASSERT_X(channel, "openNotify", "invalid channel (nil)");
+ Q_ASSERT_X(q_ptr, "openNotify", "invalid q_ptr (null)");
+
+ PendingConnection newConnection(channel, true);
+ pendingConnections.append(newConnection);
+
+ emit q_ptr->newConnection();
+}
+
+void QBluetoothServerPrivate::openNotify(IOBluetoothL2CAPChannel *channel)
+{
+ Q_ASSERT_X(listener, "openNotify", "invalid listener (nil)");
+ Q_ASSERT_X(channel, "openNotify", "invalid channel (nil)");
+ Q_ASSERT_X(q_ptr, "openNotify", "invalid q_ptr (null)");
+
+ PendingConnection newConnection(channel, true);
+ pendingConnections.append(newConnection);
+
+ emit q_ptr->newConnection();
+}
+
+QMutex &QBluetoothServerPrivate::channelMapMutex()
+{
+ static QMutex mutex;
+ return mutex;
+}
+
+bool QBluetoothServerPrivate::channelIsBusy(quint16 channelID)
+{
+ // External lock is required.
+ return busyChannels().contains(channelID);
+}
+
+quint16 QBluetoothServerPrivate::findFreeChannel()
+{
+ // External lock is required.
+ for (quint16 i = 1; i <= 30; ++i) {
+ if (!busyChannels().contains(i))
+ return i;
+ }
+
+ return 0; //Invalid port.
+}
+
+bool QBluetoothServerPrivate::psmIsBusy(quint16 psm)
+{
+ // External lock is required.
+ return busyPSMs().contains(psm);
+}
+
+quint16 QBluetoothServerPrivate::findFreePSM()
+{
+ // External lock is required.
+ for (quint16 i = 1, e = std::numeric_limits<qint16>::max(); i < e; i += 2) {
+ if (!psmIsBusy(i))
+ return i;
+ }
+
+ return 0; // Invalid PSM.
+}
+
+void QBluetoothServerPrivate::registerServer(QBluetoothServerPrivate *server, quint16 port)
+{
+ // External lock is required + port must be free.
+ Q_ASSERT_X(server, "registerServer", "invalid server (null)");
+
+ const QSInfo::Protocol type = server->serverType;
+ if (type == QSInfo::RfcommProtocol) {
+ Q_ASSERT_X(!channelIsBusy(port), "registerServer",
+ "port is busy");
+ busyChannels()[port] = server;
+ } else if (type == QSInfo::L2capProtocol) {
+ Q_ASSERT_X(!psmIsBusy(port), "registerServer",
+ "port is busy");
+ busyPSMs()[port] = server;
+ } else {
+ qCWarning(QT_BT_OSX) << "can not register a server with unknown "
+ "protocol type";
+ }
+}
+
+QBluetoothServerPrivate *QBluetoothServerPrivate::registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol)
+{
+ // Eternal lock is required.
+ if (protocol == QSInfo::RfcommProtocol) {
+ ServerMapIterator it = busyChannels().find(port);
+ if (it != busyChannels().end())
+ return it.value();
+ } else if (protocol == QSInfo::L2capProtocol) {
+ ServerMapIterator it = busyPSMs().find(port);
+ if (it != busyPSMs().end())
+ return it.value();
+ } else {
+ qCWarning(QT_BT_OSX) << "QBluetoothServerPrivate::registeredServer(), "
+ "invalid protocol";
+ }
+
+ return Q_NULLPTR;
+}
+
+void QBluetoothServerPrivate::unregisterServer(QBluetoothServerPrivate *server)
+{
+ // External lock is required.
+ const QSInfo::Protocol type = server->serverType;
+ const quint16 port = server->port;
+
+ if (type == QSInfo::RfcommProtocol) {
+ ServerMapIterator it = busyChannels().find(port);
+ if (it != busyChannels().end()) {
+ busyChannels().erase(it);
+ } else {
+ qCWarning(QT_BT_OSX) << "QBluetoothServerPrivate::unregisterServer(), "
+ "server is not registered";
+ }
+ } else if (type == QSInfo::L2capProtocol) {
+ ServerMapIterator it = busyPSMs().find(port);
+ if (it != busyPSMs().end()) {
+ busyPSMs().erase(it);
+ } else {
+ qCWarning(QT_BT_OSX) << "QBluetoothServerPrivate::unregisterServer(), "
+ "server is not registered";
+ }
+ } else {
+ qCWarning(QT_BT_OSX) << "QBluetoothServerPrivate::unregisterServer(), "
+ "invalid protocol";
+ }
+}
+
+
+QBluetoothServer::QBluetoothServer(QSInfo::Protocol serverType, QObject *parent)
+ : QObject(parent),
+ d_ptr(new QBluetoothServerPrivate(serverType, this))
+{
+}
+
+QBluetoothServer::~QBluetoothServer()
+{
+ delete d_ptr;
+}
+
+void QBluetoothServer::close()
+{
+ d_ptr->listener.reset(nil);
+
+ // Needs a lock :(
+ const QMutexLocker lock(&d_ptr->channelMapMutex());
+ d_ptr->unregisterServer(d_ptr);
+}
+
+bool QBluetoothServer::listen(const QBluetoothAddress &address, quint16 port)
+{
+ typedef QBluetoothServerPrivate::ObjCListener ObjCListener;
+
+ if (d_ptr->listener) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServer::listen() ",
+ "already in listen mode, "
+ "close server first";
+ return false;
+ }
+
+ const QBluetoothLocalDevice device(address);
+ if (!device.isValid()) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServer::listen(), device does not support Bluetooth or "
+ << address.toString()
+ << " is not a valid local adapter";
+ d_ptr->lastError = UnknownError;
+ emit error(UnknownError);
+ return false;
+ }
+
+ const QBluetoothLocalDevice::HostMode hostMode = device.hostMode();
+ if (hostMode == QBluetoothLocalDevice::HostPoweredOff) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServer::listen(), "
+ "bluetooth device is powered off";
+ d_ptr->lastError = PoweredOffError;
+ emit error(PoweredOffError);
+ return false;
+ }
+
+ const QSInfo::Protocol type = d_ptr->serverType;
+
+ if (type == QSInfo::UnknownProtocol) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServer::listen(), "
+ "invalid protocol";
+ d_ptr->lastError = UnsupportedProtocolError;
+ emit error(d_ptr->lastError);
+ return false;
+ }
+
+ d_ptr->lastError = QBluetoothServer::NoError;
+
+ // Now we have to register a (fake) port, doing a proper (?) lock.
+ const QMutexLocker lock(&d_ptr->channelMapMutex());
+
+ if (port) {
+ if (type == QSInfo::RfcommProtocol) {
+ if (d_ptr->channelIsBusy(port)) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServer::listen(), server port: "
+ << port << "already registered";
+ d_ptr->lastError = ServiceAlreadyRegisteredError;
+ }
+ } else {
+ if (d_ptr->psmIsBusy(port)) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServer::listen(), server port: "
+ << port << "already registered";
+ d_ptr->lastError = ServiceAlreadyRegisteredError;
+ }
+ }
+ } else {
+ type == QSInfo::RfcommProtocol ? port = d_ptr->findFreeChannel()
+ : port = d_ptr->findFreePSM();
+ }
+
+ if (d_ptr->lastError != QBluetoothServer::NoError) {
+ emit error(d_ptr->lastError);
+ return false;
+ }
+
+ if (!port) {
+ qCWarning(QT_BT_OSX) << "QBluetoothServer::listen(), all ports are busy";
+ d_ptr->lastError = ServiceAlreadyRegisteredError;
+ emit error(d_ptr->lastError);
+ return false;
+ }
+
+ // It's a fake port, the real one will be different
+ // (provided after a service was registered).
+ d_ptr->port = port;
+ d_ptr->registerServer(d_ptr, port);
+ d_ptr->listener.reset([[ObjCListener alloc] initWithListener:d_ptr]);
+
+ return true;
+}
+
+QBluetoothServiceInfo QBluetoothServer::listen(const QBluetoothUuid &uuid, const QString &serviceName)
+{
+ if (!listen())
+ return QBluetoothServiceInfo();
+
+ QBluetoothServiceInfo serviceInfo;
+ serviceInfo.setAttribute(QSInfo::ServiceName, serviceName);
+ serviceInfo.setAttribute(QSInfo::BrowseGroupList,
+ QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
+
+ QSInfo::Sequence classId;
+ classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
+ serviceInfo.setAttribute(QSInfo::BluetoothProfileDescriptorList, classId);
+
+ classId.prepend(QVariant::fromValue(uuid));
+ serviceInfo.setAttribute(QSInfo::ServiceClassIds, classId);
+ serviceInfo.setServiceUuid(uuid);
+
+ QSInfo::Sequence protocolDescriptorList;
+ QSInfo::Sequence protocol;
+ protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap));
+ if (d_ptr->serverType == QSInfo::L2capProtocol)
+ protocol << QVariant::fromValue(serverPort());
+ protocolDescriptorList.append(QVariant::fromValue(protocol));
+ protocol.clear();
+
+ if (d_ptr->serverType == QBluetoothServiceInfo::RfcommProtocol) {
+ protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
+ << QVariant::fromValue(quint8(serverPort()));
+ protocolDescriptorList.append(QVariant::fromValue(protocol));
+ }
+
+ serviceInfo.setAttribute(QSInfo::ProtocolDescriptorList,
+ protocolDescriptorList);
+
+
+ // It's now up to a service info to acquire a real PSM/channel ID
+ // (provided by IOBluetooth) and start a listener.
+ if (!serviceInfo.registerService())
+ return QBluetoothServiceInfo();
+
+ return serviceInfo;
+}
+
+bool QBluetoothServer::isListening() const
+{
+ return d_ptr->listener;
+}
+
+void QBluetoothServer::setMaxPendingConnections(int numConnections)
+{
+ // That's a 'fake' limit, it affects nothing.
+ d_ptr->maxPendingConnections = numConnections;
+}
+
+int QBluetoothServer::maxPendingConnections() const
+{
+ // That's a 'fake' limit, it affects nothing.
+ return d_ptr->maxPendingConnections;
+}
+
+bool QBluetoothServer::hasPendingConnections() const
+{
+ return d_ptr->pendingConnections.size();
+}
+
+QBluetoothSocket *QBluetoothServer::nextPendingConnection()
+{
+ if (!d_ptr->pendingConnections.size())
+ return Q_NULLPTR;
+
+ QScopedPointer<QBluetoothSocket> newSocket(new QBluetoothSocket);
+ QBluetoothServerPrivate::PendingConnection channel(d_ptr->pendingConnections.front());
+
+ // Remove it even if we have some errors below.
+ d_ptr->pendingConnections.pop_front();
+
+ if (d_ptr->serverType == QSInfo::RfcommProtocol) {
+ if (!newSocket->d_ptr->setChannel(static_cast<IOBluetoothRFCOMMChannel *>(channel)))
+ return Q_NULLPTR;
+ } else {
+ if (!newSocket->d_ptr->setChannel(static_cast<IOBluetoothL2CAPChannel *>(channel)))
+ return Q_NULLPTR;
+ }
+
+ return newSocket.take();
+}
+
+QBluetoothAddress QBluetoothServer::serverAddress() const
+{
+ return QBluetoothLocalDevice().address();
+}
+
+quint16 QBluetoothServer::serverPort() const
+{
+ return d_ptr->port;
+}
+
+void QBluetoothServer::setSecurityFlags(QBluetooth::SecurityFlags security)
+{
+ Q_UNUSED(security)
+ // Not implemented (yet?)
+}
+
+QBluetooth::SecurityFlags QBluetoothServer::securityFlags() const
+{
+ // Not implemented (yet?)
+ return QBluetooth::NoSecurity;
+}
+
+QSInfo::Protocol QBluetoothServer::serverType() const
+{
+ return d_ptr->serverType;
+}
+
+QBluetoothServer::Error QBluetoothServer::error() const
+{
+ return d_ptr->lastError;
+}
+
+#include "moc_qbluetoothserver.cpp"
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothserver_osx_p.h b/src/bluetooth/qbluetoothserver_osx_p.h
new file mode 100644
index 00000000..d501743c
--- /dev/null
+++ b/src/bluetooth/qbluetoothserver_osx_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtBluetooth module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL21$
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/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.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QBLUETOOTHSERVER_OSX_P_H
+#define QBLUETOOTHSERVER_OSX_P_H
+
+#ifdef QT_OSX_BLUETOOTH
+
+#include "osx/osxbtsocketlistener_p.h"
+#include "qbluetoothserviceinfo.h"
+#include "osx/osxbtutility_p.h"
+#include "qbluetoothserver.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+
+QT_BEGIN_NAMESPACE
+
+class QMutex;
+
+class QBluetoothServerPrivate : public OSXBluetooth::SocketListener
+{
+ friend class QBluetoothServer;
+ friend class QBluetoothServiceInfoPrivate;
+
+public:
+ QBluetoothServerPrivate(QBluetoothServiceInfo::Protocol type, QBluetoothServer *q);
+ ~QBluetoothServerPrivate();
+
+ void _q_newConnection();
+private:
+ bool startListener(quint16 realPort);
+ void stopListener();
+
+ // SocketListener (delegate):
+ void openNotify(IOBluetoothRFCOMMChannel *channel) Q_DECL_OVERRIDE;
+ void openNotify(IOBluetoothL2CAPChannel *channel) Q_DECL_OVERRIDE;
+
+ QBluetoothServiceInfo::Protocol serverType;
+ QBluetoothServer *q_ptr;
+ QBluetoothServer::Error lastError;
+
+ // Either a "temporary" channelID/PSM assigned by QBluetoothServer::listen,
+ // or a real channelID/PSM returned by IOBluetooth after we've registered
+ // a service.
+ quint16 port;
+
+ typedef OSXBluetooth::ObjCScopedPointer<ObjCListener> Listener;
+ Listener listener;
+
+ int maxPendingConnections;
+
+ // These static functions below
+ // deal with differences between bluetooth sockets
+ // (bluez and QtBluetooth's API) and IOBluetooth, where it's not possible
+ // to have a real PSM/channelID _before_ a service is registered,
+ // the solution - "fake" ports.
+ // These functions require external locking - using channelMapMutex.
+ static QMutex &channelMapMutex();
+
+ static bool channelIsBusy(quint16 channelID);
+ static quint16 findFreeChannel();
+
+ static bool psmIsBusy(quint16 psm);
+ static quint16 findFreePSM();
+
+ static void registerServer(QBluetoothServerPrivate *server, quint16 port);
+ static QBluetoothServerPrivate *registeredServer(quint16 port, QBluetoothServiceInfo::Protocol protocol);
+ static void unregisterServer(QBluetoothServerPrivate *server);
+
+ typedef OSXBluetooth::ObjCStrongReference<NSObject> PendingConnection;
+ QList<PendingConnection> pendingConnections;
+
+};
+
+QT_END_NAMESPACE
+
+#endif //QT_OSX_BLUETOOTH
+
+#endif
diff --git a/src/bluetooth/qbluetoothserver_p.h b/src/bluetooth/qbluetoothserver_p.h
index 75f1bde6..ee65994b 100644
--- a/src/bluetooth/qbluetoothserver_p.h
+++ b/src/bluetooth/qbluetoothserver_p.h
@@ -74,6 +74,8 @@ class QBluetoothSocket;
class QBluetoothServer;
+#ifndef QT_OSX_BLUETOOTH
+
class QBluetoothServerPrivate
#ifdef QT_QNX_BLUETOOTH
: public QObject
@@ -137,6 +139,8 @@ public:
#endif
};
+#endif //QT_OSX_BLUETOOTH
+
QT_END_NAMESPACE
#endif
diff --git a/src/bluetooth/qbluetoothserviceinfo_osx.mm b/src/bluetooth/qbluetoothserviceinfo_osx.mm
index 621aab35..61f68f7f 100644
--- a/src/bluetooth/qbluetoothserviceinfo_osx.mm
+++ b/src/bluetooth/qbluetoothserviceinfo_osx.mm
@@ -40,6 +40,7 @@
****************************************************************************/
#include "osx/osxbtservicerecord_p.h"
+#include "qbluetoothserver_osx_p.h"
#include "qbluetoothserviceinfo.h"
#include "qbluetoothdeviceinfo.h"
#include "osx/osxbtutility_p.h"
@@ -47,6 +48,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qvariant.h>
#include <QtCore/qglobal.h>
+#include <QtCore/qmutex.h>
#include <QtCore/qmap.h>
#include <QtCore/qurl.h>
@@ -58,6 +60,7 @@ QT_BEGIN_NAMESPACE
class QBluetoothServiceInfoPrivate
{
public:
+ typedef QBluetoothServiceInfo QSInfo;
QBluetoothServiceInfoPrivate(QBluetoothServiceInfo *q);
~QBluetoothServiceInfoPrivate();
@@ -116,54 +119,45 @@ bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &loca
return false;
}
- serviceRecord.reset([[IOBluetoothSDPServiceRecord
+ SDPRecord newRecord([[IOBluetoothSDPServiceRecord
publishedServiceRecordWithDictionary:serviceDict] retain]);
- if (!serviceRecord) {
+ if (!newRecord) {
qCWarning(QT_BT_OSX) << "QBluetoothServiceInfoPrivate::registerService(), "
"failed to create register a service record";
return false;
}
- QBluetoothServiceInfo::Sequence protocolDescriptorList;
- bool updatePDL = false;
+ const QSInfo::Protocol type = q_ptr->socketProtocol();
+ quint16 realPort = 0;
+ QBluetoothServerPrivate *server = Q_NULLPTR;
- if (q_ptr->socketProtocol() == QBluetoothServiceInfo::L2capProtocol) {
- //
+ if (type == 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));
- }
+ server = QBluetoothServerPrivate::registeredServer(q_ptr->protocolServiceMultiplexer(), type);
+ if ([newRecord getL2CAPPSM:&psm] != kIOReturnSuccess) {
+ [newRecord removeServiceRecord];
+ return false;
}
- } else if (q_ptr->socketProtocol() == QBluetoothServiceInfo::RfcommProtocol) {
- //
+ realPort = psm;
+ } else if (type == 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));
- }
+ server = QBluetoothServerPrivate::registeredServer(q_ptr->serverChannel(), type);
+ if ([newRecord getRFCOMMChannelID:&channelID] != kIOReturnSuccess) {
+ [newRecord removeServiceRecord];
+ return false;
}
+ realPort = channelID;
}
- if (updatePDL)
- q_ptr->setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList);
-
- // TODO - check ServiceRecordHandle + error handling - if we failed to obtain a port.
+ if (!server) {
+ [newRecord removeServiceRecord];
+ return false;
+ }
registered = true;
+ serviceRecord.reset(newRecord.take());
+ server->startListener(realPort);
return true;
}
@@ -184,7 +178,19 @@ bool QBluetoothServiceInfoPrivate::unregisterService()
[serviceRecord removeServiceRecord];
serviceRecord.reset(nil);
- return false;
+ const QSInfo::Protocol type = q_ptr->socketProtocol();
+ QBluetoothServerPrivate *server = Q_NULLPTR;
+
+ const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex());
+ if (type == QSInfo::RfcommProtocol)
+ server = QBluetoothServerPrivate::registeredServer(q_ptr->serverChannel(), type);
+ else if (type == QSInfo::L2capProtocol)
+ server = QBluetoothServerPrivate::registeredServer(q_ptr->protocolServiceMultiplexer(), type);
+
+ if (server)
+ server->stopListener();
+
+ return true;
}
bool QBluetoothServiceInfo::isRegistered() const