diff options
author | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-09-01 15:32:15 +0200 |
---|---|---|
committer | Timur Pocheptsov <Timur.Pocheptsov@digia.com> | 2014-09-16 09:12:06 +0200 |
commit | 982eeb3547f85dc76e5864559ee56db74a7dd86f (patch) | |
tree | 721e0d1a6e36ffb876e9c38adab780e56bd29779 /src/bluetooth/osx/osxbtdevicepair.mm | |
parent | bacabbcd91f018328eb0cec468c522599f614f6f (diff) |
Port QBluetoothLocalDevice and QBluetoothDeviceDiscoveryAgent to OS X.
QBluetoothLocalDevice and QBluetoothDeviceDiscoveryAgent for OS X -
Bluetooth Classic (the implementation based on IOBluetooth).
+ a very simple non-gui test (requires QApplication to work though).
Update 0: style issues reported by Qt-Bot + fix a test.
Update 1: QBluetoothLocalDevice - display confirmation
Update 2: Device discovery agent - follow the expected logic
and apply suggested fixes.
Update 3: started/finished delegate methods seems to be synchronous and immediately follow
start/stop calls on an inquiry.
Update 4: remove unused function and redundant error message.
Update 5: the first attempt to fix pairingStatus/requestPairing on a local device.
Update 6: on OS X it's impossible (with a given public API) to request 'Unpaired'.
I was only able to find some quite terrible hacks with private APIs or
even worse - playing with SystemConfiguration frameworks and changing
System Preferencies programmatically (requires authorization and looks like a
total hack, since it has nothing to do with Bluetooth framework).
Update 7: A very limited support for deviceConnected and connectedDevices.
Update 8: Fix an invalid invokeMethod's argument.
Update 9: Subject changed.
Update 10: fixes in a documentation.
Update 11: asserts in a coding convetion/style.
Update 12: "fix" asserts + emit errors if a start/stop failed.
Update 13: deviceDisconnected implemented.
Update 14: use not only paired && connected devices (QBluetoothLocalDevice::connectedDevices),
but also devices discovered by the connection monitor.
Update 15: remove a test, not required (there are 'auto' tests).
Update 16: fix private headers - they MUST have _p suffix :(
Update 17: tests are known to fail (at the moment) - IOBluetooth
requires adjustment (QApplication instead of QCoreApplication,
lack of ability to power on/off a device (not possible on Mac) +
other things).
Change-Id: Iea1c8a98f1fd719f4560ec8920d00cc07eaa8146
Reviewed-by: Alex Blasche <alexander.blasche@digia.com>
Reviewed-by: Timur Pocheptsov <Timur.Pocheptsov@digia.com>
Diffstat (limited to 'src/bluetooth/osx/osxbtdevicepair.mm')
-rw-r--r-- | src/bluetooth/osx/osxbtdevicepair.mm | 236 |
1 files changed, 236 insertions, 0 deletions
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 |