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 "qdeclarativebluetoothservice_p.h"
41
42 #include <QtCore/QLoggingCategory>
43
44 #include <QtBluetooth/QBluetoothDeviceInfo>
45 #include <QtBluetooth/QBluetoothSocket>
46 #include <QtBluetooth/QBluetoothAddress>
47 #include <QtBluetooth/QBluetoothServer>
48
49 #include <QObject>
50
51 /* ==================== QDeclarativeBluetoothService ======================= */
52
53 /*!
54 \qmltype BluetoothService
55 \instantiates QDeclarativeBluetoothService
56 \inqmlmodule QtBluetooth
57 \since 5.2
58 \brief Provides information about a particular Bluetooth service.
59
60 \sa QBluetoothAddress
61 \sa QBluetoothSocket
62
63 It allows a QML project to get information about a remote service, or describe a service
64 for a BluetoothSocket to connect to.
65 */
66
67 /*!
68 \qmlsignal BluetoothService::detailsChanged()
69
70 This signal is emitted when any of the following properties changes:
71
72 \list
73 \li deviceAddress
74 \li deviceName
75 \li serviceDescription
76 \li serviceName
77 \li serviceProtocol
78 \li serviceUuid
79 \endlist
80
81 The corresponding handler is \c onDetailsChanged.
82 */
83
84 Q_DECLARE_LOGGING_CATEGORY(QT_BT_QML)
85
86 class QDeclarativeBluetoothServicePrivate
87 {
88 public:
QDeclarativeBluetoothServicePrivate()89 QDeclarativeBluetoothServicePrivate()
90 : m_componentComplete(false)
91 {
92
93 }
94
~QDeclarativeBluetoothServicePrivate()95 ~QDeclarativeBluetoothServicePrivate()
96 {
97 delete m_service;
98 }
99
100 bool m_componentComplete;
101 QBluetoothServiceInfo *m_service = nullptr;
102 QDeclarativeBluetoothService::Protocol m_protocol;
103 QBluetoothServer *m_server = nullptr;
104 };
105
QDeclarativeBluetoothService(QObject * parent)106 QDeclarativeBluetoothService::QDeclarativeBluetoothService(QObject *parent) :
107 QObject(parent)
108 {
109 d = new QDeclarativeBluetoothServicePrivate;
110 d->m_service = new QBluetoothServiceInfo();
111 }
112
QDeclarativeBluetoothService(const QBluetoothServiceInfo & service,QObject * parent)113 QDeclarativeBluetoothService::QDeclarativeBluetoothService(const QBluetoothServiceInfo &service, QObject *parent)
114 : QObject(parent)
115 {
116 d = new QDeclarativeBluetoothServicePrivate;
117 d->m_service = new QBluetoothServiceInfo(service);
118 }
119
~QDeclarativeBluetoothService()120 QDeclarativeBluetoothService::~QDeclarativeBluetoothService()
121 {
122 delete d;
123 }
124
componentComplete()125 void QDeclarativeBluetoothService::componentComplete()
126 {
127 d->m_componentComplete = true;
128
129 if (!d->m_service->isRegistered())
130 setRegistered(true);
131 }
132
133
134 /*!
135 \qmlproperty string BluetoothService::deviceName
136
137 This property holds the name of the remote device. Changing this property emits the
138 detailsChanged signal.
139 */
140
deviceName() const141 QString QDeclarativeBluetoothService::deviceName() const
142 {
143
144 return d->m_service->device().name();
145 }
146
147 /*!
148 \qmlproperty string BluetoothService::deviceAddress
149
150 This property holds the remote device's MAC address. It must be a valid address to
151 connect to a remote device using a Bluetooth socket. Changing this property emits the
152 detailsChanged signal.
153
154 */
155
deviceAddress() const156 QString QDeclarativeBluetoothService::deviceAddress() const
157 {
158 return d->m_service->device().address().toString();
159 }
160
setDeviceAddress(const QString & newAddress)161 void QDeclarativeBluetoothService::setDeviceAddress(const QString &newAddress)
162 {
163 QBluetoothAddress address(newAddress);
164 QBluetoothDeviceInfo device(address, QString(), QBluetoothDeviceInfo::ComputerDevice);
165 d->m_service->setDevice(device);
166 emit detailsChanged();
167 }
168
169 /*!
170 \qmlproperty string BluetoothService::serviceName
171
172 This property holds the name of the remote service if available. Changing this property emits the
173 detailsChanged signal.
174 */
175
serviceName() const176 QString QDeclarativeBluetoothService::serviceName() const
177 {
178 return d->m_service->serviceName();
179 }
180
setServiceName(const QString & name)181 void QDeclarativeBluetoothService::setServiceName(const QString &name)
182 {
183 d->m_service->setServiceName(name);
184 emit detailsChanged();
185 }
186
187
188 /*!
189 \qmlproperty string BluetoothService::serviceDescription
190
191 This property holds the description provided by the remote service. Changing this property emits the
192 detailsChanged signal.
193 */
serviceDescription() const194 QString QDeclarativeBluetoothService::serviceDescription() const
195 {
196 return d->m_service->serviceDescription();
197 }
198
setServiceDescription(const QString & description)199 void QDeclarativeBluetoothService::setServiceDescription(const QString &description)
200 {
201 d->m_service->setServiceDescription(description);
202 emit detailsChanged();
203 }
204
205 /*!
206 \qmlproperty enumeration BluetoothService::serviceProtocol
207
208 This property holds the protocol used for the service. Changing this property emits the
209 detailsChanged signal.
210
211 Possible values for this property are:
212 \table
213 \header \li Property \li Description
214 \row \li \c BluetoothService.RfcommProtocol
215 \li The Rfcomm protocol is used.
216 \row \li \c BluetoothService.L2capProtocol
217 \li The L2cap protocol is used.
218 \row \li \c BluetoothService.UnknownProtocol
219 \li The protocol is unknown.
220 \endtable
221
222 \sa QBluetoothServiceInfo::Protocol
223
224 */
225
serviceProtocol() const226 QDeclarativeBluetoothService::Protocol QDeclarativeBluetoothService::serviceProtocol() const
227 {
228 return d->m_protocol;
229 }
230
setServiceProtocol(QDeclarativeBluetoothService::Protocol protocol)231 void QDeclarativeBluetoothService::setServiceProtocol(QDeclarativeBluetoothService::Protocol protocol)
232 {
233 d->m_protocol = protocol;
234 emit detailsChanged();
235 }
236
237 /*!
238 \qmlproperty string BluetoothService::serviceUuid
239
240 This property holds the UUID of the remote service. Service UUID,
241 and the address must be set to connect to a remote service. Changing
242 this property emits the detailsChanged signal.
243
244 */
245
serviceUuid() const246 QString QDeclarativeBluetoothService::serviceUuid() const
247 {
248 return d->m_service->serviceUuid().toString();
249 }
250
setServiceUuid(const QString & uuid)251 void QDeclarativeBluetoothService::setServiceUuid(const QString &uuid)
252 {
253 d->m_service->setServiceUuid(QBluetoothUuid(uuid));
254 emit detailsChanged();
255 }
256
257 /*!
258 \qmlproperty string BluetoothService::registered
259
260 This property holds the registration/publication status of the service. If true, the service
261 is published during service discovery.
262 */
263
isRegistered() const264 bool QDeclarativeBluetoothService::isRegistered() const
265 {
266 return d->m_service->isRegistered();
267 }
268
setRegistered(bool registered)269 void QDeclarativeBluetoothService::setRegistered(bool registered)
270 {
271 if (!d->m_componentComplete) {
272 return;
273 }
274
275 delete d->m_server;
276 d->m_server = nullptr;
277
278 if (!registered) {
279 d->m_service->unregisterService();
280 emit registeredChanged();
281 return;
282 }
283
284 if (d->m_protocol == UnknownProtocol) {
285 qCWarning(QT_BT_QML) << "Unknown protocol, can't make service" << d->m_protocol;
286 return;
287 }
288
289 QBluetoothServer *server
290 = new QBluetoothServer(static_cast<QBluetoothServiceInfo::Protocol>(d->m_protocol));
291 server->setMaxPendingConnections(1);
292 if (!server->listen()) {
293 qCWarning(QT_BT_QML) << "Could not start server. Error:" << server->error();
294 return;
295 }
296
297 d->m_server = server;
298 connect(d->m_server, &QBluetoothServer::newConnection,
299 this, &QDeclarativeBluetoothService::new_connection);
300
301 d->m_service->setAttribute(QBluetoothServiceInfo::ServiceRecordHandle, (uint)0x00010010);
302
303 QBluetoothServiceInfo::Sequence classId;
304 classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::SerialPort));
305 d->m_service->setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
306
307 //qDebug() << "name/uuid" << d->m_name << d->m_uuid << d->m_port;
308
309 QBluetoothServiceInfo::Sequence publicBrowse;
310 publicBrowse << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup));
311 d->m_service->setAttribute(QBluetoothServiceInfo::BrowseGroupList, publicBrowse);
312
313 QBluetoothServiceInfo::Sequence protocolDescriptorList;
314 QBluetoothServiceInfo::Sequence protocol;
315
316 if (d->m_protocol == L2CapProtocol) {
317 protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap))
318 << QVariant::fromValue(quint16(d->m_server->serverPort()));
319 } else if (d->m_protocol == RfcommProtocol) {
320 //rfcomm implies l2cp protocol
321 {
322 QBluetoothServiceInfo::Sequence l2cpProtocol;
323 l2cpProtocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::L2cap));
324 protocolDescriptorList.append(QVariant::fromValue(l2cpProtocol));
325 }
326 protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::Rfcomm))
327 << QVariant::fromValue(quint8(d->m_server->serverPort()));
328 }
329 protocolDescriptorList.append(QVariant::fromValue(protocol));
330
331 d->m_service->setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList,
332 protocolDescriptorList);
333
334 if (d->m_service->registerService())
335 emit registeredChanged();
336 else
337 qCWarning(QT_BT_QML) << "Register service failed"; // TODO: propagate this error to the user
338 }
339
serviceInfo() const340 QBluetoothServiceInfo *QDeclarativeBluetoothService::serviceInfo() const
341 {
342 return d->m_service;
343 }
344
new_connection()345 void QDeclarativeBluetoothService::new_connection()
346 {
347 emit newClient();
348 }
349
nextClient()350 QDeclarativeBluetoothSocket *QDeclarativeBluetoothService::nextClient()
351 {
352 QBluetoothServer *server = qobject_cast<QBluetoothServer *>(d->m_server);
353 if (server) {
354 if (server->hasPendingConnections()) {
355 QBluetoothSocket *socket = server->nextPendingConnection();
356 return new QDeclarativeBluetoothSocket(socket, this, nullptr);
357 }
358 else {
359 qCWarning(QT_BT_QML) << "Socket has no pending connection, failing";
360 return nullptr;
361 }
362 }
363 return nullptr;
364 }
365
assignNextClient(QDeclarativeBluetoothSocket * dbs)366 void QDeclarativeBluetoothService::assignNextClient(QDeclarativeBluetoothSocket *dbs)
367 {
368 QBluetoothServer *server = qobject_cast<QBluetoothServer *>(d->m_server);
369 if (server) {
370 if (server->hasPendingConnections()) {
371 QBluetoothSocket *socket = server->nextPendingConnection();
372 dbs->newSocket(socket, this);
373 return;
374 }
375 else {
376 qCWarning(QT_BT_QML) << "Socket has no pending connection, failing";
377 return;
378 }
379 }
380 return;
381 }
382
383