Fix crash in Bluez implementation for QBluetoothSocket
[qt:qtconnectivity.git] / src / bluetooth / qbluetoothsocket_bluez.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtBluetooth module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qbluetoothsocket.h"
43 #include "qbluetoothsocket_p.h"
44
45 #include "bluez/manager_p.h"
46 #include "bluez/adapter_p.h"
47 #include "bluez/device_p.h"
48
49 #include <qplatformdefs.h>
50
51 #include <QtCore/QLoggingCategory>
52 #include <bluetooth/bluetooth.h>
53 #include <bluetooth/rfcomm.h>
54 #include <bluetooth/l2cap.h>
55
56 #include <errno.h>
57 #include <unistd.h>
58 #include <string.h>
59
60 #include <QtCore/QSocketNotifier>
61
62 QT_BEGIN_NAMESPACE
63
64 Q_DECLARE_LOGGING_CATEGORY(QT_BT_BLUEZ)
65
66 QBluetoothSocketPrivate::QBluetoothSocketPrivate()
67     : socket(-1),
68       socketType(QBluetoothServiceInfo::UnknownProtocol),
69       state(QBluetoothSocket::UnconnectedState),
70       readNotifier(0),
71       connectWriteNotifier(0),
72       connecting(false),
73       discoveryAgent(0)
74 {
75 }
76
77 QBluetoothSocketPrivate::~QBluetoothSocketPrivate()
78 {
79     delete readNotifier;
80     readNotifier = 0;
81     delete connectWriteNotifier;
82     connectWriteNotifier = 0;
83 }
84
85 bool QBluetoothSocketPrivate::ensureNativeSocket(QBluetoothServiceInfo::Protocol type)
86 {
87     if (socket != -1) {
88         if (socketType == type)
89             return true;
90
91         delete readNotifier;
92         readNotifier = 0;
93         delete connectWriteNotifier;
94         connectWriteNotifier = 0;
95         QT_CLOSE(socket);
96     }
97
98     socketType = type;
99
100     switch (type) {
101     case QBluetoothServiceInfo::L2capProtocol:
102         socket = ::socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
103         break;
104     case QBluetoothServiceInfo::RfcommProtocol:
105         socket = ::socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
106         break;
107     default:
108         socket = -1;
109     }
110
111     if (socket == -1)
112         return false;
113
114     int flags = fcntl(socket, F_GETFL, 0);
115     fcntl(socket, F_SETFL, flags | O_NONBLOCK);
116
117     Q_Q(QBluetoothSocket);
118     readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
119     QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_q_readNotify()));
120     connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q);    
121     QObject::connect(connectWriteNotifier, SIGNAL(activated(int)), q, SLOT(_q_writeNotify()));
122
123     connectWriteNotifier->setEnabled(false);
124     readNotifier->setEnabled(false);
125
126
127     return true;
128 }
129
130 void QBluetoothSocketPrivate::connectToService(const QBluetoothAddress &address, quint16 port, QIODevice::OpenMode openMode)
131 {
132     Q_Q(QBluetoothSocket);
133     Q_UNUSED(openMode);
134     int result = -1;
135
136     if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
137         sockaddr_rc addr;
138
139         memset(&addr, 0, sizeof(addr));
140         addr.rc_family = AF_BLUETOOTH;
141         addr.rc_channel = port;
142
143         convertAddress(address.toUInt64(), addr.rc_bdaddr.b);
144
145         connectWriteNotifier->setEnabled(true);
146         readNotifier->setEnabled(true);QString();
147
148         result = ::connect(socket, (sockaddr *)&addr, sizeof(addr));
149     } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
150         sockaddr_l2 addr;
151
152         memset(&addr, 0, sizeof(addr));
153         addr.l2_family = AF_BLUETOOTH;
154         addr.l2_psm = port;
155
156         convertAddress(address.toUInt64(), addr.l2_bdaddr.b);
157
158         connectWriteNotifier->setEnabled(true);
159         readNotifier->setEnabled(true);
160
161         result = ::connect(socket, (sockaddr *)&addr, sizeof(addr));
162     }
163
164     if (result >= 0 || (result == -1 && errno == EINPROGRESS)) {
165         connecting = true;
166         q->setSocketState(QBluetoothSocket::ConnectingState);
167     } else {
168         errorString = QString::fromLocal8Bit(strerror(errno));
169         q->setSocketError(QBluetoothSocket::UnknownSocketError);
170     }
171 }
172
173 void QBluetoothSocketPrivate::_q_writeNotify()
174 {
175     Q_Q(QBluetoothSocket);
176     if(connecting && state == QBluetoothSocket::ConnectingState){
177         int errorno, len;
178         len = sizeof(errorno);
179         ::getsockopt(socket, SOL_SOCKET, SO_ERROR, &errorno, (socklen_t*)&len);
180         if(errorno) {
181             errorString = QString::fromLocal8Bit(strerror(errorno));
182             emit q->error(QBluetoothSocket::UnknownSocketError);
183             return;
184         }
185
186         q->setSocketState(QBluetoothSocket::ConnectedState);
187         emit q->connected();
188
189         connectWriteNotifier->setEnabled(false);
190         connecting = false;
191     }
192     else {
193         if (txBuffer.size() == 0) {
194             connectWriteNotifier->setEnabled(false);
195             return;
196         }
197
198         char buf[1024];
199
200         int size = txBuffer.read(buf, 1024);
201
202         if (::write(socket, buf, size) != size) {
203             socketError = QBluetoothSocket::NetworkError;
204             emit q->error(socketError);
205         }
206         else {
207             emit q->bytesWritten(size);
208         }
209
210         if (txBuffer.size()) {
211             connectWriteNotifier->setEnabled(true);
212         }
213         else if (state == QBluetoothSocket::ClosingState) {
214             connectWriteNotifier->setEnabled(false);
215             this->close();
216         }
217     }    
218 }
219
220 // TODO: move to private backend?
221
222 void QBluetoothSocketPrivate::_q_readNotify()
223 {
224     Q_Q(QBluetoothSocket);
225     char *writePointer = buffer.reserve(QPRIVATELINEARBUFFER_BUFFERSIZE);
226 //    qint64 readFromDevice = q->readData(writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE);
227     int readFromDevice = ::read(socket, writePointer, QPRIVATELINEARBUFFER_BUFFERSIZE);
228     if(readFromDevice <= 0){
229         int errsv = errno;
230         readNotifier->setEnabled(false);
231         connectWriteNotifier->setEnabled(false);
232         errorString = QString::fromLocal8Bit(strerror(errsv));
233         qCWarning(QT_BT_BLUEZ) << Q_FUNC_INFO << socket << "error:" << readFromDevice << errorString;
234         if(errsv == EHOSTDOWN)
235             emit q->error(QBluetoothSocket::HostNotFoundError);
236         else
237             emit q->error(QBluetoothSocket::UnknownSocketError);
238
239         q->disconnectFromService();
240         q->setSocketState(QBluetoothSocket::UnconnectedState);        
241     }
242     else {
243         buffer.chop(QPRIVATELINEARBUFFER_BUFFERSIZE - (readFromDevice < 0 ? 0 : readFromDevice));
244
245         emit q->readyRead();
246     }
247 }
248
249 void QBluetoothSocketPrivate::abort()
250 {
251     delete readNotifier;
252     readNotifier = 0;
253     delete connectWriteNotifier;
254     connectWriteNotifier = 0;
255
256     // We don't transition through Closing for abort, so
257     // we don't call disconnectFromService or
258     // QBluetoothSocket::close
259     QT_CLOSE(socket);
260     socket = -1;
261
262     Q_Q(QBluetoothSocket);
263     emit q->disconnected();
264 }
265
266 QString QBluetoothSocketPrivate::localName() const
267 {
268     if (!m_localName.isEmpty())
269         return m_localName;
270
271     const QBluetoothAddress address = localAddress();
272     if (address.isNull())
273         return QString();
274
275     OrgBluezManagerInterface manager(QLatin1String("org.bluez"), QLatin1String("/"),
276                                      QDBusConnection::systemBus());
277
278     QDBusPendingReply<QDBusObjectPath> reply = manager.FindAdapter(address.toString());
279     reply.waitForFinished();
280     if (reply.isError())
281         return QString();
282
283     OrgBluezAdapterInterface adapter(QLatin1String("org.bluez"), reply.value().path(),
284                                      QDBusConnection::systemBus());
285
286     QDBusPendingReply<QVariantMap> properties = adapter.GetProperties();
287     properties.waitForFinished();
288     if (properties.isError())
289         return QString();
290
291     m_localName = properties.value().value(QLatin1String("Name")).toString();
292
293     return m_localName;
294 }
295
296 QBluetoothAddress QBluetoothSocketPrivate::localAddress() const
297 {
298     if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
299         sockaddr_rc addr;
300         socklen_t addrLength = sizeof(addr);
301
302         if (::getsockname(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0) {
303             quint64 bdaddr;
304             convertAddress(addr.rc_bdaddr.b, bdaddr);
305             return QBluetoothAddress(bdaddr);
306         }
307     } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
308         sockaddr_l2 addr;
309         socklen_t addrLength = sizeof(addr);
310
311         if (::getsockname(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0) {
312             quint64 bdaddr;
313             convertAddress(addr.l2_bdaddr.b, bdaddr);
314             return QBluetoothAddress(bdaddr);
315         }
316     }
317
318     return QBluetoothAddress();
319 }
320
321 quint16 QBluetoothSocketPrivate::localPort() const
322 {
323     if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
324         sockaddr_rc addr;
325         socklen_t addrLength = sizeof(addr);
326
327         if (::getsockname(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
328             return addr.rc_channel;
329     } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
330         sockaddr_l2 addr;
331         socklen_t addrLength = sizeof(addr);
332
333         if (::getsockname(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
334             return addr.l2_psm;
335     }
336
337     return 0;
338 }
339
340 QString QBluetoothSocketPrivate::peerName() const
341 {
342     if (!m_peerName.isEmpty())
343         return m_peerName;
344
345     quint64 bdaddr;
346
347     if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
348         sockaddr_rc addr;
349         socklen_t addrLength = sizeof(addr);
350
351         if (::getpeername(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) < 0)
352             return QString();
353
354         convertAddress(addr.rc_bdaddr.b, bdaddr);
355     } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
356         sockaddr_l2 addr;
357         socklen_t addrLength = sizeof(addr);
358
359         if (::getpeername(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) < 0)
360             return QString();
361
362         convertAddress(addr.l2_bdaddr.b, bdaddr);
363     } else {
364         qCWarning(QT_BT_BLUEZ) << "peerName() called on socket of unknown type";
365         return QString();
366     }
367
368     const QString peerAddress = QBluetoothAddress(bdaddr).toString();
369     const QString localAdapter = localAddress().toString();
370
371     OrgBluezManagerInterface manager(QLatin1String("org.bluez"), QLatin1String("/"),
372                                      QDBusConnection::systemBus());
373
374     QDBusPendingReply<QDBusObjectPath> reply = manager.FindAdapter(localAdapter);
375     reply.waitForFinished();
376     if (reply.isError())
377         return QString();
378
379     OrgBluezAdapterInterface adapter(QLatin1String("org.bluez"), reply.value().path(),
380                                      QDBusConnection::systemBus());
381
382     QDBusPendingReply<QDBusObjectPath> deviceObjectPath = adapter.CreateDevice(peerAddress);
383     deviceObjectPath.waitForFinished();
384     if (deviceObjectPath.isError()) {
385         if (deviceObjectPath.error().name() != QLatin1String("org.bluez.Error.AlreadyExists"))
386             return QString();
387
388         deviceObjectPath = adapter.FindDevice(peerAddress);
389         deviceObjectPath.waitForFinished();
390         if (deviceObjectPath.isError())
391             return QString();
392     }
393
394     OrgBluezDeviceInterface device(QLatin1String("org.bluez"), deviceObjectPath.value().path(),
395                                    QDBusConnection::systemBus());
396
397     QDBusPendingReply<QVariantMap> properties = device.GetProperties();
398     properties.waitForFinished();
399     if (properties.isError())
400         return QString();
401
402     m_peerName = properties.value().value(QLatin1String("Alias")).toString();
403
404     return m_peerName;
405 }
406
407 QBluetoothAddress QBluetoothSocketPrivate::peerAddress() const
408 {
409     if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
410         sockaddr_rc addr;
411         socklen_t addrLength = sizeof(addr);
412
413         if (::getpeername(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0) {
414             quint64 bdaddr;
415             convertAddress(addr.rc_bdaddr.b, bdaddr);
416             return QBluetoothAddress(bdaddr);
417         }
418     } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
419         sockaddr_l2 addr;
420         socklen_t addrLength = sizeof(addr);
421
422         if (::getpeername(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0) {
423             quint64 bdaddr;
424             convertAddress(addr.l2_bdaddr.b, bdaddr);
425             return QBluetoothAddress(bdaddr);
426         }
427     }
428
429     return QBluetoothAddress();
430 }
431
432 quint16 QBluetoothSocketPrivate::peerPort() const
433 {
434     if (socketType == QBluetoothServiceInfo::RfcommProtocol) {
435         sockaddr_rc addr;
436         socklen_t addrLength = sizeof(addr);
437
438         if (::getpeername(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
439             return addr.rc_channel;
440     } else if (socketType == QBluetoothServiceInfo::L2capProtocol) {
441         sockaddr_l2 addr;
442         socklen_t addrLength = sizeof(addr);
443
444         if (::getpeername(socket, reinterpret_cast<sockaddr *>(&addr), &addrLength) == 0)
445             return addr.l2_psm;
446     }
447
448     return 0;
449 }
450
451 qint64 QBluetoothSocketPrivate::writeData(const char *data, qint64 maxSize)
452 {
453     Q_Q(QBluetoothSocket);
454     if (q->openMode() & QIODevice::Unbuffered) {
455         if (::write(socket, data, maxSize) != maxSize) {
456             socketError = QBluetoothSocket::NetworkError;
457             emit q->error(socketError);
458         }
459
460         emit q->bytesWritten(maxSize);
461
462         return maxSize;
463     }
464     else {
465
466         if(!connectWriteNotifier)
467             return 0;
468
469         if(txBuffer.size() == 0) {
470             connectWriteNotifier->setEnabled(true);        
471             QMetaObject::invokeMethod(q, "_q_writeNotify", Qt::QueuedConnection);
472         }
473
474         char *txbuf = txBuffer.reserve(maxSize);
475         memcpy(txbuf, data, maxSize);
476
477         return maxSize;
478     }
479 }
480
481 qint64 QBluetoothSocketPrivate::readData(char *data, qint64 maxSize)
482 {
483     if(!buffer.isEmpty()){
484         int i = buffer.read(data, maxSize);
485         return i;
486
487     }
488     return 0;
489 }
490
491 void QBluetoothSocketPrivate::close()
492 {
493     Q_Q(QBluetoothSocket);
494
495     // Only go through closing if the socket was fully opened
496     if(state == QBluetoothSocket::ConnectedState)
497         q->setSocketState(QBluetoothSocket::ClosingState);
498
499     if(txBuffer.size() > 0 &&
500        state == QBluetoothSocket::ClosingState){
501         connectWriteNotifier->setEnabled(true);
502     }
503     else {
504
505         delete readNotifier;
506         readNotifier = 0;
507         delete connectWriteNotifier;
508         connectWriteNotifier = 0;
509
510         // We are disconnected now, so go to unconnected.
511         q->setSocketState(QBluetoothSocket::UnconnectedState);
512         emit q->disconnected();
513         QT_CLOSE(socket);
514         socket = -1;
515     }
516
517 }
518
519 bool QBluetoothSocketPrivate::setSocketDescriptor(int socketDescriptor, QBluetoothServiceInfo::Protocol socketType_,
520                                            QBluetoothSocket::SocketState socketState, QBluetoothSocket::OpenMode openMode)
521 {
522     Q_Q(QBluetoothSocket);
523     delete readNotifier;
524     readNotifier = 0;
525     delete connectWriteNotifier;
526     connectWriteNotifier = 0;
527
528     socketType = socketType_;
529     socket = socketDescriptor;
530
531     // ensure that O_NONBLOCK is set on new connections.
532     int flags = fcntl(socket, F_GETFL, 0);
533     if (!(flags & O_NONBLOCK))
534         fcntl(socket, F_SETFL, flags | O_NONBLOCK);
535
536     readNotifier = new QSocketNotifier(socket, QSocketNotifier::Read);
537     QObject::connect(readNotifier, SIGNAL(activated(int)), q, SLOT(_q_readNotify()));
538     connectWriteNotifier = new QSocketNotifier(socket, QSocketNotifier::Write, q);
539     QObject::connect(connectWriteNotifier, SIGNAL(activated(int)), q, SLOT(_q_writeNotify()));
540
541     q->setSocketState(socketState);
542     q->setOpenMode(openMode);
543
544     return true;
545 }
546
547 int QBluetoothSocketPrivate::socketDescriptor() const
548 {
549     return socket;
550 }
551
552 qint64 QBluetoothSocketPrivate::bytesAvailable() const
553 {
554     return buffer.size();
555 }
556
557 QT_END_NAMESPACE