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
76Q_DECLARE_LOGGING_CATEGORY(QT_BT_QML)
77
78class QDeclarativeBluetoothSocketPrivate
79{
80public:
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
91 ~QDeclarativeBluetoothSocketPrivate()
92 {
93 delete m_socket;
94 }
95
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(service: *m_service->serviceInfo());
115 QObject::connect(sender: m_socket, signal: &QBluetoothSocket::connected,
116 receiver: m_dbs, slot: &QDeclarativeBluetoothSocket::socket_connected);
117 QObject::connect(sender: m_socket, signal: &QBluetoothSocket::disconnected,
118 receiver: m_dbs, slot: &QDeclarativeBluetoothSocket::socket_disconnected);
119 QObject::connect(sender: m_socket, signal: QOverload<QBluetoothSocket::SocketError>::of(ptr: &QBluetoothSocket::error),
120 receiver: m_dbs, slot: &QDeclarativeBluetoothSocket::socket_error);
121 QObject::connect(sender: m_socket, signal: &QBluetoothSocket::stateChanged,
122 receiver: m_dbs, slot: &QDeclarativeBluetoothSocket::socket_state);
123 QObject::connect(sender: m_socket, signal: &QIODevice::readyRead,
124 receiver: m_dbs, slot: &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
136QDeclarativeBluetoothSocket::QDeclarativeBluetoothSocket(QObject *parent) :
137 QObject(parent)
138{
139 d = new QDeclarativeBluetoothSocketPrivate(this);
140}
141
142QDeclarativeBluetoothSocket::QDeclarativeBluetoothSocket(QDeclarativeBluetoothService *service, QObject *parent)
143 : QObject(parent)
144{
145 d = new QDeclarativeBluetoothSocketPrivate(this);
146 d->m_service = service;
147}
148
149QDeclarativeBluetoothSocket::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(sender: socket, SIGNAL(connected()), receiver: this, SLOT(socket_connected()));
159 QObject::connect(sender: socket, SIGNAL(disconnected()), receiver: this, SLOT(socket_disconnected()));
160 QObject::connect(sender: socket, SIGNAL(error(QBluetoothSocket::SocketError)), receiver: this, SLOT(socket_error(QBluetoothSocket::SocketError)));
161 QObject::connect(sender: socket, SIGNAL(stateChanged(QBluetoothSocket::SocketState)), receiver: this, SLOT(socket_state(QBluetoothSocket::SocketState)));
162 QObject::connect(sender: socket, SIGNAL(readyRead()), receiver: this, SLOT(socket_readyRead()));
163}
164
165
166QDeclarativeBluetoothSocket::~QDeclarativeBluetoothSocket()
167{
168 delete d;
169}
170
171void 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
188QDeclarativeBluetoothService *QDeclarativeBluetoothSocket::service()
189{
190 return d->m_service;
191}
192
193void 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
216bool QDeclarativeBluetoothSocket::connected() const
217{
218 if (!d->m_socket)
219 return false;
220
221 return d->m_socket->state() == QBluetoothSocket::ConnectedState;
222}
223
224void 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
259QDeclarativeBluetoothSocket::Error QDeclarativeBluetoothSocket::error() const
260{
261 return d->m_error;
262}
263
264void QDeclarativeBluetoothSocket::socket_connected()
265{
266 emit connectedChanged();
267}
268
269void QDeclarativeBluetoothSocket::socket_disconnected()
270{
271 d->m_socket->deleteLater();
272 d->m_socket = nullptr;
273 emit connectedChanged();
274}
275
276void QDeclarativeBluetoothSocket::socket_error(QBluetoothSocket::SocketError error)
277{
278 d->m_error = static_cast<QDeclarativeBluetoothSocket::Error>(error);
279
280 emit errorChanged();
281}
282
283void 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*/
310QDeclarativeBluetoothSocket::SocketState QDeclarativeBluetoothSocket::state() const
311{
312 return d->m_state;
313}
314
315void 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
330QString 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(str: line.constData(), size: 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
348void 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(data: text);
357}
358
359void 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(sender: socket, signal: &QBluetoothSocket::connected,
372 receiver: this, slot: &QDeclarativeBluetoothSocket::socket_connected);
373 QObject::connect(sender: socket, signal: &QBluetoothSocket::disconnected,
374 receiver: this, slot: &QDeclarativeBluetoothSocket::socket_disconnected);
375 QObject::connect(sender: socket, signal: QOverload<QBluetoothSocket::SocketError>::of(ptr: &QBluetoothSocket::error),
376 receiver: this, slot: &QDeclarativeBluetoothSocket::socket_error);
377 QObject::connect(sender: socket, signal: &QBluetoothSocket::stateChanged,
378 receiver: this, slot: &QDeclarativeBluetoothSocket::socket_state);
379 QObject::connect(sender: socket, signal: &QIODevice::readyRead,
380 receiver: this, slot: &QDeclarativeBluetoothSocket::socket_readyRead);
381
382 socket_state(state: socket->state());
383
384 emit connectedChanged();
385}
386

source code of qtconnectivity/src/imports/bluetooth/qdeclarativebluetoothsocket.cpp