#include #include #include "obexconnection.h" ObexTransfer::ObexTransfer(obex_t *obex, obex_object_t *obj, QObject *parent) : QObject(parent), _obex(obex), _obj(obj) { } void ObexTransfer::cancel() { // TODO } ObexConnection::ObexConnection(ToqConnection *conn, QObject *parent) : QObject(parent), _conn(conn), _socket(0), _reconnectTimer(new QTimer(this)), _obex(0), _connected(false), _busy(false) { connect(_conn, &ToqConnection::connected, this, &ObexConnection::handleToqConnected); connect(_conn, &ToqConnection::disconnected, this, &ObexConnection::handleToqDisconnected); connect(_reconnectTimer, &QTimer::timeout, this, &ObexConnection::tryConnect); _reconnectTimer->setSingleShot(true); _reconnectTimer->setInterval(500); if (_conn->isConnected()) { _reconnectTimer->start(); } } ObexTransfer * ObexConnection::put(const QString &name, const QByteArray &data) { obex_object_t *obj = OBEX_ObjectNew(_obex, OBEX_CMD_PUT); obex_headerdata_t hd; hd.bq4 = data.size(); OBEX_ObjectAddHeader(_obex, obj, OBEX_HDR_LENGTH, hd, 4, OBEX_FL_FIT_ONE_PACKET); QTextCodec *codec = QTextCodec::codecForName("UTF-16BE"); QByteArray unicode = codec->fromUnicode(name); unicode.append("\0\0", 2); // Require null terminator hd.bs = reinterpret_cast(unicode.constData()); OBEX_ObjectAddHeader(_obex, obj, OBEX_HDR_NAME, hd, unicode.size(), OBEX_FL_FIT_ONE_PACKET); hd.bs = reinterpret_cast(data.constData()); OBEX_ObjectAddHeader(_obex, obj, OBEX_HDR_BODY, hd, data.size(), 0); ObexTransfer *transfer = new ObexTransfer(_obex, obj, this); connect(transfer, &ObexTransfer::destroyed, this, &ObexConnection::handleTransferDestroyed); _pending.push_back(transfer); // Delay actual start of the transfer in order to give time for caller // to connect to signals of the returned transfer object. QMetaObject::invokeMethod(this, "handleNextPending", Qt::QueuedConnection); return transfer; } void ObexConnection::tryConnect() { Q_ASSERT(!_socket); Q_ASSERT(_conn->isConnected()); qDebug() << "Trying to make OBEX connection"; _socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol, this); connect(_socket, &QBluetoothSocket::connected, this, &ObexConnection::handleSocketConnected); connect(_socket, &QBluetoothSocket::disconnected, this, &ObexConnection::handleSocketDisconnected); connect(_socket, (void (QBluetoothSocket::*)(QBluetoothSocket::SocketError))&QBluetoothSocket::error, this, &ObexConnection::handleSocketError); connect(_socket, &QBluetoothSocket::readyRead, this, &ObexConnection::handleSocketData); _socket->connectToService(_conn->address(), 4); } void ObexConnection::handleToqConnected() { if (_socket) { qDebug() << "Toq just connected but OBEX connection still active"; _socket->disconnectFromService(); if (_socket) { _socket->deleteLater(); _socket = 0; } } tryConnect(); } void ObexConnection::handleToqDisconnected() { _reconnectTimer->stop(); if (_obex) { OBEX_Cleanup(_obex); _obex = 0; } if (_socket) { _socket->disconnectFromService(); if (_socket) { _socket->deleteLater(); _socket = 0; } } _connected = false; _busy = false; } void ObexConnection::handleSocketConnected() { qDebug() << "OPP connected"; _obex = OBEX_Init(OBEX_TRANS_CUSTOM, handleObexEvent, 0); if (!_obex) { qWarning() << "Could not initialize OBEX library"; // If we fail to connect, the watch will eventually disconnect from us. // So there's no need to do anything. return; } OBEX_SetUserData(_obex, this); OBEX_SetTransportMTU(_obex, OBEX_MAXIMUM_MTU, OBEX_MAXIMUM_MTU); obex_ctrans_t trans; trans.connect = obexConnect; trans.disconnect = obexDisconnect; trans.listen = obexListen; trans.write = obexWrite; trans.handleinput = obexHandleInput; trans.customdata = _socket; int rc = OBEX_RegisterCTransport(_obex, &trans); if (rc < 0) { qWarning() << "Could not initialize custom OBEX transport"; return; } obex_object_t *obj = OBEX_ObjectNew(_obex, OBEX_CMD_CONNECT); Q_ASSERT(obj); if (OBEX_Request(_obex, obj) < 0) { qWarning() << "Could not send OBEX_CMD_CONNECT"; return; } _busy = true; // We're waiting for CONNECT result } void ObexConnection::handleSocketDisconnected() { qDebug() << "OPP disconnected"; if (_obex) { OBEX_Cleanup(_obex); _obex = 0; } if (_socket) { _socket->deleteLater(); _socket = 0; } emit disconnected(); if (_conn->isConnected()) { qDebug() << "OPP socket died but main socket still alive, retrying connection"; _reconnectTimer->start(); } _connected = false; _busy = false; } void ObexConnection::handleSocketError(QBluetoothSocket::SocketError error) { qWarning() << "OPP socket error" << error; } void ObexConnection::handleSocketData() { static char buffer[4096]; while (_socket->bytesAvailable() > 0) { qint64 read = _socket->read(buffer, sizeof(buffer)); int rc = OBEX_CustomDataFeed(_obex, reinterpret_cast(buffer), read); if (rc < 0) { qWarning() << "Could not feed data to OBEX"; } } } void ObexConnection::handleTransferDestroyed() { ObexTransfer *transfer = static_cast(sender()); _pending.removeAll(transfer); } void ObexConnection::handleNextPending() { if (!_connected) return; if (_busy) return; if (_pending.empty()) return; ObexTransfer *transfer = _pending.head(); int rc = OBEX_Request(_obex, transfer->_obj); if (rc < 0) { qWarning() << "Failed to perform request"; _socket->disconnectFromService(); _pending.dequeue(); } else { qDebug() << "Request performed"; _busy = true; } } void ObexConnection::handleObexEvent(obex_t *handle, obex_object_t *obj, int mode, int event, int obex_cmd, int obex_rsp) { ObexConnection *self = static_cast(OBEX_GetUserData(handle)); ObexTransfer *transfer; Q_UNUSED(mode); qDebug() << "OBEX event" << event << obex_cmd << obex_rsp; switch (event) { case OBEX_EV_PROGRESS: qDebug() << "Progress"; break; case OBEX_EV_REQDONE: if (!self->_busy) { qWarning() << "Response was requested, but we were not expecting anything"; } switch (obex_cmd) { case OBEX_CMD_CONNECT: switch (obex_rsp) { case OBEX_RSP_SUCCESS: qDebug() << "OPP OBEX connected"; self->_connected = true; self->_busy = false; emit self->connected(); self->handleNextPending(); break; default: qWarning() << "OPP OBEX failed to connect:" << OBEX_ResponseToString(obex_rsp); self->_socket->disconnectFromService(); break; } break; case OBEX_CMD_PUT: case OBEX_CMD_GET: Q_ASSERT(!self->_pending.isEmpty()); transfer = self->_pending.dequeue(); self->disconnect(transfer, nullptr, self, nullptr); if (transfer->_obj != obj) { qWarning() << "Got result for a request we did not make"; self->_socket->disconnectFromService(); return; } switch (obex_rsp) { case OBEX_RSP_SUCCESS: qDebug() << "OPP OBEX transfer success"; emit transfer->finished(); break; default: qWarning() << "OPP OBEX transfer failed:" << OBEX_ResponseToString(obex_rsp); emit transfer->error(obex_rsp); break; } self->_busy = false; self->handleNextPending(); break; default: // Not interested break; } break; } } int ObexConnection::obexConnect(obex_t *handle, void *data) { Q_UNUSED(handle); Q_UNUSED(data); return 1; } int ObexConnection::obexDisconnect(obex_t *handle, void *data) { Q_UNUSED(handle); Q_UNUSED(data); return 1; } int ObexConnection::obexListen(obex_t *handle, void *data) { Q_UNUSED(handle); Q_UNUSED(data); return -1; // Should never happen } int ObexConnection::obexWrite(obex_t *handle, void *data, quint8 *buf, int buflen) { QBluetoothSocket *socket = static_cast(data); Q_UNUSED(handle); return socket->write(reinterpret_cast(buf), buflen); } int ObexConnection::obexHandleInput(obex_t *handle, void *data, int timeout) { Q_UNUSED(handle); Q_UNUSED(data); Q_UNUSED(timeout); return 1; }