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: |
89 | QDeclarativeBluetoothServicePrivate() |
90 | : m_componentComplete(false) |
91 | { |
92 | |
93 | } |
94 | |
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 | |
106 | QDeclarativeBluetoothService::QDeclarativeBluetoothService(QObject *parent) : |
107 | QObject(parent) |
108 | { |
109 | d = new QDeclarativeBluetoothServicePrivate; |
110 | d->m_service = new QBluetoothServiceInfo(); |
111 | } |
112 | |
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 | |
120 | QDeclarativeBluetoothService::~QDeclarativeBluetoothService() |
121 | { |
122 | delete d; |
123 | } |
124 | |
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 | |
141 | 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 | |
156 | QString QDeclarativeBluetoothService::deviceAddress() const |
157 | { |
158 | return d->m_service->device().address().toString(); |
159 | } |
160 | |
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 | |
176 | QString QDeclarativeBluetoothService::serviceName() const |
177 | { |
178 | return d->m_service->serviceName(); |
179 | } |
180 | |
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 | */ |
194 | QString QDeclarativeBluetoothService::serviceDescription() const |
195 | { |
196 | return d->m_service->serviceDescription(); |
197 | } |
198 | |
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 | |
226 | QDeclarativeBluetoothService::Protocol QDeclarativeBluetoothService::serviceProtocol() const |
227 | { |
228 | return d->m_protocol; |
229 | } |
230 | |
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 | |
246 | QString QDeclarativeBluetoothService::serviceUuid() const |
247 | { |
248 | return d->m_service->serviceUuid().toString(); |
249 | } |
250 | |
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 | |
264 | bool QDeclarativeBluetoothService::isRegistered() const |
265 | { |
266 | return d->m_service->isRegistered(); |
267 | } |
268 | |
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(sender: d->m_server, signal: &QBluetoothServer::newConnection, |
299 | receiver: this, slot: &QDeclarativeBluetoothService::new_connection); |
300 | |
301 | d->m_service->setAttribute(attributeId: QBluetoothServiceInfo::ServiceRecordHandle, value: (uint)0x00010010); |
302 | |
303 | QBluetoothServiceInfo::Sequence classId; |
304 | classId << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::SerialPort)); |
305 | d->m_service->setAttribute(attributeId: QBluetoothServiceInfo::ServiceClassIds, value: classId); |
306 | |
307 | //qDebug() << "name/uuid" << d->m_name << d->m_uuid << d->m_port; |
308 | |
309 | QBluetoothServiceInfo::Sequence publicBrowse; |
310 | publicBrowse << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::PublicBrowseGroup)); |
311 | d->m_service->setAttribute(attributeId: QBluetoothServiceInfo::BrowseGroupList, value: publicBrowse); |
312 | |
313 | QBluetoothServiceInfo::Sequence protocolDescriptorList; |
314 | QBluetoothServiceInfo::Sequence protocol; |
315 | |
316 | if (d->m_protocol == L2CapProtocol) { |
317 | protocol << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::L2cap)) |
318 | << QVariant::fromValue(value: 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(value: QBluetoothUuid(QBluetoothUuid::L2cap)); |
324 | protocolDescriptorList.append(t: QVariant::fromValue(value: l2cpProtocol)); |
325 | } |
326 | protocol << QVariant::fromValue(value: QBluetoothUuid(QBluetoothUuid::Rfcomm)) |
327 | << QVariant::fromValue(value: quint8(d->m_server->serverPort())); |
328 | } |
329 | protocolDescriptorList.append(t: QVariant::fromValue(value: protocol)); |
330 | |
331 | d->m_service->setAttribute(attributeId: QBluetoothServiceInfo::ProtocolDescriptorList, |
332 | value: 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 | |
340 | QBluetoothServiceInfo *QDeclarativeBluetoothService::serviceInfo() const |
341 | { |
342 | return d->m_service; |
343 | } |
344 | |
345 | void QDeclarativeBluetoothService::new_connection() |
346 | { |
347 | emit newClient(); |
348 | } |
349 | |
350 | QDeclarativeBluetoothSocket *QDeclarativeBluetoothService::nextClient() |
351 | { |
352 | QBluetoothServer *server = qobject_cast<QBluetoothServer *>(object: 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 | |
366 | void QDeclarativeBluetoothService::assignNextClient(QDeclarativeBluetoothSocket *dbs) |
367 | { |
368 | QBluetoothServer *server = qobject_cast<QBluetoothServer *>(object: d->m_server); |
369 | if (server) { |
370 | if (server->hasPendingConnections()) { |
371 | QBluetoothSocket *socket = server->nextPendingConnection(); |
372 | dbs->newSocket(socket, service: 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 | |