1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
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 The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qdeclarativebluetoothsocket_p.h"
41 
42 #include <QtCore/QLoggingCategory>
43 #include <QtCore/QPointer>
44 #include <QtCore/QStringList>
45 #include <QtCore/QDataStream>
46 #include <QtCore/QByteArray>
47 
48 #include <QtBluetooth/QBluetoothDeviceInfo>
49 #include <QtBluetooth/QBluetoothAddress>
50 #include <QtBluetooth/QBluetoothSocket>
51 
52 /*!
53     \qmltype BluetoothSocket
54     \instantiates QDeclarativeBluetoothSocket
55     \inqmlmodule QtBluetooth
56     \since 5.2
57     \brief Enables connecting and communicating with a Bluetooth service or
58     device.
59 
60    \sa QBluetoothSocket
61    \sa QDataStream
62 
63     It allows a QML class connect to another Bluetooth device and exchange strings
64     with it. Data is sent and received using a QDataStream object allowing type
65     safe transfers of QStrings. QDataStream is a well known format and can be
66     decoded by non-Qt applications. Note that for the ease of use, BluetoothSocket
67     is only well suited for use with strings. If you want to
68     use a binary protocol for your application's communication you should
69     consider using its C++ counterpart QBluetoothSocket.
70 
71     Connections to remote devices can be over RFCOMM or L2CAP.  Either the remote port
72     or service UUID is required.  This is specified by creating a BluetoothService,
73     or passing in the service return from BluetoothDiscoveryModel.
74  */
75 
76 Q_DECLARE_LOGGING_CATEGORY(QT_BT_QML)
77 
78 class QDeclarativeBluetoothSocketPrivate
79 {
80 public:
QDeclarativeBluetoothSocketPrivate(QDeclarativeBluetoothSocket * bs)81     QDeclarativeBluetoothSocketPrivate(QDeclarativeBluetoothSocket *bs)
82         : m_dbs(bs),
83           m_error(QDeclarativeBluetoothSocket::NoError),
84           m_state(QDeclarativeBluetoothSocket::NoServiceSet),
85           m_componentCompleted(false),
86           m_connected(false)
87     {
88 
89     }
90 
~QDeclarativeBluetoothSocketPrivate()91     ~QDeclarativeBluetoothSocketPrivate()
92     {
93         delete m_socket;
94     }
95 
connect()96     void connect()
97     {
98         Q_ASSERT(m_service);
99         //qDebug() << "Connecting to: " << m_service->serviceInfo()->device().address().toString();
100         m_error = QDeclarativeBluetoothSocket::NoError;
101 
102         if (m_socket)
103             m_socket->deleteLater();
104 
105         QBluetoothServiceInfo::Protocol socketProtocol;
106         if (m_service->serviceInfo()->socketProtocol() == QBluetoothServiceInfo::L2capProtocol)
107             socketProtocol = QBluetoothServiceInfo::L2capProtocol;
108         else if (m_service->serviceInfo()->socketProtocol() == QBluetoothServiceInfo::RfcommProtocol)
109             socketProtocol = QBluetoothServiceInfo::RfcommProtocol;
110         else
111             socketProtocol = QBluetoothServiceInfo::UnknownProtocol;
112 
113         m_socket = new QBluetoothSocket(socketProtocol);
114         m_socket->connectToService(*m_service->serviceInfo());
115         QObject::connect(m_socket, &QBluetoothSocket::connected,
116                          m_dbs, &QDeclarativeBluetoothSocket::socket_connected);
117         QObject::connect(m_socket, &QBluetoothSocket::disconnected,
118                          m_dbs, &QDeclarativeBluetoothSocket::socket_disconnected);
119         QObject::connect(m_socket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::error),
120                          m_dbs, &QDeclarativeBluetoothSocket::socket_error);
121         QObject::connect(m_socket, &QBluetoothSocket::stateChanged,
122                          m_dbs, &QDeclarativeBluetoothSocket::socket_state);
123         QObject::connect(m_socket, &QIODevice::readyRead,
124                          m_dbs, &QDeclarativeBluetoothSocket::socket_readyRead);
125     }
126 
127     QDeclarativeBluetoothSocket *m_dbs;
128     QDeclarativeBluetoothService *m_service = nullptr;
129     QBluetoothSocket *m_socket = nullptr;
130     QDeclarativeBluetoothSocket::Error m_error;
131     QDeclarativeBluetoothSocket::SocketState m_state;
132     bool m_componentCompleted;
133     bool m_connected;
134 };
135 
QDeclarativeBluetoothSocket(QObject * parent)136 QDeclarativeBluetoothSocket::QDeclarativeBluetoothSocket(QObject *parent) :
137     QObject(parent)
138 {
139     d = new QDeclarativeBluetoothSocketPrivate(this);
140 }
141 
QDeclarativeBluetoothSocket(QDeclarativeBluetoothService * service,QObject * parent)142 QDeclarativeBluetoothSocket::QDeclarativeBluetoothSocket(QDeclarativeBluetoothService *service, QObject *parent)
143     : QObject(parent)
144 {
145     d = new QDeclarativeBluetoothSocketPrivate(this);
146     d->m_service = service;
147 }
148 
QDeclarativeBluetoothSocket(QBluetoothSocket * socket,QDeclarativeBluetoothService * service,QObject * parent)149 QDeclarativeBluetoothSocket::QDeclarativeBluetoothSocket(QBluetoothSocket *socket, QDeclarativeBluetoothService *service, QObject *parent)
150     : QObject(parent)
151 {
152     d = new QDeclarativeBluetoothSocketPrivate(this);
153     d->m_service = service;
154     d->m_socket = socket;
155     d->m_connected = true;
156     d->m_componentCompleted = true;
157 
158     QObject::connect(socket, SIGNAL(connected()), this, SLOT(socket_connected()));
159     QObject::connect(socket, SIGNAL(disconnected()), this, SLOT(socket_disconnected()));
160     QObject::connect(socket, SIGNAL(error(QBluetoothSocket::SocketError)), this, SLOT(socket_error(QBluetoothSocket::SocketError)));
161     QObject::connect(socket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), this, SLOT(socket_state(QBluetoothSocket::SocketState)));
162     QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(socket_readyRead()));
163 }
164 
165 
~QDeclarativeBluetoothSocket()166 QDeclarativeBluetoothSocket::~QDeclarativeBluetoothSocket()
167 {
168     delete d;
169 }
170 
componentComplete()171 void QDeclarativeBluetoothSocket::componentComplete()
172 {
173     d->m_componentCompleted = true;
174 
175     if (d->m_connected && d->m_service)
176         d->connect();
177 }
178 
179 /*!
180   \qmlproperty BluetoothService BluetoothSocket::service
181 
182   This property holds the details of the remote service to connect to. It can be
183   set to a static BluetoothService with a fixed description, or a service returned
184   by service discovery.
185   */
186 
187 
service()188 QDeclarativeBluetoothService *QDeclarativeBluetoothSocket::service()
189 {
190     return d->m_service;
191 }
192 
setService(QDeclarativeBluetoothService * service)193 void QDeclarativeBluetoothSocket::setService(QDeclarativeBluetoothService *service)
194 {
195     d->m_service = service;
196 
197     if (!d->m_componentCompleted)
198         return;
199 
200     if (d->m_connected)
201         d->connect();
202     emit serviceChanged();
203 }
204 
205 /*!
206   \qmlproperty bool BluetoothSocket::connected
207 
208   This property holds the connection state of the socket. If the socket is
209   connected to peer, it returns true. It can be set true or false to control the
210   connection. When set to true, the property will not return true until the
211   connection is established.
212 
213   */
214 
215 
connected() const216 bool QDeclarativeBluetoothSocket::connected() const
217 {
218     if (!d->m_socket)
219         return false;
220 
221     return d->m_socket->state() == QBluetoothSocket::ConnectedState;
222 }
223 
setConnected(bool connected)224 void QDeclarativeBluetoothSocket::setConnected(bool connected)
225 {
226     d->m_connected = connected;
227     if (connected && d->m_componentCompleted) {
228         if (d->m_service) {
229             d->connect();
230         }
231         else {
232             qCWarning(QT_BT_QML) << "BluetoothSocket::setConnected called before a service was set";
233         }
234     }
235 
236     if (!connected && d->m_socket){
237         d->m_socket->close();
238     }
239 }
240 
241 /*!
242     \qmlproperty enumeration BluetoothSocket::error
243 
244     This property holds the last error that happened.
245     \list
246         \li \c{NoError}
247         \li \c{UnknownSocketError}
248         \li \c{HostNotFoundError}
249         \li \c{ServiceNotFoundError}
250         \li \c{NetworkError}
251         \li \c{UnsupportedProtocolError}
252         \li \c{RemoteHostClosedError}
253     \endlist
254 
255     The errors are derived from \l QBluetoothSocket::SocketError. This property is read-only.
256 */
257 
258 
error() const259 QDeclarativeBluetoothSocket::Error QDeclarativeBluetoothSocket::error() const
260 {
261     return d->m_error;
262 }
263 
socket_connected()264 void QDeclarativeBluetoothSocket::socket_connected()
265 {
266     emit connectedChanged();
267 }
268 
socket_disconnected()269 void QDeclarativeBluetoothSocket::socket_disconnected()
270 {
271     d->m_socket->deleteLater();
272     d->m_socket = nullptr;
273     emit connectedChanged();
274 }
275 
socket_error(QBluetoothSocket::SocketError error)276 void QDeclarativeBluetoothSocket::socket_error(QBluetoothSocket::SocketError error)
277 {
278     d->m_error = static_cast<QDeclarativeBluetoothSocket::Error>(error);
279 
280     emit errorChanged();
281 }
282 
socket_state(QBluetoothSocket::SocketState state)283 void QDeclarativeBluetoothSocket::socket_state(QBluetoothSocket::SocketState state)
284 {
285     d->m_state = static_cast<QDeclarativeBluetoothSocket::SocketState>(state);
286 
287     emit stateChanged();
288 }
289 
290 /*!
291     \qmlproperty enumeration BluetoothSocket::state
292 
293     This property holds the current state of the socket.
294 
295     \list
296         \li \c{NoServiceSet}
297         \li \c{Unconnected}
298         \li \c{ServiceLookup}
299         \li \c{Connecting}
300         \li \c{Connected}
301         \li \c{Closing}
302         \li \c{Listening}
303         \li \c{Bound}
304     \endlist
305 
306     The states (except \c{NoServiceSet}) are derived from \l QBluetoothSocket::SocketState. This property is read-only.
307     \c{NoServiceSet} indicates that the socket state is not yet available due to the \l service not being
308     set yet.
309 */
state() const310 QDeclarativeBluetoothSocket::SocketState QDeclarativeBluetoothSocket::state() const
311 {
312     return d->m_state;
313 }
314 
socket_readyRead()315 void QDeclarativeBluetoothSocket::socket_readyRead()
316 {
317     emit dataAvailable();
318 }
319 
320 /*!
321   \qmlproperty string BluetoothSocket::stringData
322 
323   This property receives or sends data to a remote Bluetooth device. Arrival of
324   data can be detected by connecting to this properties changed signal and can be read via
325   stringData. Setting stringData will transmit the string.
326   If excessive amounts of data are sent, the function may block sending. Reading will
327   never block.
328   */
329 
stringData()330 QString QDeclarativeBluetoothSocket::stringData()
331 {
332     if (!d->m_socket|| !d->m_socket->bytesAvailable())
333         return QString();
334 
335     QString data;
336     while (d->m_socket->canReadLine()) {
337         QByteArray line = d->m_socket->readLine();
338         data += QString::fromUtf8(line.constData(), line.length());
339     }
340     return data;
341 }
342 
343 /*!
344   This method transmits the string data passed with "data" to the remote device.
345   If excessive amounts of data are sent, the function may block sending.
346  */
347 
sendStringData(const QString & data)348 void QDeclarativeBluetoothSocket::sendStringData(const QString &data)
349 {
350     if (!d->m_connected || !d->m_socket){
351         qCWarning(QT_BT_QML) << "Writing data to unconnected socket";
352         return;
353     }
354 
355     QByteArray text = data.toUtf8() + '\n';
356     d->m_socket->write(text);
357 }
358 
newSocket(QBluetoothSocket * socket,QDeclarativeBluetoothService * service)359 void QDeclarativeBluetoothSocket::newSocket(QBluetoothSocket *socket, QDeclarativeBluetoothService *service)
360 {
361     if (d->m_socket){
362         delete d->m_socket;
363     }
364 
365     d->m_service = service;
366     d->m_socket = socket;
367     d->m_connected = true;
368     d->m_componentCompleted = true;
369     d->m_error = NoError;
370 
371     QObject::connect(socket, &QBluetoothSocket::connected,
372                      this, &QDeclarativeBluetoothSocket::socket_connected);
373     QObject::connect(socket, &QBluetoothSocket::disconnected,
374                      this, &QDeclarativeBluetoothSocket::socket_disconnected);
375     QObject::connect(socket, QOverload<QBluetoothSocket::SocketError>::of(&QBluetoothSocket::error),
376                      this, &QDeclarativeBluetoothSocket::socket_error);
377     QObject::connect(socket, &QBluetoothSocket::stateChanged,
378                      this, &QDeclarativeBluetoothSocket::socket_state);
379     QObject::connect(socket, &QIODevice::readyRead,
380                      this, &QDeclarativeBluetoothSocket::socket_readyRead);
381 
382     socket_state(socket->state());
383 
384     emit connectedChanged();
385 }
386