/**************************************************************************** ** ** 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 "qbluetoothserviceinfo.h" #include "osxbtservicerecord_p.h" #include "osxbluetooth_p.h" #include #include #include #include QT_BEGIN_NAMESPACE namespace OSXBluetooth { // // Returns a dictionary containing the Bluetooth RFCOMM service definition // corresponding to the provided |uuid| and |options|. namespace { typedef ObjCStrongReference Dictionary; typedef ObjCStrongReference SDPUUid; typedef ObjCStrongReference Number; typedef QBluetoothServiceInfo QSInfo; typedef QSInfo::Sequence Sequence; typedef QSInfo::AttributeId AttributeId; } #if 0 QBluetoothUuid profile_uuid(const QBluetoothServiceInfo &serviceInfo) { // Strategy to pick service uuid: // 1.) use serviceUuid() // 2.) use first custom uuid if available // 3.) use first service class uuid QBluetoothUuid serviceUuid(serviceInfo.serviceUuid()); if (serviceUuid.isNull()) { const QVariant var(serviceInfo.attribute(QBluetoothServiceInfo::ServiceClassIds)); if (var.isValid()) { const Sequence seq(var.value()); for (int i = 0; i < seq.count(); ++i) { QBluetoothUuid uuid(seq.at(i).value()); if (uuid.isNull()) continue; const int size = uuid.minimumSize(); if (size == 2 || size == 4) { // Base UUID derived if (serviceUuid.isNull()) serviceUuid = uuid; } else { return uuid; } } } } return serviceUuid; } #endif template Number variant_to_nsnumber(const QVariant &); template<> Number variant_to_nsnumber(const QVariant &var) { return Number([NSNumber numberWithUnsignedChar:var.value()], true); } template<> Number variant_to_nsnumber(const QVariant &var) { return Number([NSNumber numberWithUnsignedShort:var.value()], true); } template<> Number variant_to_nsnumber(const QVariant &var) { return Number([NSNumber numberWithUnsignedInt:var.value()], true); } template<> Number variant_to_nsnumber(const QVariant &var) { return Number([NSNumber numberWithChar:var.value()], true); } template<> Number variant_to_nsnumber(const QVariant &var) { return Number([NSNumber numberWithShort:var.value()], true); } template<> Number variant_to_nsnumber(const QVariant &var) { return Number([NSNumber numberWithInt:var.value()], true); } template void add_attribute(const QVariant &var, AttributeId key, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)"); if (!var.canConvert()) return; const Number num(variant_to_nsnumber(var)); [dict setObject:num forKey:[NSString stringWithFormat:@"%d", int(key)]]; } template<> void add_attribute(const QVariant &var, AttributeId key, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)"); if (!var.canConvert()) return; const QString string(var.value()); if (string.length()) { if (NSString *const nsString = string.toNSString()) [dict setObject:nsString forKey:[NSString stringWithFormat:@"%d", int(key)]]; } } template<> void add_attribute(const QVariant &var, AttributeId key, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)"); if (!var.canConvert()) return; SDPUUid ioUUID(iobluetooth_uuid(var.value())); [dict setObject:ioUUID forKey:[NSString stringWithFormat:@"%d", int(key)]]; } template<> void add_attribute(const QVariant &var, AttributeId key, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)"); if (!var.canConvert()) return; Q_UNUSED(var) Q_UNUSED(key) Q_UNUSED(dict) // TODO: not clear how should I pass an url in a dictionary, NSURL does not work. } template void add_attribute(const QVariant &var, NSMutableArray *list); template void add_attribute(const QVariant &var, NSMutableArray *list) { Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)"); if (!var.canConvert()) return; const Number num(variant_to_nsnumber(var)); [list addObject:num]; } template<> void add_attribute(const QVariant &var, NSMutableArray *list) { Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)"); if (!var.canConvert()) return; const QString string(var.value()); if (string.length()) { if (NSString *const nsString = string.toNSString()) [list addObject:nsString]; } } template<> void add_attribute(const QVariant &var, NSMutableArray *list) { Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)"); if (!var.canConvert()) return; SDPUUid ioUUID(iobluetooth_uuid(var.value())); [list addObject:ioUUID]; } template<> void add_attribute(const QVariant &var, NSMutableArray *list) { Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)"); if (!var.canConvert()) return; Q_UNUSED(var) Q_UNUSED(list) // TODO: not clear how should I pass an url in a dictionary, NSURL does not work. } void add_rfcomm_protocol_descriptor_list(uint16 channelID, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)"); QT_BT_MAC_AUTORELEASEPOOL; // Objective-C has literals (for arrays and dictionaries), but it will not compile // on 10.7 or below, so quite a lot of code here. NSMutableArray *const descriptorList = [NSMutableArray array]; IOBluetoothSDPUUID *const l2capUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP]; NSArray *const l2capList = [NSArray arrayWithObject:l2capUUID]; [descriptorList addObject:l2capList]; // IOBluetoothSDPUUID *const rfcommUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16RFCOMM]; NSMutableDictionary *const rfcommDict = [NSMutableDictionary dictionary]; [rfcommDict setObject:[NSNumber numberWithInt:1] forKey:@"DataElementType"]; [rfcommDict setObject:[NSNumber numberWithInt:1] forKey:@"DataElementSize"]; [rfcommDict setObject:[NSNumber numberWithInt:channelID] forKey:@"DataElementValue"]; // NSMutableArray *const rfcommList = [NSMutableArray array]; [rfcommList addObject:rfcommUUID]; [rfcommList addObject:rfcommDict]; [descriptorList addObject:rfcommList]; [dict setObject:descriptorList forKey:[NSString stringWithFormat:@"%d", kBluetoothSDPAttributeIdentifierProtocolDescriptorList]]; } void add_l2cap_protocol_descriptor_list(uint16 psm, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)"); QT_BT_MAC_AUTORELEASEPOOL; // Objective-C has literals (for arrays and dictionaries), but it will not compile // on 10.7 or below, so quite a lot of code here. NSMutableArray *const descriptorList = [NSMutableArray array]; NSMutableArray *const l2capList = [NSMutableArray array]; IOBluetoothSDPUUID *const l2capUUID = [IOBluetoothSDPUUID uuid16:kBluetoothSDPUUID16L2CAP]; [l2capList addObject:l2capUUID]; NSMutableDictionary *const l2capDict = [NSMutableDictionary dictionary]; [l2capDict setObject:[NSNumber numberWithInt:1] forKey:@"DataElementType"]; [l2capDict setObject:[NSNumber numberWithInt:2] forKey:@"DataElementSize"]; [l2capDict setObject:[NSNumber numberWithInt:psm] forKey:@"DataElementValue"]; [l2capList addObject:l2capDict]; [descriptorList addObject:l2capList]; [dict setObject:descriptorList forKey:[NSString stringWithFormat:@"%d", kBluetoothSDPAttributeIdentifierProtocolDescriptorList]]; } bool add_attribute(const QVariant &var, AttributeId key, NSMutableArray *list) { Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)"); if (var.canConvert()) return false; if (var.canConvert()) { //ServiceName, ServiceDescription, ServiceProvider. add_attribute(var, list); } else if (var.canConvert()) { add_attribute(var, list); } else { // Here we need 'key' to understand the type. // We can have different integer types actually, so I have to check // the 'key' to be sure the conversion is reasonable. switch (key) { case QSInfo::ServiceRecordHandle: case QSInfo::ServiceRecordState: case QSInfo::ServiceInfoTimeToLive: add_attribute(var, list); break; case QSInfo::ServiceAvailability: add_attribute(var, list); break; case QSInfo::IconUrl: case QSInfo::DocumentationUrl: case QSInfo::ClientExecutableUrl: add_attribute(var, list); break; default:; } } return true; } bool add_attribute(const QBluetoothServiceInfo &serviceInfo, AttributeId key, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dict (nil)"); const QVariant var(serviceInfo.attribute(key)); if (var.canConvert()) return false; if (var.canConvert()) { //ServiceName, ServiceDescription, ServiceProvider. add_attribute(var, key, dict); } else if (var.canConvert()) { add_attribute(serviceInfo.attribute(key), key, dict); } else { // We can have different integer types actually, so I have to check // the 'key' to be sure the conversion is reasonable. switch (key) { case QSInfo::ServiceRecordHandle: case QSInfo::ServiceRecordState: case QSInfo::ServiceInfoTimeToLive: add_attribute(serviceInfo.attribute(key), key, dict); break; case QSInfo::ServiceAvailability: add_attribute(serviceInfo.attribute(key), key, dict); break; case QSInfo::IconUrl: case QSInfo::DocumentationUrl: case QSInfo::ClientExecutableUrl: add_attribute(serviceInfo.attribute(key), key, dict); break; default:; } } return true; } bool add_sequence_attribute(const QVariant &var, AttributeId key, NSMutableArray *list) { // Add a "nested" sequence. Q_ASSERT_X(list, Q_FUNC_INFO, "invalid list (nil)"); if (var.isNull() || !var.canConvert()) return false; const Sequence sequence(var.value()); foreach (const QVariant &var, sequence) { if (var.canConvert()) { NSMutableArray *const nested = [NSMutableArray array]; add_sequence_attribute(var, key, nested); [list addObject:nested]; } else { add_attribute(var, key, list); } } return true; } bool add_sequence_attribute(const QBluetoothServiceInfo &serviceInfo, AttributeId key, Dictionary dict) { Q_ASSERT_X(dict, Q_FUNC_INFO, "invalid dictionary (nil)"); const QVariant &var(serviceInfo.attribute(key)); if (var.isNull() || !var.canConvert()) return false; QT_BT_MAC_AUTORELEASEPOOL; NSMutableArray *const list = [NSMutableArray array]; const Sequence sequence(var.value()); foreach (const QVariant &element, sequence) { if (!add_sequence_attribute(element, key, list)) add_attribute(element, key, list); } [dict setObject:list forKey:[NSString stringWithFormat:@"%d", int(key)]]; return true; } Dictionary iobluetooth_service_dictionary(const QBluetoothServiceInfo &serviceInfo) { Dictionary dict; if (serviceInfo.socketProtocol() == QBluetoothServiceInfo::UnknownProtocol) return dict; const QList attributeIds(serviceInfo.attributes()); if (!attributeIds.size()) return dict; dict.reset([[NSMutableDictionary alloc] init]); foreach (quint16 key, attributeIds) { if (key == QSInfo::ProtocolDescriptorList) // We handle it in a special way. continue; // TODO: check if non-sequence QVariant still must be // converted into NSArray for some attribute ID. if (!add_sequence_attribute(serviceInfo, AttributeId(key), dict)) add_attribute(serviceInfo, AttributeId(key), dict); } if (serviceInfo.socketProtocol() == QBluetoothServiceInfo::L2capProtocol) { add_l2cap_protocol_descriptor_list(serviceInfo.protocolServiceMultiplexer(), dict); } else { add_rfcomm_protocol_descriptor_list(serviceInfo.serverChannel(), dict); } return dict; } } QT_END_NAMESPACE