#include #ifdef MANUAL_SDP #include #include #endif #include "saprotocol.h" #include "sapbtlistener.h" #include "sapbtpeer.h" #ifdef DESKTOP #include "hfpag.h" #endif namespace { #ifdef MANUAL_SDP // Basically, QBluetoothServiceInfo is not yet compatible with Bluez5, // so we hack around it by doing our own SDP connection. void add_sdp_record(sdp_session_t *session, const QBluetoothUuid &btuuid, quint16 port) { sdp_list_t *svclass_id, *apseq, *root; uuid_t root_uuid, svclass_uuid, l2cap_uuid, rfcomm_uuid; sdp_list_t *aproto, *proto[2]; sdp_data_t *chan; sdp_record_t *record = sdp_record_alloc(); sdp_set_info_attr(record, "SAP", "gearbttest", "Samsung Accessory Protocol"); sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP); root = sdp_list_append(0, &root_uuid); sdp_set_browse_groups(record, root); QByteArray uuid = btuuid.toRfc4122(); sdp_uuid128_create(&svclass_uuid, uuid.constData()); svclass_id = sdp_list_append(0, &svclass_uuid); sdp_set_service_classes(record, svclass_id); sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID); proto[0] = sdp_list_append(0, &l2cap_uuid); apseq = sdp_list_append(0, proto[0]); sdp_uuid16_create(&rfcomm_uuid, RFCOMM_UUID); proto[1] = sdp_list_append(0, &rfcomm_uuid); chan = sdp_data_alloc(SDP_UINT8, &port); proto[1] = sdp_list_append(proto[1], chan); apseq = sdp_list_append(apseq, proto[1]); aproto = sdp_list_append(0, apseq); sdp_set_access_protos(record, aproto); if (sdp_record_register(session, record, 0) < 0) { qWarning() << "sdp_record_register failed"; } } void add_sdp_records(quint16 port) { bdaddr_t addr_any = {{0, 0, 0, 0, 0, 0}}; bdaddr_t addr_local = {{0, 0, 0, 0xFF, 0xFF, 0xFF}}; sdp_session_t *sess = sdp_connect(&addr_any, &addr_local, 0); if (!sess) { qWarning() << "sdp_connect failed"; return; } add_sdp_record(sess, SAProtocol::dataServiceUuid, port); } #endif } SAPBTListener::SAPBTListener(QObject *parent) : QObject(parent), _server(0) { } SAPBTListener::~SAPBTListener() { stop(); } void SAPBTListener::start() { if (_server) { return; } #if QT_VERSION >= QT_VERSION_CHECK(5,2,0) _server = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this); #else _server = new QRfcommServer(this); #endif connect(_server, SIGNAL(newConnection()), this, SLOT(acceptConnection())); if (!_server->listen(QBluetoothAddress(), 0)) { qWarning() << "Failed to start Bluetooth listener socket"; stop(); return; } quint8 serverPort = _server->serverPort(); #if MANUAL_SDP // Basically, QBluetoothServiceInfo is not yet compatible with Bluez5, // so we hack around it by doing our own SDP connection. add_sdp_records(serverPort); #else /* Service Name: SAP Service RecHandle: 0x1000b Service Class ID List: UUID 128: a49eb41e-cb06-495c-9f4f-bb80a90cdf00 Protocol Descriptor List: "L2CAP" (0x0100) "RFCOMM" (0x0003) Channel: 5 Service Name: SAP Service RecHandle: 0x1000c Service Class ID List: UUID 128: a49eb41e-cb06-495c-9f4f-aa80a90cdf4a Protocol Descriptor List: "L2CAP" (0x0100) "RFCOMM" (0x0003) Channel: 6 */ _service.setServiceName("SAP"); _service.setServiceDescription("Samsung Accessory Profile"); _service.setServiceProvider("gearbtteest"); QBluetoothServiceInfo::Sequence classIds; classIds.append(QVariant::fromValue(SAProtocol::dataServiceUuid)); _service.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classIds); QBluetoothServiceInfo::Sequence browseGroupList; browseGroupList.append(QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup))); _service.setAttribute(QBluetoothServiceInfo::BrowseGroupList, browseGroupList); QBluetoothServiceInfo::Sequence protocolDescriptorList; QBluetoothServiceInfo::Sequence protocol; protocol.append(QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap))); protocolDescriptorList.append(QVariant::fromValue(protocol)); protocol.clear(); protocol.append(QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))); protocol.append(QVariant::fromValue(serverPort)); protocolDescriptorList.append(QVariant::fromValue(protocol)); protocol.clear(); _service.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList); if (!_service.registerService()) { qWarning() << "Failed to register the SAP service"; } #endif } void SAPBTListener::stop() { if (!_server) { return; } if (!_service.unregisterService()) { qWarning() << "Failed to unregister SAP service"; } delete _server; _server = 0; } void SAPBTListener::nudge(const QBluetoothAddress &address) { #if QT_VERSION >= QT_VERSION_CHECK(5,2,0) QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); #else QBluetoothSocket *socket = new QBluetoothSocket(QBluetoothSocket::RfcommSocket, this); #endif connect(socket, SIGNAL(connected()), socket, SLOT(deleteLater())); connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(handleNudgeError(QBluetoothSocket::SocketError))); qDebug() << "Nudging" << address.toString(); socket->connectToService(address, 2); //SAPProtocol::nudgeServiceUuid); #if DESKTOP // At the same time set up and HFP connection to the watch. new HfpAg(address, this); #endif } void SAPBTListener::acceptConnection() { qDebug() << "Incoming BT connection"; QBluetoothSocket *socket = _server->nextPendingConnection(); if (!socket) { qWarning() << "Actually, no incoming connection"; return; } qDebug() << "Got connection"; // TODO Why am I hardcoding the role here new SAPBTPeer(SAProtocol::ClientRole, socket, this); } void SAPBTListener::handleNudgeError(QBluetoothSocket::SocketError error) { QBluetoothSocket *socket = static_cast(sender()); qWarning() << "Cannot nudge:" << error << socket->errorString(); socket->abort(); socket->deleteLater(); }