summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bluetooth/bluetooth.pro17
-rw-r--r--src/bluetooth/osx/osxbt.pri9
-rw-r--r--src/bluetooth/osx/osxbtconnectionmonitor.mm143
-rw-r--r--src/bluetooth/osx/osxbtconnectionmonitor_p.h86
-rw-r--r--src/bluetooth/osx/osxbtdeviceinquiry.mm199
-rw-r--r--src/bluetooth/osx/osxbtdeviceinquiry_p.h101
-rw-r--r--src/bluetooth/osx/osxbtdevicepair.mm236
-rw-r--r--src/bluetooth/osx/osxbtdevicepair_p.h132
-rw-r--r--src/bluetooth/osx/osxbtutility.mm97
-rw-r--r--src/bluetooth/osx/osxbtutility_p.h148
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm404
-rw-r--r--src/bluetooth/qbluetoothlocaldevice.cpp24
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_osx.mm499
-rw-r--r--src/bluetooth/qbluetoothlocaldevice_p.h2
-rw-r--r--tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro2
-rw-r--r--tests/auto/qbluetoothlocaldevice/qbluetoothlocaldevice.pro3
-rw-r--r--tests/auto/qbluetoothserver/qbluetoothserver.pro3
-rw-r--r--tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro1
-rw-r--r--tests/auto/qbluetoothserviceinfo/qbluetoothserviceinfo.pro3
-rw-r--r--tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro3
-rw-r--r--tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro3
21 files changed, 2111 insertions, 4 deletions
diff --git a/src/bluetooth/bluetooth.pro b/src/bluetooth/bluetooth.pro
index af0d96f0..49febab8 100644
--- a/src/bluetooth/bluetooth.pro
+++ b/src/bluetooth/bluetooth.pro
@@ -142,6 +142,23 @@ config_bluez:qtHaveModule(dbus) {
qbluetoothserver_android.cpp \
qlowenergycontroller_p.cpp
+} else:osx {
+ DEFINES += QT_OSX_BLUETOOTH
+ LIBS += -framework Foundation -framework IOBluetooth
+
+ include(osx/osxbt.pri)
+ OBJECTIVE_SOURCES += \
+ qbluetoothlocaldevice_osx.mm \
+ qbluetoothdevicediscoveryagent_osx.mm
+
+ SOURCES += \
+ qbluetoothservicediscoveryagent_p.cpp \
+ qbluetoothserviceinfo_p.cpp \
+ qbluetoothsocket_p.cpp \
+ qbluetoothserver_p.cpp \
+ qlowenergycontroller_p.cpp
+
+ SOURCES -= qbluetoothdevicediscoveryagent.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
new file mode 100644
index 00000000..fd25d17e
--- /dev/null
+++ b/src/bluetooth/osx/osxbt.pri
@@ -0,0 +1,9 @@
+HEADERS += osx/osxbtutility_p.h \
+ osx/osxbtdevicepair_p.h \
+ osx/osxbtdeviceinquiry_p.h \
+ osx/osxbtconnectionmonitor_p.h
+
+OBJECTIVE_SOURCES += osx/osxbtutility.mm \
+ osx/osxbtdevicepair.mm \
+ osx/osxbtdeviceinquiry.mm \
+ osx/osxbtconnectionmonitor.mm
diff --git a/src/bluetooth/osx/osxbtconnectionmonitor.mm b/src/bluetooth/osx/osxbtconnectionmonitor.mm
new file mode 100644
index 00000000..7f9b08a2
--- /dev/null
+++ b/src/bluetooth/osx/osxbtconnectionmonitor.mm
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** 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 "osxbtconnectionmonitor_p.h"
+#include "osxbtutility_p.h"
+
+#include <QtCore/qdebug.h>
+
+// Import, since these headers are not protected from the multiple inclusion.
+#import <IOBluetooth/objc/IOBluetoothUserNotification.h>
+#import <IOBluetooth/objc/IOBluetoothDevice.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+ConnectionMonitor::~ConnectionMonitor()
+{
+}
+
+}
+
+QT_END_NAMESPACE
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+@implementation QT_MANGLE_NAMESPACE(OSXBTConnectionMonitor)
+
+- (id)initWithMonitor:(OSXBluetooth::ConnectionMonitor *)aMonitor
+{
+ Q_ASSERT_X(aMonitor, "-initWithMonitor:", "invalid monitor (null)");
+
+ if (self = [super init]) {
+ monitor = aMonitor;
+ discoveryNotification = [[IOBluetoothDevice registerForConnectNotifications:self
+ selector:@selector(connectionNotification:withDevice:)] retain];
+ foundConnections = [[NSMutableArray alloc] init];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [discoveryNotification unregister];
+ [discoveryNotification release];
+
+ for (IOBluetoothUserNotification *n in foundConnections)
+ [n unregister];
+
+ [foundConnections release];
+
+ [super dealloc];
+}
+
+- (void)connectionNotification:(IOBluetoothUserNotification *)aNotification
+ withDevice:(IOBluetoothDevice *)device
+{
+ Q_UNUSED(aNotification)
+
+ typedef IOBluetoothUserNotification Notification;
+
+ if (!device)
+ return;
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ // All Obj-C objects are autoreleased.
+
+ const QBluetoothAddress deviceAddress(OSXBluetooth::qt_bt_address([device getAddress]));
+ if (deviceAddress.isNull())
+ return;
+
+ if (foundConnections) {
+ Notification *const notification = [device registerForDisconnectNotification:self
+ selector: @selector(connectionClosedNotification:withDevice:)];
+ if (notification)
+ [foundConnections addObject:notification];
+ }
+
+ Q_ASSERT_X(monitor, "-connectionNotification:withDevice:", "invalid monitor (null)");
+ monitor->deviceConnected(deviceAddress);
+}
+
+- (void)connectionClosedNotification:(IOBluetoothUserNotification *)notification
+ withDevice:(IOBluetoothDevice *)device
+{
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ [notification unregister];//?
+
+ // Does IOBluetoothUserNotification override isEqual?
+ [foundConnections removeObject:notification];
+
+ const QBluetoothAddress deviceAddress(OSXBluetooth::qt_bt_address([device getAddress]));
+ if (deviceAddress.isNull())
+ return;
+
+ Q_ASSERT_X(monitor, "-connectionClosedNotification:withDevice:", "invalid monitor (null)");
+ monitor->deviceDisconnected(deviceAddress);
+}
+
+@end
diff --git a/src/bluetooth/osx/osxbtconnectionmonitor_p.h b/src/bluetooth/osx/osxbtconnectionmonitor_p.h
new file mode 100644
index 00000000..bf0b45e8
--- /dev/null
+++ b/src/bluetooth/osx/osxbtconnectionmonitor_p.h
@@ -0,0 +1,86 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef OSXBTCONNECTIONMONITOR_P_H
+#define OSXBTCONNECTIONMONITOR_P_H
+
+#include "qbluetoothaddress.h"
+
+#include <QtCore/qglobal.h>
+
+#include <Foundation/Foundation.h>
+
+@class QT_MANGLE_NAMESPACE(OSXBTConnectionMonitor);
+@class IOBluetoothUserNotification;
+@class IOBluetoothDevice;
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+class ConnectionMonitor {
+public:
+ typedef QT_MANGLE_NAMESPACE(OSXBTConnectionMonitor) ObjCConnectionMonitor;
+
+ virtual ~ConnectionMonitor();
+
+ virtual void deviceConnected(const QBluetoothAddress &address) = 0;
+ virtual void deviceDisconnected(const QBluetoothAddress &address) = 0;
+};
+
+}
+
+QT_END_NAMESPACE
+
+@interface QT_MANGLE_NAMESPACE(OSXBTConnectionMonitor) : NSObject
+{
+ QT_PREPEND_NAMESPACE(OSXBluetooth::ConnectionMonitor) *monitor;
+ IOBluetoothUserNotification *discoveryNotification;
+ NSMutableArray *foundConnections;
+}
+
+- (id)initWithMonitor:(QT_PREPEND_NAMESPACE(OSXBluetooth::ConnectionMonitor) *)monitor;
+- (void)connectionNotification:(id)notification withDevice:(IOBluetoothDevice *)device;
+- (void)connectionClosedNotification:(id)notification withDevice:(IOBluetoothDevice *)device;
+
+@end
+
+#endif
diff --git a/src/bluetooth/osx/osxbtdeviceinquiry.mm b/src/bluetooth/osx/osxbtdeviceinquiry.mm
new file mode 100644
index 00000000..a15554d8
--- /dev/null
+++ b/src/bluetooth/osx/osxbtdeviceinquiry.mm
@@ -0,0 +1,199 @@
+/****************************************************************************
+**
+** 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 "osxbtdeviceinquiry_p.h"
+#include "osxbtutility_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qdebug.h>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+DeviceInquiryDelegate::~DeviceInquiryDelegate()
+{
+}
+
+}
+
+
+QT_END_NAMESPACE
+
+
+#ifdef QT_NAMESPACE
+// We do not want to litter a code with QT_PREPEND_NAMESPACE, right?
+using namespace QT_NAMESPACE;
+#endif
+
+
+@implementation QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry)
+
+- (id)initWithDelegate:(OSXBluetooth::DeviceInquiryDelegate *)delegate
+{
+ if (self = [super init]) {
+ Q_ASSERT_X(delegate != Q_NULLPTR, "-initWithDelegate:",
+ "invalid device inquiry delegate (null)");
+
+ m_inquiry = [[IOBluetoothDeviceInquiry inquiryWithDelegate:self] retain];
+
+ if (m_inquiry) {
+ // TODO: something more reasonable required!
+ m_inquiry.inquiryLength = 20;
+ m_delegate = delegate;
+ } else {
+ qCCritical(QT_BT_OSX) << "-initWithDelegate:, failed to create "
+ "a device inquiry";
+ }
+
+ m_active = false;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ // Noop if m_inquiry is nil.
+ [m_inquiry setDelegate:nil];
+ if (m_active)
+ [m_inquiry stop];
+ [m_inquiry release];
+
+ [super dealloc];
+}
+
+- (bool)isActive
+{
+ return m_active;
+}
+
+- (IOReturn)start
+{
+ if (!m_inquiry)
+ return kIOReturnNoPower;
+
+ if (m_active)
+ return kIOReturnBusy;
+
+ m_active = true;
+ [m_inquiry clearFoundDevices];// TODO: implement update?
+ const IOReturn result = [m_inquiry start];
+ if (result != kIOReturnSuccess)
+ m_active = false;
+
+ return result;
+}
+
+- (IOReturn)stop
+{
+ if (m_active) {
+ Q_ASSERT_X(m_inquiry != nil, "-stop",
+ "active but nil inquiry");
+
+ m_active = false;
+ const IOReturn res = [m_inquiry stop];
+ if (res != kIOReturnSuccess)
+ m_active = true;
+
+ return res;
+ }
+
+ return kIOReturnSuccess;
+}
+
+- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry *)sender
+ error:(IOReturn)error aborted:(BOOL)aborted
+{
+ Q_UNUSED(aborted)
+
+ if (sender != m_inquiry) // Can never happen in the current version.
+ return;
+
+ m_active = false;
+
+ Q_ASSERT_X(m_delegate != Q_NULLPTR, "-deviceInquiryComplete:error:aborted",
+ "invalid device inquiry delegate (null)");
+
+ if (error != kIOReturnSuccess)
+ m_delegate->error(sender, error);
+ else
+ m_delegate->inquiryFinished(sender);
+}
+
+- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *)sender
+ device:(IOBluetoothDevice *)device
+{
+ if (sender != m_inquiry) // Can never happen in the current version.
+ return;
+
+ Q_ASSERT_X(m_delegate != Q_NULLPTR, "-deviceInquiryDeviceFound:device:",
+ "invalid device inquiry delegate (null)");
+
+ m_delegate->deviceFound(sender, device);
+}
+
+- (void)deviceInquiryDeviceNameUpdated:(IOBluetoothDeviceInquiry *)sender
+ device:(IOBluetoothDevice*)device devicesRemaining:(uint32_t)devicesRemaining
+{
+ Q_UNUSED(sender)
+ Q_UNUSED(device)
+ Q_UNUSED(devicesRemaining)
+ // TODO: add names update into DeviceInquiryDelegate.
+}
+
+- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry *)sender
+{
+ Q_UNUSED(sender)
+}
+
+- (void)deviceInquiryUpdatingDeviceNamesStarted:(IOBluetoothDeviceInquiry *)sender
+ devicesRemaining:(uint32_t)devicesRemaining
+{
+ Q_UNUSED(sender)
+ Q_UNUSED(devicesRemaining)
+
+ // TODO: add names update into DeviceInquiryDelegate.
+}
+
+
+@end
diff --git a/src/bluetooth/osx/osxbtdeviceinquiry_p.h b/src/bluetooth/osx/osxbtdeviceinquiry_p.h
new file mode 100644
index 00000000..2eda032c
--- /dev/null
+++ b/src/bluetooth/osx/osxbtdeviceinquiry_p.h
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef OSXBTDEVICEINQUIRY_P_H
+#define OSXBTDEVICEINQUIRY_P_H
+
+#include <QtCore/qglobal.h>
+
+// We have to import objc code (it does not have inclusion guards).
+#import <IOBluetooth/objc/IOBluetoothDeviceInquiry.h>
+
+#include <Foundation/Foundation.h>
+#include <IOKit/IOReturn.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+class DeviceInquiryDelegate {
+public:
+ virtual ~DeviceInquiryDelegate();
+
+ virtual void inquiryFinished(IOBluetoothDeviceInquiry *inq) = 0;
+ virtual void error(IOBluetoothDeviceInquiry *inq, IOReturn error) = 0;
+ virtual void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) = 0;
+};
+
+}
+
+QT_END_NAMESPACE
+
+@interface QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry) : NSObject<IOBluetoothDeviceInquiryDelegate>
+{
+ IOBluetoothDeviceInquiry *m_inquiry;
+ bool m_active;
+ QT_PREPEND_NAMESPACE(OSXBluetooth::DeviceInquiryDelegate) *m_delegate;//C++ "delegate"
+}
+
+- (id)initWithDelegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::DeviceInquiryDelegate) *)delegate;
+- (void)dealloc;
+
+- (bool)isActive;
+- (IOReturn)start;
+- (IOReturn)stop;
+
+//Obj-C delegate:
+- (void)deviceInquiryComplete:(IOBluetoothDeviceInquiry *)sender
+ error:(IOReturn)error aborted:(BOOL)aborted;
+
+- (void)deviceInquiryDeviceFound:(IOBluetoothDeviceInquiry *)sender
+ device:(IOBluetoothDevice *)device;
+
+- (void)deviceInquiryDeviceNameUpdated:(IOBluetoothDeviceInquiry *)sender
+ device:(IOBluetoothDevice*)device devicesRemaining:(uint32_t)devicesRemaining;
+
+- (void)deviceInquiryStarted:(IOBluetoothDeviceInquiry *)sender;
+
+- (void)deviceInquiryUpdatingDeviceNamesStarted:(IOBluetoothDeviceInquiry *)sender
+ devicesRemaining:(uint32_t)devicesRemaining;
+
+@end
+
+#endif
diff --git a/src/bluetooth/osx/osxbtdevicepair.mm b/src/bluetooth/osx/osxbtdevicepair.mm
new file mode 100644
index 00000000..726df9a8
--- /dev/null
+++ b/src/bluetooth/osx/osxbtdevicepair.mm
@@ -0,0 +1,236 @@
+/****************************************************************************
+**
+** 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 "osxbtdevicepair_p.h"
+#include "osxbtutility_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qdebug.h>
+
+// Import to avoid problems with multiple inclusion (objc headers are not guarded against).
+#import <IOBluetooth/objc/IOBluetoothDevice.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+ObjCStrongReference<IOBluetoothDevice> device_with_address(const QBluetoothAddress &address)
+{
+ if (address.isNull())
+ return ObjCStrongReference<IOBluetoothDevice>(nil, false);
+
+ const BluetoothDeviceAddress &iobtAddress = iobluetooth_address(address);
+ ObjCStrongReference<IOBluetoothDevice> res([[IOBluetoothDevice deviceWithAddress:&iobtAddress] retain], false);
+ return res;
+}
+
+PairingDelegate::~PairingDelegate()
+{
+}
+
+}
+
+
+QT_END_NAMESPACE
+
+
+#ifdef QT_NAMESPACE
+using namespace QT_NAMESPACE;
+#endif
+
+@implementation QT_MANGLE_NAMESPACE(OSXBTPairing)
+
+- (id)initWithTarget:(const QBluetoothAddress &)address
+ delegate:(OSXBluetooth::PairingDelegate *)object
+{
+ if (self = [super init]) {
+ Q_ASSERT_X(address.isNull() == false, "-initWithTarget:delegate",
+ "invalid target address");
+ Q_ASSERT_X(object != Q_NULLPTR, "-initWithTarget:delegate:",
+ "invalid delegate (null)");
+
+ m_targetAddress = address;
+ m_object = object;
+ m_active = false;
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [m_pairing stop]; // Stop also sets a delegate to nil (Apple's docs).
+ [m_pairing release];
+
+ [super dealloc];
+}
+
+- (IOReturn) start
+{
+ if (m_active)
+ return kIOReturnBusy;
+
+ Q_ASSERT_X(m_targetAddress.isNull() == false, "-start",
+ "invalid target address");
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ const BluetoothDeviceAddress &iobtAddress = OSXBluetooth::iobluetooth_address(m_targetAddress);
+ // Device is autoreleased.
+ IOBluetoothDevice *const device = [IOBluetoothDevice deviceWithAddress:&iobtAddress];
+ if (!device) {
+ qCCritical(QT_BT_OSX) << "-start:, failed to create a device "
+ "to pair with";
+ return kIOReturnInternalError; // TODO: Find something more appropriate.
+ }
+
+ m_pairing = [[IOBluetoothDevicePair pairWithDevice:device] retain];
+ if (!m_pairing) {
+ qCCritical(QT_BT_OSX) << "-start, failed to create pair";
+ return kIOReturnInternalError;
+ }
+
+ [m_pairing setDelegate:self];
+ const IOReturn result = [m_pairing start];
+ if (result != kIOReturnSuccess) {
+ [m_pairing release];
+ m_pairing = nil;
+ } else
+ m_active = true;
+
+ return result;
+}
+
+- (bool)isActive
+{
+ return m_active;
+}
+
+- (void)stop
+{
+ // stop: stops pairing, removes the delegate
+ // and disconnects if device was connected.
+ if (m_pairing)
+ [m_pairing stop];
+}
+
+- (const QBluetoothAddress &)targetAddress
+{
+ return m_targetAddress;
+}
+
+- (IOBluetoothDevicePair *)pairingRequest
+{
+ return [[m_pairing retain] autorelease];
+}
+
+- (IOBluetoothDevice *)targetDevice
+{
+ return [m_pairing device];//It's retained/autoreleased by pair (?).
+}
+
+// IOBluetoothDevicePairDelegate:
+
+- (void)devicePairingStarted:(id)sender
+{
+ Q_UNUSED(sender)
+ //
+ NSLog(@"pairing started ... to be implemented");
+}
+
+- (void)devicePairingConnecting:(id)sender
+{
+ Q_UNUSED(sender)
+
+ NSLog(@"connecting ... to be implemented");
+}
+
+- (void)deviceParingPINCodeRequest:(id)sender
+{
+ Q_UNUSED(sender)
+
+ NSLog(@"pin code request ... to be implemented");
+}
+
+- (void)devicePairingUserConfirmationRequest:(id)sender
+ numericValue:(BluetoothNumericValue)numericValue
+{
+ if (sender != m_pairing) // Can never happen.
+ return;
+
+ Q_ASSERT_X(m_object != Q_NULLPTR, "-devicePairingUserConfirmationRequest:numericValue:",
+ "invalid delegate (null)");
+
+ m_object->requestUserConfirmation(self, numericValue);
+}
+
+- (void)devicePairingUserPasskeyNotification:(id)sender
+ passkey:(BluetoothPasskey)passkey
+{
+ Q_UNUSED(sender)
+ Q_UNUSED(passkey)
+
+ NSLog(@"pass key notification ... to be implemented");
+}
+
+- (void)devicePairingFinished:(id)sender error:(IOReturn)error
+{
+ Q_ASSERT_X(m_object != Q_NULLPTR, "-devicePairingFinished:",
+ "invalid delegate (null)");
+
+ if (sender != m_pairing) // Can never happen though.
+ return;
+
+ m_active = false;
+ if (error != kIOReturnSuccess)
+ m_object->error(self, error);
+ else
+ m_object->pairingFinished(self);
+}
+
+- (void)deviceSimplePairingComplete:(id)sender
+ status:(BluetoothHCIEventStatus)status
+{
+ Q_UNUSED(sender)
+ Q_UNUSED(status)
+}
+
+@end
diff --git a/src/bluetooth/osx/osxbtdevicepair_p.h b/src/bluetooth/osx/osxbtdevicepair_p.h
new file mode 100644
index 00000000..a3b8d77a
--- /dev/null
+++ b/src/bluetooth/osx/osxbtdevicepair_p.h
@@ -0,0 +1,132 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef OSXBTPAIRINGDELEGATE_P_H
+#define OSXBTPAIRINGDELEGATE_P_H
+
+#include "qbluetoothaddress.h"
+#include "osxbtutility_p.h"
+
+#include <QtCore/qglobal.h>
+
+#include <Foundation/Foundation.h>
+
+// This header is not guarded agains multiple includes ...
+// We have to "import".
+#import <IOBluetooth/objc/IOBluetoothDevicePair.h>
+
+@class QT_MANGLE_NAMESPACE(OSXBTPairing);
+@class IOBluetoothDevice;
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+// C++ delegate.
+class PairingDelegate
+{
+public:
+ typedef QT_MANGLE_NAMESPACE(OSXBTPairing) ObjCPairingRequest;
+
+ virtual ~PairingDelegate();
+
+ // virtual void pairingStarted(ObjCPairingRequest *pair) = 0;
+ virtual void connecting(ObjCPairingRequest *pair) = 0;
+ virtual void requestPIN(ObjCPairingRequest *pair) = 0;
+ virtual void requestUserConfirmation(ObjCPairingRequest *pair,
+ BluetoothNumericValue) = 0;
+ virtual void passkeyNotification(ObjCPairingRequest *pair,
+ BluetoothPasskey passkey) = 0;
+ virtual void error(ObjCPairingRequest *pair, IOReturn errorCode) = 0;
+ virtual void pairingFinished(ObjCPairingRequest *pair) = 0;
+};
+
+ObjCStrongReference<IOBluetoothDevice> device_with_address(const QBluetoothAddress &address);
+
+} // Namespace OSXBluetooth.
+
+QT_END_NAMESPACE
+
+@interface QT_MANGLE_NAMESPACE(OSXBTPairing) : NSObject<IOBluetoothDevicePairDelegate>
+{
+ // TODO: check how it works - C++ object as a member
+ QT_PREPEND_NAMESPACE(QBluetoothAddress) m_targetAddress;
+
+ bool m_active;
+ IOBluetoothDevicePair *m_pairing; // The real pairing request
+ QT_PREPEND_NAMESPACE(OSXBluetooth::PairingDelegate) *m_object;
+}
+
+- (id)initWithTarget:(const QBluetoothAddress &)address
+ delegate:(QT_PREPEND_NAMESPACE(OSXBluetooth::PairingDelegate) *)object;
+
+- (void)dealloc;
+
+- (IOReturn)start;
+- (bool)isActive;
+- (void)stop;
+
+- (const QBluetoothAddress &)targetAddress;
+- (IOBluetoothDevicePair *)pairingRequest;
+- (IOBluetoothDevice *)targetDevice;
+
+// IOBluetoothDevicePairDelegate:
+
+- (void)devicePairingStarted:(id)sender;
+- (void)devicePairingConnecting:(id)sender;
+- (void)deviceParingPINCodeRequest:(id)sender;
+
+- (void)devicePairingUserConfirmationRequest:(id)sender
+ numericValue:(BluetoothNumericValue)numericValue;
+
+- (void)devicePairingUserPasskeyNotification:(id)sender
+ passkey:(BluetoothPasskey)passkey;
+
+- (void)devicePairingFinished:(id)sender
+ error:(IOReturn)error;
+
+- (void)deviceSimplePairingComplete:(id)sender
+ status:(BluetoothHCIEventStatus)status;
+
+@end
+
+
+#endif
diff --git a/src/bluetooth/osx/osxbtutility.mm b/src/bluetooth/osx/osxbtutility.mm
new file mode 100644
index 00000000..14e8a947
--- /dev/null
+++ b/src/bluetooth/osx/osxbtutility.mm
@@ -0,0 +1,97 @@
+/****************************************************************************
+**
+** 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 "qbluetoothaddress.h"
+#include "osxbtutility_p.h"
+
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QT_BT_OSX, "qt.bluetooth.osx")
+
+namespace OSXBluetooth {
+
+QString qt_bt_address(NSString *address)
+{
+ if (address && address.length) {
+ NSString *const fixed = [address stringByReplacingOccurrencesOfString:@"-" withString:@":"];
+ return QString::fromNSString(fixed);
+ }
+
+ return QString();
+}
+
+QBluetoothAddress qt_bt_address(const BluetoothDeviceAddress *a)
+{
+ if (a) {
+ // TODO: can a byte order be different in BluetoothDeviceAddress?
+ const quint64 qAddress = a->data[5] |
+ qint64(a->data[4]) << 8 |
+ qint64(a->data[3]) << 16 |
+ qint64(a->data[2]) << 24 |
+ qint64(a->data[1]) << 32 |
+ qint64(a->data[0]) << 40;
+ return QBluetoothAddress(qAddress);
+ }
+
+ return QBluetoothAddress();
+}
+
+BluetoothDeviceAddress iobluetooth_address(const QBluetoothAddress &qAddress)
+{
+ BluetoothDeviceAddress a = {};
+ if (!qAddress.isNull()) {
+ const quint64 val = qAddress.toUInt64();
+ a.data[0] = (val >> 40) & 0xff;
+ a.data[1] = (val >> 32) & 0xff;
+ a.data[2] = (val >> 24) & 0xff;
+ a.data[3] = (val >> 16) & 0xff;
+ a.data[4] = (val >> 8) & 0xff;
+ a.data[5] = val & 0xff;
+ }
+
+ return a;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/osx/osxbtutility_p.h b/src/bluetooth/osx/osxbtutility_p.h
new file mode 100644
index 00000000..cf267fc9
--- /dev/null
+++ b/src/bluetooth/osx/osxbtutility_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** 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$
+**
+****************************************************************************/
+
+#ifndef OSXBTUTILITY_P_H
+#define OSXBTUTILITY_P_H
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qglobal.h>
+
+#include <Foundation/Foundation.h>
+#include <IOBluetooth/Bluetooth.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace OSXBluetooth {
+
+struct NSObjectDeleter {
+ static void cleanup(NSObject *obj)
+ {
+ [obj release];
+ }
+};
+
+template<class T>
+class ObjCScopedPointer : public QScopedPointer<NSObject, NSObjectDeleter>
+{
+public:
+ explicit ObjCScopedPointer(T *ptr = Q_NULLPTR) : QScopedPointer(ptr){}
+ operator T*() const
+ {
+ return static_cast<T *>(this->data());
+ }
+};
+
+typedef ObjCScopedPointer<NSAutoreleasePool> AutoreleasePool;
+#define QT_BT_MAC_AUTORELEASEPOOL const OSXBluetooth::AutoreleasePool pool([[NSAutoreleasePool alloc] init])
+
+template<class T>
+class ObjCStrongReference {
+public:
+ explicit ObjCStrongReference(T *obj, bool retain)
+ {
+ if (retain)
+ m_ptr = [obj retain];
+ else
+ m_ptr = obj; // For example, created with initWithXXXX.
+ }
+ ObjCStrongReference(const ObjCStrongReference &rhs)
+ {
+ m_ptr = [rhs.m_ptr retain];
+ }
+ ObjCStrongReference &operator = (const ObjCStrongReference &rhs)
+ {
+ // "Old-style" implementation:
+ if (this != &rhs && m_ptr != rhs.m_ptr) {
+ [m_ptr release];
+ m_ptr = [rhs.m_ptr retain];
+ }
+
+ return *this;
+ }
+
+#ifdef Q_COMPILER_RVALUE_REFS
+ ObjCStrongReference(ObjCStrongReference &&xval)
+ {
+ m_ptr = xval.m_ptr;
+ xval.m_ptr = nil;
+ }
+
+ ObjCStrongReference &operator = (ObjCStrongReference &&xval)
+ {
+ m_ptr = xval.m_ptr;
+ xval.m_ptr = nil;
+ return *this;
+ }
+#endif
+
+ ~ObjCStrongReference()
+ {
+ [m_ptr release];
+ }
+
+ void reset(T *newVal)
+ {
+ if (m_ptr != newVal) {
+ [m_ptr release];
+ m_ptr = [newVal retain];
+ }
+ }
+
+ operator T *() const
+ {
+ return m_ptr;
+ }
+private:
+ T *m_ptr;
+};
+
+QString qt_bt_address(NSString *address);
+class QBluetoothAddress qt_bt_address(const BluetoothDeviceAddress *address);
+BluetoothDeviceAddress iobluetooth_address(const QBluetoothAddress &address);
+
+} // namespace OSXBluetooth
+
+Q_DECLARE_LOGGING_CATEGORY(QT_BT_OSX)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm
new file mode 100644
index 00000000..e7eb3c60
--- /dev/null
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_osx.mm
@@ -0,0 +1,404 @@
+/****************************************************************************
+**
+** 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 "qbluetoothdevicediscoveryagent.h"
+#include "osx/osxbtdeviceinquiry_p.h"
+#include "qbluetoothlocaldevice.h"
+#include "qbluetoothdeviceinfo.h"
+#include "osx/osxbtutility_p.h"
+#include "qbluetoothhostinfo.h"
+#include "qbluetoothuuid.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+
+// We have to import, otherwise Apple's header is not protected
+// from multiple inclusion.
+#import <IOBluetooth/objc/IOBluetoothHostController.h>
+#import <IOBluetooth/objc/IOBluetoothDevice.h>
+
+// TODO: check how all these things work with threads.
+
+QT_BEGIN_NAMESPACE
+
+using OSXBluetooth::ObjCScopedPointer;
+
+class QBluetoothDeviceDiscoveryAgentPrivate : public OSXBluetooth::DeviceInquiryDelegate
+{
+ friend class QBluetoothDeviceDiscoveryAgent;
+public:
+ QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress & address,
+ QBluetoothDeviceDiscoveryAgent *q);
+ virtual ~QBluetoothDeviceDiscoveryAgentPrivate(); // Just to make compiler happy.
+
+ bool isValid() const;
+ bool isActive() const;
+
+ void start();
+ void stop();
+
+private:
+ // DeviceInquiryDelegate:
+ void inquiryFinished(IOBluetoothDeviceInquiry *inq) Q_DECL_OVERRIDE;
+ void error(IOBluetoothDeviceInquiry *inq, IOReturn error) Q_DECL_OVERRIDE;
+ void deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device) Q_DECL_OVERRIDE;
+
+ void setError(IOReturn error, const QString &text = QString());
+ void setError(QBluetoothDeviceDiscoveryAgent::Error, const QString &text = QString());
+
+ QBluetoothDeviceDiscoveryAgent *q_ptr;
+
+ QBluetoothAddress adapterAddress;
+
+ bool startPending;
+ bool stopPending;
+
+ QBluetoothDeviceDiscoveryAgent::Error lastError;
+ QString errorString;
+
+ QBluetoothDeviceDiscoveryAgent::InquiryType inquiryType;
+
+ typedef QT_MANGLE_NAMESPACE(OSXBTDeviceInquiry) DeviceInquiryObjC;
+ typedef ObjCScopedPointer<DeviceInquiryObjC> DeviceInquiry;
+ DeviceInquiry inquiry;
+
+ typedef ObjCScopedPointer<IOBluetoothHostController> HostController;
+ HostController hostController; // Not sure I need it at all.
+
+ typedef QList<QBluetoothDeviceInfo> DevicesList;
+ DevicesList discoveredDevices;
+};
+
+QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(const QBluetoothAddress &adapter,
+ QBluetoothDeviceDiscoveryAgent *q) :
+ q_ptr(q),
+ adapterAddress(adapter),
+ startPending(false),
+ stopPending(false),
+ lastError(QBluetoothDeviceDiscoveryAgent::NoError),
+ inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry)
+{
+ Q_ASSERT_X(q != Q_NULLPTR, "QBluetoothDeviceDiscoveryAgentPrivate()",
+ "invalid q_ptr (null)");
+
+ HostController controller([[IOBluetoothHostController defaultController] retain]);
+ if (!controller) {
+ qCCritical(QT_BT_OSX) << "QBluetoothDeviceDiscoveryAgentPrivate() "
+ "no default host controller";
+ setError(QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError);
+ return;
+ }
+
+ DeviceInquiry newInquiry([[DeviceInquiryObjC alloc]initWithDelegate:this]);
+ if (!newInquiry) { // Obj-C's way of "reporting errors":
+ qCCritical(QT_BT_OSX) << "QBluetoothDeviceDiscoveryAgentPrivate() "
+ "failed to initialize an inquiry";
+ setError(QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError);
+ return;
+ }
+
+ hostController.reset(controller.take());
+ inquiry.reset(newInquiry.take());
+}
+
+QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate()
+{
+}
+
+bool QBluetoothDeviceDiscoveryAgentPrivate::isValid() const
+{
+ // isValid() - Qt in general does not use exceptions, but the ctor
+ // can fail to initialize some important data-members
+ // (and the error is probably not even related to Bluetooth at all)
+ // - say, allocation error - this is what meant here by valid/invalid.
+ return hostController && inquiry;
+}
+
+bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const
+{
+ if (startPending)
+ return true;
+ if (stopPending)
+ return false;
+
+ return [inquiry isActive];
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::start()
+{
+ Q_ASSERT_X(!isActive(), "start()", "called on active device discovery agent");
+ Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError,
+ "start()", "called with invalid bluetooth adapter");
+
+ if (stopPending) {
+ startPending = true;
+ return;
+ }
+
+ discoveredDevices.clear();
+ setError(QBluetoothDeviceDiscoveryAgent::NoError);
+
+ const IOReturn res = [inquiry start];
+ if (res != kIOReturnSuccess) {
+ setError(res, QObject::tr("device discovery agent: failed to start"));
+ emit q_ptr->error(lastError);
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::stop()
+{
+ Q_ASSERT_X(isActive(), "stop()", "called whithout active inquiry");
+ Q_ASSERT_X(lastError != QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError,
+ "stop()", "called with invalid bluetooth adapter");
+
+ const bool prevStart = startPending;
+ startPending = false;
+
+ setError(QBluetoothDeviceDiscoveryAgent::NoError);
+
+ if ([inquiry isActive]) {
+ stopPending = true;
+ const IOReturn res = [inquiry stop];
+ if (res != kIOReturnSuccess) {
+ qCWarning(QT_BT_OSX) << "QBluetoothDeviceDiscoveryAgentPrivate::stop(), "
+ "failed to stop";
+ startPending = prevStart;
+ stopPending = false;
+ setError(res, QObject::tr("device discovery agent: failed to stop"));
+ emit q_ptr->error(lastError);
+ }
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::inquiryFinished(IOBluetoothDeviceInquiry *inq)
+{
+ Q_UNUSED(inq)
+
+ Q_ASSERT_X(isValid(), "inquiryFinished", "invalid device discovery agent"); //We can never be here.
+ Q_ASSERT_X(inq, "inquiryFinished", "invalid device inquiry (nil)");
+ Q_ASSERT_X(q_ptr, "inquiryFinished", "invalid q_ptr (null)");
+
+ if (stopPending && !startPending) {
+ stopPending = false;
+ emit q_ptr->canceled();
+ } else if (startPending) {
+ startPending = false;
+ stopPending = false;
+ start();
+ } else {
+ emit q_ptr->finished();
+ }
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::error(IOBluetoothDeviceInquiry *inq, IOReturn error)
+{
+ Q_UNUSED(inq)
+
+ Q_ASSERT_X(isValid(), "error", "invalid device discovery agent");
+
+ startPending = false;
+ stopPending = false;
+
+ setError(error);
+
+ Q_ASSERT_X(q_ptr, "error", "invalid q_ptr (null)");
+ emit q_ptr->error(lastError);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(IOBluetoothDeviceInquiry *inq, IOBluetoothDevice *device)
+{
+ Q_UNUSED(inq)
+
+ Q_ASSERT_X(isValid(), "deviceFound()",
+ "invalid device discovery agent");
+ Q_ASSERT_X(device, "deviceFound()", "invalid IOBluetoothDevice (nil)");
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ // Let's collect some info about this device:
+ const QBluetoothAddress deviceAddress(OSXBluetooth::qt_bt_address([device getAddress]));
+ if (deviceAddress.isNull()) {
+ qCWarning(QT_BT_OSX) << "QBluetoothDeviceDiscoveryAgentPrivate::deviceFound(), "
+ "invalid Bluetooth address";
+ return;
+ }
+
+ QString deviceName;
+ if (device.name)
+ deviceName = QString::fromNSString(device.name);
+
+ const qint32 classOfDevice(device.classOfDevice);
+
+ QBluetoothDeviceInfo deviceInfo(deviceAddress, deviceName, classOfDevice);
+ deviceInfo.setCoreConfigurations(classOfDevice ? QBluetoothDeviceInfo::BaseRateCoreConfiguration:
+ QBluetoothDeviceInfo::LowEnergyCoreConfiguration);
+ deviceInfo.setRssi(device.RSSI);
+
+ // TODO: check if I can extract services' uuids from device.services
+ // and use them.
+ deviceInfo.setServiceUuids(QList<QBluetoothUuid>(),
+ QBluetoothDeviceInfo::DataIncomplete);
+
+ for (int i = 0, e = discoveredDevices.size(); i < e; ++i) {
+ if (discoveredDevices[i].address() == deviceInfo.address()) {
+ if (discoveredDevices[i] == deviceInfo)
+ return;
+ discoveredDevices.replace(i, deviceInfo);
+ emit q_ptr->deviceDiscovered(deviceInfo);
+ return;
+ }
+ }
+
+ discoveredDevices.append(deviceInfo);
+ emit q_ptr->deviceDiscovered(deviceInfo);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::setError(IOReturn error, const QString &text)
+{
+ if (error == kIOReturnSuccess)
+ setError(QBluetoothDeviceDiscoveryAgent::NoError, text);
+ else if (error == kIOReturnNoPower)
+ setError(QBluetoothDeviceDiscoveryAgent::PoweredOffError, text);
+ else
+ setError(QBluetoothDeviceDiscoveryAgent::UnknownError, text);
+}
+
+void QBluetoothDeviceDiscoveryAgentPrivate::setError(QBluetoothDeviceDiscoveryAgent::Error error,
+ const QString &text)
+{
+ lastError = error;
+
+ if (text.length() > 0) {
+ errorString = text;
+ } else {
+ switch (lastError) {
+ case QBluetoothDeviceDiscoveryAgent::NoError:
+ errorString = QString();
+ break;
+ case QBluetoothDeviceDiscoveryAgent::PoweredOffError:
+ errorString = QObject::tr("device discovery agent: adapter is powered off");
+ break;
+ case QBluetoothDeviceDiscoveryAgent::InvalidBluetoothAdapterError:
+ errorString = QObject::tr("device discovery agent: invalid bluetooth adapter");
+ break;
+ case QBluetoothDeviceDiscoveryAgent::InputOutputError:
+ errorString = QObject::tr("device discovery agent: input output error");
+ break;
+ case QBluetoothDeviceDiscoveryAgent::UnknownError:
+ default:
+ errorString = QObject::tr("device discovery agent: unknown error");
+ }
+ }
+}
+
+QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(QBluetoothAddress(), this))
+{
+}
+
+QBluetoothDeviceDiscoveryAgent::QBluetoothDeviceDiscoveryAgent(
+ const QBluetoothAddress &deviceAdapter, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothDeviceDiscoveryAgentPrivate(deviceAdapter, this))
+{
+ if (!deviceAdapter.isNull()) {
+ const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices();
+ foreach (const QBluetoothHostInfo &hostInfo, localDevices) {
+ if (hostInfo.address() == deviceAdapter)
+ return;
+ }
+ d_ptr->setError(InvalidBluetoothAdapterError);
+ }
+}
+
+QBluetoothDeviceDiscoveryAgent::~QBluetoothDeviceDiscoveryAgent()
+{
+ delete d_ptr;
+}
+
+QBluetoothDeviceDiscoveryAgent::InquiryType QBluetoothDeviceDiscoveryAgent::inquiryType() const
+{
+ Q_D(const QBluetoothDeviceDiscoveryAgent);
+ return d->inquiryType;
+}
+
+void QBluetoothDeviceDiscoveryAgent::setInquiryType(QBluetoothDeviceDiscoveryAgent::InquiryType type)
+{
+ Q_D(QBluetoothDeviceDiscoveryAgent);
+ d->inquiryType = type;
+}
+
+QList<QBluetoothDeviceInfo> QBluetoothDeviceDiscoveryAgent::discoveredDevices() const
+{
+ return d_ptr->discoveredDevices;
+}
+
+void QBluetoothDeviceDiscoveryAgent::start()
+{
+ if (!isActive() && d_ptr->lastError != InvalidBluetoothAdapterError)
+ d_ptr->start();
+}
+
+void QBluetoothDeviceDiscoveryAgent::stop()
+{
+ if (isActive() && d_ptr->lastError != InvalidBluetoothAdapterError)
+ d_ptr->stop();
+}
+
+bool QBluetoothDeviceDiscoveryAgent::isActive() const
+{
+ return d_ptr->isActive();
+}
+
+QBluetoothDeviceDiscoveryAgent::Error QBluetoothDeviceDiscoveryAgent::error() const
+{
+ return d_ptr->lastError;
+}
+
+QString QBluetoothDeviceDiscoveryAgent::errorString() const
+{
+ return d_ptr->errorString;
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothlocaldevice.cpp b/src/bluetooth/qbluetoothlocaldevice.cpp
index fa700c6a..0e2ef1ab 100644
--- a/src/bluetooth/qbluetoothlocaldevice.cpp
+++ b/src/bluetooth/qbluetoothlocaldevice.cpp
@@ -91,6 +91,7 @@ QT_BEGIN_NAMESPACE
only made discoverable for a limited period of time. This can speed up discovery between gaming devices,
as service discovery can be skipped on devices not in LimitedInquiry mode. In this mode, the device will
be connectable and powered on, if required. This mode is is not supported on Android.
+ On OS X, it is not possible to set the \l hostMode() to HostConnectable or HostPoweredOff.
*/
@@ -107,6 +108,9 @@ public:
} _registerLocalDeviceMetaTypes;
}
+
+#ifndef QT_OSX_BLUETOOTH
+
/*!
Destroys the QBluetoothLocalDevice.
*/
@@ -134,6 +138,8 @@ bool QBluetoothLocalDevice::isValid() const
return false;
}
+#endif
+
/*!
\fn void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
@@ -142,13 +148,16 @@ bool QBluetoothLocalDevice::isValid() const
\note Due to varying security policies on the supported platforms, this method may have
differing behaviors on the various platforms. For example the system may ask the user for
confirmation before turning Bluetooth on or off and not all host modes may be supported.
+ On OS X, it is not possbile to programmatically change the \l hostMode().
+ A user can only switch Bluetooth on/off in the System Preferences.
Please refer to the platform specific Bluetooth documentation for details.
*/
/*!
\fn QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
- Returns the current host mode of this local Bluetooth device.
+ Returns the current host mode of this local Bluetooth device. On OS X, it is either
+ HostPoweredOff or HostConnectable.
*/
/*!
@@ -166,7 +175,8 @@ bool QBluetoothLocalDevice::isValid() const
/*!
\fn QList<QBluetoothLocalDevice> QBluetoothLocalDevice::allDevices()
- Returns a list of all available local Bluetooth devices.
+ Returns a list of all available local Bluetooth devices. On OS X, there is
+ only the "default" local device.
*/
/*!
@@ -177,6 +187,7 @@ bool QBluetoothLocalDevice::isValid() const
\note Due to varying security policies on the supported platforms, this method may have
differing behaviors on the various platforms. For example
the system may ask the user for confirmation before turning Bluetooth on or off.
+ On OS X it is not possible to power on/off Bluetooth.
Please refer to the platform specific Bluetooth documentation for details.
*/
@@ -217,7 +228,7 @@ bool QBluetoothLocalDevice::isValid() const
Returns the list of connected devices. This list is different from the list of currently
paired devices.
- On Android it is not possible to retrieve a list of connected devices. It is only possible to
+ On Android and OS X, it is not possible to retrieve a list of connected devices. It is only possible to
listen to (dis)connect changes. For convenience, this class monitors all connect
and disconnect events since its instanciation and returns the current list when calling this function.
Therefore it is possible that this function returns an empty list shortly after creating an
@@ -240,6 +251,8 @@ bool QBluetoothLocalDevice::isValid() const
must be called to indicate if the user accepts or rejects the displayed pin.
This signal is only emitted for pairing requests issues by calling \l requestPairing().
+ On OS X, this method never gets called - there is a callback with a PIN (IOBluetooth),
+ but it expects immediate reply yes/no - and there is no time to show any dialog or compare PINs.
\sa pairingConfirmation()
*/
@@ -267,6 +280,11 @@ bool QBluetoothLocalDevice::isValid() const
Set the \a pairing status with \a address. The results are returned by the signal, pairingFinished().
On BlackBerry AuthorizedPaired is not possible and will have the same behavior as Paired.
+
+ On OS X, it is not possible to unpair a device. If Unpaired is requested, \l pairingFinished()
+ is immediately emitted although the device remains paired. It is possible to request the pairing
+ for a previously unpaired device. In addition \l AuthorizedPaired has the same behavior as \l Paired.
+
Caution: creating a pairing may take minutes, and may require the user to acknowledge.
*/
diff --git a/src/bluetooth/qbluetoothlocaldevice_osx.mm b/src/bluetooth/qbluetoothlocaldevice_osx.mm
new file mode 100644
index 00000000..6a222ae6
--- /dev/null
+++ b/src/bluetooth/qbluetoothlocaldevice_osx.mm
@@ -0,0 +1,499 @@
+/****************************************************************************
+**
+** 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/osxbtconnectionmonitor_p.h"
+#include "qbluetoothlocaldevice_p.h"
+#include "qbluetoothlocaldevice.h"
+#include "osx/osxbtdevicepair_p.h"
+#include "osx/osxbtutility_p.h"
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qmap.h>
+
+// We have to import, not include. Obj-C headers are not protected
+// against a multiple inclusion.
+#import <IOBluetooth/objc/IOBluetoothHostController.h>
+#import <IOBluetooth/objc/IOBluetoothDevice.h>
+
+#include <algorithm>
+
+// TODO: check how all these things work with threads.
+
+QT_BEGIN_NAMESPACE
+
+class QBluetoothLocalDevicePrivate : public OSXBluetooth::PairingDelegate,
+ public OSXBluetooth::ConnectionMonitor
+{
+ friend class QBluetoothLocalDevice;
+public:
+ typedef QBluetoothLocalDevice::Pairing Pairing;
+
+ QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *, const QBluetoothAddress & =
+ QBluetoothAddress());
+
+ bool isValid() const;
+ void requestPairing(const QBluetoothAddress &address, Pairing pairing);
+ Pairing pairingStatus(const QBluetoothAddress &address) const;
+
+private:
+
+ // PairingDelegate:
+ void connecting(ObjCPairingRequest *pair) Q_DECL_OVERRIDE;
+ void requestPIN(ObjCPairingRequest *pair) Q_DECL_OVERRIDE;
+ void requestUserConfirmation(ObjCPairingRequest *pair,
+ BluetoothNumericValue) Q_DECL_OVERRIDE;
+ void passkeyNotification(ObjCPairingRequest *pair,
+ BluetoothPasskey passkey) Q_DECL_OVERRIDE;
+ void error(ObjCPairingRequest *pair, IOReturn errorCode) Q_DECL_OVERRIDE;
+ void pairingFinished(ObjCPairingRequest *pair) Q_DECL_OVERRIDE;
+
+ // ConnectionMonitor
+ void deviceConnected(const QBluetoothAddress &deviceAddress) Q_DECL_OVERRIDE;
+ void deviceDisconnected(const QBluetoothAddress &deviceAddress) Q_DECL_OVERRIDE;
+
+ void emitPairingFinished(const QBluetoothAddress &deviceAddress, Pairing pairing, bool queued);
+ void emitError(QBluetoothLocalDevice::Error error, bool queued);
+
+ void unpair(const QBluetoothAddress &deviceAddress);
+
+ QBluetoothLocalDevice *q_ptr;
+
+ typedef OSXBluetooth::ObjCScopedPointer<IOBluetoothHostController> HostController;
+ HostController hostController;
+
+ typedef OSXBluetooth::ObjCStrongReference<ObjCPairingRequest> PairingRequest;
+ typedef QMap<QBluetoothAddress, PairingRequest> RequestMap;
+
+ RequestMap pairingRequests;
+
+ OSXBluetooth::ObjCScopedPointer<ObjCConnectionMonitor> connectionMonitor;
+ QList<QBluetoothAddress> discoveredDevices;
+};
+
+QBluetoothLocalDevicePrivate::QBluetoothLocalDevicePrivate(QBluetoothLocalDevice *q,
+ const QBluetoothAddress &address) :
+ q_ptr(q)
+{
+ Q_ASSERT_X(q, "QBluetoothLocalDevicePrivate", "invalid q_ptr (null)");
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ HostController defaultController([[IOBluetoothHostController defaultController] retain]);
+ if (!defaultController) {
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevicePrivate(), failed to "
+ "init a host controller object";
+ return;
+ }
+
+ if (!address.isNull()) {
+ NSString *const hciAddress = [defaultController addressAsString];
+ if (!hciAddress) {
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevicePrivate(), "
+ "failed to obtain an address";
+ return;
+ }
+
+ if (address.toString() != QString::fromNSString(hciAddress)) {
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevicePrivate(), "
+ "invalid local device's address";
+ return;
+ }
+ }
+
+ hostController.reset(defaultController.take());
+
+ // This one is optional, if it fails to initialize, we do not care at all.
+ connectionMonitor.reset([[ObjCConnectionMonitor alloc] initWithMonitor:this]);
+}
+
+bool QBluetoothLocalDevicePrivate::isValid() const
+{
+ return hostController;
+}
+
+void QBluetoothLocalDevicePrivate::requestPairing(const QBluetoothAddress &address, Pairing pairing)
+{
+ Q_ASSERT_X(isValid(), "requestPairing()", "invalid local device");
+ Q_ASSERT_X(!address.isNull(), "requestPairing()", "invalid device address");
+
+ using OSXBluetooth::device_with_address;
+ using OSXBluetooth::ObjCStrongReference;
+
+ // That's a really special case on OS X.
+ if (pairing == QBluetoothLocalDevice::Unpaired)
+ return unpair(address);
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ if (pairing == QBluetoothLocalDevice::AuthorizedPaired)
+ pairing = QBluetoothLocalDevice::Paired;
+
+ RequestMap::iterator pos = pairingRequests.find(address);
+ if (pos != pairingRequests.end()) {
+ if ([pos.value() isActive]) // Still trying to pair, continue.
+ return;
+
+ // 'device' is autoreleased:
+ IOBluetoothDevice *const device = [pos.value() targetDevice];
+ if ([device isPaired]) {
+ emitPairingFinished(address, pairing, true);
+ } else if ([pos.value() start] != kIOReturnSuccess) {
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevicePrivate::requestPairing(), "
+ "failed to start a new pairing request";
+ emitError(QBluetoothLocalDevice::PairingError, true);
+ }
+ return;
+ }
+
+ // That's a totally new request ('Paired', since we are here).
+ // Even if this device is paired (not by our local device), I still create a pairing request,
+ // it'll just finish with success (skipping any intermediate steps).
+ PairingRequest newRequest([[ObjCPairingRequest alloc] initWithTarget:address delegate:this], false);
+ if (!newRequest) {
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevicePrivate::requestPairing(), "
+ "failed to allocate a new pairing request";
+ emitError(QBluetoothLocalDevice::PairingError, true);
+ return;
+ }
+
+ pos = pairingRequests.insert(address, newRequest);
+ const IOReturn result = [newRequest start];
+ if (result != kIOReturnSuccess) {
+ pairingRequests.erase(pos);
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevicePrivate::requestPairing(), "
+ "failed to start a new pairing request";
+ emitError(QBluetoothLocalDevice::PairingError, true);
+ }
+}
+
+QBluetoothLocalDevice::Pairing QBluetoothLocalDevicePrivate::pairingStatus(const QBluetoothAddress &address)const
+{
+ Q_ASSERT_X(isValid(), "pairingStatus", "invalid local device");
+ Q_ASSERT_X(!address.isNull(), "pairingStatus", "invalid address");
+
+ using OSXBluetooth::device_with_address;
+ using OSXBluetooth::ObjCStrongReference;
+
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ RequestMap::const_iterator it = pairingRequests.find(address);
+ if (it != pairingRequests.end()) {
+ // All Obj-C objects are autoreleased.
+ IOBluetoothDevice *const device = [it.value() targetDevice];
+ if (device && [device isPaired])
+ return QBluetoothLocalDevice::Paired;
+ } else {
+ // Try even if device was not paired by this local device ...
+ const ObjCStrongReference<IOBluetoothDevice> device(device_with_address(address));
+ if (device && [device isPaired])
+ return QBluetoothLocalDevice::Paired;
+ }
+
+ return QBluetoothLocalDevice::Unpaired;
+}
+
+void QBluetoothLocalDevicePrivate::connecting(ObjCPairingRequest *pair)
+{
+ Q_UNUSED(pair)
+}
+
+void QBluetoothLocalDevicePrivate::requestPIN(ObjCPairingRequest *pair)
+{
+ Q_UNUSED(pair)
+}
+
+void QBluetoothLocalDevicePrivate::requestUserConfirmation(ObjCPairingRequest *pair, BluetoothNumericValue intPin)
+{
+ Q_UNUSED(pair)
+ Q_UNUSED(intPin)
+}
+
+void QBluetoothLocalDevicePrivate::passkeyNotification(ObjCPairingRequest *pair,
+ BluetoothPasskey passkey)
+{
+ Q_UNUSED(pair)
+ Q_UNUSED(passkey)
+}
+
+void QBluetoothLocalDevicePrivate::error(ObjCPairingRequest *pair, IOReturn errorCode)
+{
+ Q_UNUSED(pair)
+ Q_UNUSED(errorCode)
+ // TODO: map from IOReturn to QBluetoothLocalDevice::Error.
+ // TODO: emit or invokeMethod???
+ emitError(QBluetoothLocalDevice::PairingError, false);
+}
+
+void QBluetoothLocalDevicePrivate::pairingFinished(ObjCPairingRequest *pair)
+{
+ Q_ASSERT_X(pair, "QBluetoothLocalDevicePrivate::pairingFinished()",
+ "invalid pairing request (nil)");
+
+ const QBluetoothAddress &deviceAddress = [pair targetAddress];
+ Q_ASSERT_X(!deviceAddress.isNull(), "pairingFinished()",
+ "invalid target address");
+
+ emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Paired, false);
+}
+
+void QBluetoothLocalDevicePrivate::deviceConnected(const QBluetoothAddress &deviceAddress)
+{
+ Q_ASSERT_X(q_ptr, "deviceConnected()", "invalid q_ptr (null)");
+
+ if (!discoveredDevices.contains(deviceAddress))
+ discoveredDevices.append(deviceAddress);
+
+ QMetaObject::invokeMethod(q_ptr, "deviceConnected", Qt::QueuedConnection,
+ Q_ARG(QBluetoothAddress, deviceAddress));
+}
+
+void QBluetoothLocalDevicePrivate::deviceDisconnected(const QBluetoothAddress &deviceAddress)
+{
+ Q_ASSERT_X(q_ptr, "deviceDisconnected()", "invalid q_ptr (null)");
+
+ QList<QBluetoothAddress>::iterator devicePos =std::find(discoveredDevices.begin(),
+ discoveredDevices.end(),
+ deviceAddress);
+
+ if (devicePos != discoveredDevices.end())
+ discoveredDevices.erase(devicePos);
+
+ QMetaObject::invokeMethod(q_ptr, "deviceDisconnected", Qt::QueuedConnection,
+ Q_ARG(QBluetoothAddress, deviceAddress));
+}
+
+void QBluetoothLocalDevicePrivate::emitError(QBluetoothLocalDevice::Error error, bool queued)
+{
+ Q_ASSERT_X(q_ptr, "QBluetoothLocalDevicePrivate::error()",
+ "invalid q_ptr (null)");
+
+ if (queued) {
+ QMetaObject::invokeMethod(q_ptr, "error", Qt::QueuedConnection,
+ Q_ARG(QBluetoothLocalDevice::Error, error));
+ } else {
+ emit q_ptr->error(QBluetoothLocalDevice::PairingError);
+ }
+}
+
+void QBluetoothLocalDevicePrivate::emitPairingFinished(const QBluetoothAddress &deviceAddress,
+ Pairing pairing, bool queued)
+{
+ Q_ASSERT_X(!deviceAddress.isNull(), "pairingFinished()", "invalid target device address");
+ Q_ASSERT_X(q_ptr, "pairingFinished()", "invalid q_ptr (null)");
+
+ if (queued) {
+ QMetaObject::invokeMethod(q_ptr, "pairingFinished", Qt::QueuedConnection,
+ Q_ARG(QBluetoothAddress, deviceAddress),
+ Q_ARG(QBluetoothLocalDevice::Pairing, pairing));
+ } else {
+ emit q_ptr->pairingFinished(deviceAddress, pairing);
+ }
+}
+
+void QBluetoothLocalDevicePrivate::unpair(const QBluetoothAddress &deviceAddress)
+{
+ Q_ASSERT_X(!deviceAddress.isNull(), "unpair()",
+ "invalid target address");
+
+ emitPairingFinished(deviceAddress, QBluetoothLocalDevice::Unpaired, true);
+}
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this))
+{
+}
+
+QBluetoothLocalDevice::QBluetoothLocalDevice(const QBluetoothAddress &address, QObject *parent) :
+ QObject(parent),
+ d_ptr(new QBluetoothLocalDevicePrivate(this, address))
+{
+}
+
+QBluetoothLocalDevice::~QBluetoothLocalDevice()
+{
+ delete d_ptr;
+}
+
+bool QBluetoothLocalDevice::isValid() const
+{
+ return d_ptr->isValid();
+}
+
+
+QString QBluetoothLocalDevice::name() const
+{
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ if (isValid()) {
+ if (NSString *const nsn = [d_ptr->hostController nameAsString])
+ return QString::fromNSString(nsn);
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevice::name(), "
+ "failed to obtain a name";
+ }
+
+ return QString();
+}
+
+QBluetoothAddress QBluetoothLocalDevice::address() const
+{
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ if (isValid()) {
+ if (NSString *const nsa = [d_ptr->hostController addressAsString])
+ return QBluetoothAddress(OSXBluetooth::qt_bt_address(nsa));
+
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevice::address(), "
+ "failed to obtain an address";
+ } else {
+ qCWarning(QT_BT_OSX) << "QBluetoothLocalDevice::address(), "
+ "invalid local device";
+ }
+
+ return QBluetoothAddress();
+}
+
+void QBluetoothLocalDevice::powerOn()
+{
+ if (!isValid()) {
+ qCWarning(QT_BT_OSX) << "QBluetoothLocalDevice::powerOn() "
+ "invalid local device";
+ }
+}
+
+void QBluetoothLocalDevice::setHostMode(QBluetoothLocalDevice::HostMode mode)
+{
+ Q_UNUSED(mode)
+
+ if (!isValid()) {
+ qCWarning(QT_BT_OSX) << "QBluetoothLovalDevice::setHostMode() "
+ "invalid local device";
+ }
+}
+
+QBluetoothLocalDevice::HostMode QBluetoothLocalDevice::hostMode() const
+{
+ if (!isValid() || ![d_ptr->hostController powerState])
+ return HostPoweredOff;
+
+ return HostConnectable;
+}
+
+QList<QBluetoothAddress> QBluetoothLocalDevice::connectedDevices() const
+{
+ QT_BT_MAC_AUTORELEASEPOOL;
+
+ QList<QBluetoothAddress> connectedDevices;
+
+ // Take the devices known to IOBluetooth to be paired and connected first:
+ NSArray *const pairedDevices = [IOBluetoothDevice pairedDevices];
+ for (IOBluetoothDevice *device in pairedDevices) {
+ if ([device isConnected]) {
+ const QBluetoothAddress address(OSXBluetooth::qt_bt_address([device getAddress]));
+ if (!address.isNull())
+ connectedDevices.append(address);
+ }
+ }
+
+ // Add devices, discovered by the connection monitor:
+ connectedDevices += d_ptr->discoveredDevices;
+ // Find something more elegant? :)
+ // But after all, addresses are integers.
+ std::sort(connectedDevices.begin(), connectedDevices.end());
+ connectedDevices.erase(std::unique(connectedDevices.begin(),
+ connectedDevices.end()),
+ connectedDevices.end());
+
+ return connectedDevices;
+}
+
+QList<QBluetoothHostInfo> QBluetoothLocalDevice::allDevices()
+{
+ QList<QBluetoothHostInfo> localDevices;
+
+ QBluetoothLocalDevice defaultAdapter;
+ if (!defaultAdapter.isValid() || defaultAdapter.address().isNull()) {
+ qCCritical(QT_BT_OSX) << "QBluetoothLocalDevice::allDevices(), no valid device found";
+ return localDevices;
+ }
+
+ QBluetoothHostInfo deviceInfo;
+ deviceInfo.setName(defaultAdapter.name());
+ deviceInfo.setAddress(defaultAdapter.address());
+
+ localDevices.append(deviceInfo);
+
+ return localDevices;
+}
+
+void QBluetoothLocalDevice::pairingConfirmation(bool confirmation)
+{
+ Q_UNUSED(confirmation)
+}
+
+
+void QBluetoothLocalDevice::requestPairing(const QBluetoothAddress &address, Pairing pairing)
+{
+ if (!isValid())
+ qCWarning(QT_BT_OSX) << "QBluetoothLocalDevice::requestPairing(), invalid local device";
+
+ if (!isValid() || address.isNull()) {
+ d_ptr->emitError(PairingError, true);
+ return;
+ }
+
+ return d_ptr->requestPairing(address, pairing);
+}
+
+QBluetoothLocalDevice::Pairing QBluetoothLocalDevice::pairingStatus(const QBluetoothAddress &address) const
+{
+ if (!isValid())
+ qCWarning(QT_BT_OSX) << "QBluetoothLocalDevice::pairingStatus(), invalid local device";
+
+ if (!isValid() || address.isNull())
+ return Unpaired;
+
+ return d_ptr->pairingStatus(address);
+}
+
+QT_END_NAMESPACE
diff --git a/src/bluetooth/qbluetoothlocaldevice_p.h b/src/bluetooth/qbluetoothlocaldevice_p.h
index 5d84af7e..8cd7505c 100644
--- a/src/bluetooth/qbluetoothlocaldevice_p.h
+++ b/src/bluetooth/qbluetoothlocaldevice_p.h
@@ -237,7 +237,7 @@ private:
bool isValidDevice;
QList<QBluetoothAddress> connectedDevicesSet;
};
-#else
+#elif !defined(QT_OSX_BLUETOOTH)
class QBluetoothLocalDevicePrivate : public QObject
{
public:
diff --git a/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro b/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro
index 6a426208..43eda822 100644
--- a/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro
+++ b/tests/auto/qbluetoothdevicediscoveryagent/qbluetoothdevicediscoveryagent.pro
@@ -5,3 +5,5 @@ CONFIG += testcase
QT = core concurrent bluetooth testlib
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+
+osx:CONFIG += insignificant_test
diff --git a/tests/auto/qbluetoothlocaldevice/qbluetoothlocaldevice.pro b/tests/auto/qbluetoothlocaldevice/qbluetoothlocaldevice.pro
index 8f31fc14..a31b942d 100644
--- a/tests/auto/qbluetoothlocaldevice/qbluetoothlocaldevice.pro
+++ b/tests/auto/qbluetoothlocaldevice/qbluetoothlocaldevice.pro
@@ -5,3 +5,6 @@ CONFIG += testcase
QT = core concurrent bluetooth testlib
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+
+osx:CONFIG += insignificant_test
+
diff --git a/tests/auto/qbluetoothserver/qbluetoothserver.pro b/tests/auto/qbluetoothserver/qbluetoothserver.pro
index 4b5e5d46..ccc8aa60 100644
--- a/tests/auto/qbluetoothserver/qbluetoothserver.pro
+++ b/tests/auto/qbluetoothserver/qbluetoothserver.pro
@@ -8,3 +8,6 @@ OTHER_FILES += \
README.txt
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+
+osx:CONFIG += insignificant_test
+
diff --git a/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro b/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro
index cdf8a78b..680a5e6b 100644
--- a/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro
+++ b/tests/auto/qbluetoothservicediscoveryagent/qbluetoothservicediscoveryagent.pro
@@ -8,3 +8,4 @@ blackberry {
}
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+osx:CONFIG += insignificant_test
diff --git a/tests/auto/qbluetoothserviceinfo/qbluetoothserviceinfo.pro b/tests/auto/qbluetoothserviceinfo/qbluetoothserviceinfo.pro
index 90703bd1..f0b4e5e8 100644
--- a/tests/auto/qbluetoothserviceinfo/qbluetoothserviceinfo.pro
+++ b/tests/auto/qbluetoothserviceinfo/qbluetoothserviceinfo.pro
@@ -4,3 +4,6 @@ CONFIG += testcase
QT = core concurrent bluetooth testlib
DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
+
+osx:CONFIG += insignificant_test
+
diff --git a/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro b/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro
index 33302d60..74fdbf0d 100644
--- a/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro
+++ b/tests/auto/qlowenergycharacteristic/qlowenergycharacteristic.pro
@@ -7,3 +7,6 @@ DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0
blackberry {
LIBS += -lbtapi
}
+
+osx:CONFIG += insignificant_test
+
diff --git a/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro b/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro
index 60b5a740..dc069ca5 100644
--- a/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro
+++ b/tests/auto/qlowenergydescriptor/qlowenergydescriptor.pro
@@ -6,3 +6,6 @@ QT = core bluetooth testlib
blackberry {
LIBS += -lbtapi
}
+
+osx:CONFIG += insignificant_test
+