| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** 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 The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "osx/osxbtservicerecord_p.h" |
| #include "qbluetoothserviceinfo_p.h" |
| #include "qbluetoothserviceinfo.h" |
| #include "qbluetoothdeviceinfo.h" |
| #include "qbluetoothserver_p.h" |
| #include "osx/osxbtutility_p.h" |
| #include "osx/osxbluetooth_p.h" |
| |
| #include <QtCore/qloggingcategory.h> |
| #include <QtCore/qvariant.h> |
| #include <QtCore/qglobal.h> |
| #include <QtCore/qmutex.h> |
| #include <QtCore/qmap.h> |
| #include <QtCore/qurl.h> |
| |
| #include <Foundation/Foundation.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| namespace { |
| |
| using DarwinBluetooth::RetainPolicy; |
| using ServiceInfo = QBluetoothServiceInfo; |
| |
| // Alas, since there is no d_ptr<->q_ptr link (which is not that bad in itself), |
| // I need these getters duplicated here: |
| ServiceInfo::Protocol socket_protocol(const QBluetoothServiceInfoPrivate &privateInfo) |
| { |
| ServiceInfo::Sequence parameters = privateInfo.protocolDescriptor(QBluetoothUuid::Rfcomm); |
| if (!parameters.isEmpty()) |
| return ServiceInfo::RfcommProtocol; |
| |
| parameters = privateInfo.protocolDescriptor(QBluetoothUuid::L2cap); |
| if (!parameters.isEmpty()) |
| return ServiceInfo::L2capProtocol; |
| |
| return ServiceInfo::UnknownProtocol; |
| } |
| |
| int channel_or_psm(const QBluetoothServiceInfoPrivate &privateInfo, QBluetoothUuid::ProtocolUuid uuid) |
| { |
| const auto parameters = privateInfo.protocolDescriptor(uuid); |
| if (parameters.isEmpty()) |
| return -1; |
| else if (parameters.count() == 1) |
| return 0; |
| |
| return parameters.at(1).toInt(); |
| } |
| |
| } // unnamed namespace |
| |
| QBluetoothServiceInfoPrivate::QBluetoothServiceInfoPrivate() |
| { |
| } |
| |
| QBluetoothServiceInfoPrivate::~QBluetoothServiceInfoPrivate() |
| { |
| } |
| |
| bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothAddress &localAddress) |
| { |
| Q_UNUSED(localAddress); |
| return false; |
| } |
| |
| bool QBluetoothServiceInfoPrivate::registerService(const QBluetoothServiceInfo &info) |
| { |
| using namespace OSXBluetooth; |
| |
| if (isRegistered()) |
| return false; |
| |
| using namespace OSXBluetooth; |
| |
| ObjCStrongReference<NSMutableDictionary> serviceDict(iobluetooth_service_dictionary(info)); |
| if (!serviceDict) { |
| qCWarning(QT_BT_OSX) << "failed to create a service dictionary"; |
| return false; |
| } |
| |
| Q_ASSERT(!registered); |
| Q_ASSERT_X(!serviceRecord, Q_FUNC_INFO, "not registered, but serviceRecord is not nil"); |
| |
| SDPRecord newRecord; |
| newRecord.reset([IOBluetoothSDPServiceRecord |
| publishedServiceRecordWithDictionary:serviceDict], RetainPolicy::doInitialRetain); |
| if (!newRecord) { |
| qCWarning(QT_BT_OSX) << "failed to register a service record"; |
| return false; |
| } |
| |
| BluetoothSDPServiceRecordHandle newRecordHandle = 0; |
| auto *ioSDPRecord = newRecord.getAs<IOBluetoothSDPServiceRecord>(); |
| if ([ioSDPRecord getServiceRecordHandle:&newRecordHandle] != kIOReturnSuccess) { |
| qCWarning(QT_BT_OSX) << "failed to register a service record"; |
| [ioSDPRecord removeServiceRecord]; |
| return false; |
| } |
| |
| const ServiceInfo::Protocol type = info.socketProtocol(); |
| quint16 realPort = 0; |
| QBluetoothServerPrivate *server = nullptr; |
| bool configured = false; |
| |
| if (type == QBluetoothServiceInfo::L2capProtocol) { |
| BluetoothL2CAPPSM psm = 0; |
| server = QBluetoothServerPrivate::registeredServer(info.protocolServiceMultiplexer(), type); |
| if ([ioSDPRecord getL2CAPPSM:&psm] == kIOReturnSuccess) { |
| configured = true; |
| realPort = psm; |
| } |
| } else if (type == QBluetoothServiceInfo::RfcommProtocol) { |
| BluetoothRFCOMMChannelID channelID = 0; |
| server = QBluetoothServerPrivate::registeredServer(info.serverChannel(), type); |
| if ([ioSDPRecord getRFCOMMChannelID:&channelID] == kIOReturnSuccess) { |
| configured = true; |
| realPort = channelID; |
| } |
| } |
| |
| if (!configured) { |
| [ioSDPRecord removeServiceRecord]; |
| qCWarning(QT_BT_OSX) << "failed to register a service record"; |
| return false; |
| } |
| |
| registered = true; |
| serviceRecord.swap(newRecord); |
| serviceRecordHandle = newRecordHandle; |
| |
| if (server) |
| server->startListener(realPort); |
| |
| return true; |
| } |
| |
| bool QBluetoothServiceInfoPrivate::isRegistered() const |
| { |
| return registered; |
| } |
| |
| bool QBluetoothServiceInfoPrivate::unregisterService() |
| { |
| if (!registered) |
| return false; |
| |
| Q_ASSERT_X(serviceRecord, Q_FUNC_INFO, "service registered, but serviceRecord is nil"); |
| |
| auto *nativeRecord = serviceRecord.getAs<IOBluetoothSDPServiceRecord>(); |
| [nativeRecord removeServiceRecord]; |
| serviceRecord.reset(); |
| |
| const ServiceInfo::Protocol type = socket_protocol(*this); |
| QBluetoothServerPrivate *server = nullptr; |
| |
| const QMutexLocker lock(&QBluetoothServerPrivate::channelMapMutex()); |
| if (type == ServiceInfo::RfcommProtocol) |
| server = QBluetoothServerPrivate::registeredServer(channel_or_psm(*this, QBluetoothUuid::Rfcomm), type); |
| else if (type == ServiceInfo::L2capProtocol) |
| server = QBluetoothServerPrivate::registeredServer(channel_or_psm(*this, QBluetoothUuid::L2cap), type); |
| |
| if (server) |
| server->stopListener(); |
| |
| registered = false; |
| serviceRecordHandle = 0; |
| |
| return true; |
| } |
| |
| QT_END_NAMESPACE |