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