Merge remote-tracking branch 'origin/stable' into dev
[qt:qtconnectivity.git] / src / bluetooth / qbluetoothservicediscoveryagent.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qbluetoothhostinfo.h"
43 #include "qbluetoothlocaldevice.h"
44 #include "qbluetoothservicediscoveryagent.h"
45 #include "qbluetoothservicediscoveryagent_p.h"
46
47 #include "qbluetoothdevicediscoveryagent.h"
48
49 QT_BEGIN_NAMESPACE
50
51 /*!
52     \class QBluetoothServiceDiscoveryAgent
53     \inmodule QtBluetooth
54     \brief The QBluetoothServiceDiscoveryAgent class enables you to query for
55     Bluetooth services.
56
57     To query the services provided by all contactable Bluetooth devices:
58     \list
59     \li create an instance of QBluetoothServiceDiscoveryAgent,
60     \li connect to either the serviceDiscovered() or finished() signals,
61     \li and call start().
62     \endlist
63
64     \snippet doc_src_qtbluetooth.cpp discovery
65
66     By default a minimal service discovery is performed. In this mode, the QBluetotohServiceInfo
67     objects returned are guaranteed to contain only device and service UUID information. Depending
68     on platform and device capabilities, other service information may also be available. For most
69     use cases this is adequate as QBluetoothSocket::connectToService() will perform additional
70     discovery if required.  If full service information is required, pass \l FullDiscovery as the
71     discoveryMode parameter to start().
72
73     This class may internally utilize \l QBluetoothDeviceDiscoveryAgent to find unknown devices.
74
75     \sa QBluetoothDeviceDiscoveryAgent
76 */
77
78 /*!
79     \enum QBluetoothServiceDiscoveryAgent::Error
80
81     This enum describes errors that can occur during service discovery.
82
83     \value NoError          No error has occurred.
84     \value PoweredOffError  The Bluetooth adaptor is powered off, power it on before doing discovery.
85     \value InputOutputError    Writing or reading from the device resulted in an error.
86     \value InvalidBluetoothAdapterError The passed local adapter address does not match the physical
87                                         adapter address of any local Bluetooth device.
88     \value UnknownError     An unknown error has occurred.
89 */
90
91 /*!
92     \enum QBluetoothServiceDiscoveryAgent::DiscoveryMode
93
94     This enum describes the service discovery mode.
95
96     \value MinimalDiscovery     Performs a minimal service discovery. The QBluetoothServiceInfo
97     objects returned may be incomplete and are only guaranteed to contain device and service UUID information.
98     \value FullDiscovery        Performs a full service discovery.
99 */
100
101 /*!
102     \fn QBluetoothServiceDiscoveryAgent::serviceDiscovered(const QBluetoothServiceInfo &info)
103
104     This signal is emitted when the Bluetooth service described by \a info is discovered.
105 */
106
107 /*!
108     \fn QBluetoothServiceDiscoveryAgent::finished()
109
110     This signal is emitted when the Bluetooth service discovery completes.
111
112     Unlike the \l QBluetoothDeviceDiscoveryAgent::finished() signal this
113     signal will even be emitted when an error occurred during the service discovery. Therefore
114     it is recommended to check the \l error() signal to evaluate the success of the
115     service discovery discovery.
116 */
117
118 /*!
119     \fn void QBluetoothServiceDiscoveryAgent::error(QBluetoothServiceDiscoveryAgent::Error error)
120
121     This signal is emitted when an \a error occurs. The \a error parameter describes the error that
122     occurred.
123 */
124
125 /*!
126     Constructs a new QBluetoothServiceDiscoveryAgent with \a parent. The search is performed via the
127     local default Bluetooth adapter.
128 */
129 QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent)
130 : QObject(parent), d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(QBluetoothAddress()))
131 {
132      d_ptr->q_ptr = this;
133 }
134
135 /*!
136     Constructs a new QBluetoothServiceDiscoveryAgent for \a deviceAdapter and with \a parent.
137
138     It uses \a deviceAdapter for the service search. If \a deviceAdapter is default constructed
139     the resulting QBluetoothServiceDiscoveryAgent object will use the local default Bluetooth adapter.
140
141     If a \a deviceAdapter is specified that is not a local adapter \l error() will be set to
142     \l InvalidBluetoothAdapterError. Therefore it is recommended to test the error flag immediately after
143     using this constructor.
144
145     \sa error()
146 */
147 QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent)
148 : QObject(parent), d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(deviceAdapter))
149 {
150     d_ptr->q_ptr = this;
151     if (!deviceAdapter.isNull()) {
152         const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices();
153         foreach (const QBluetoothHostInfo &hostInfo, localDevices) {
154             if (hostInfo.address() == deviceAdapter)
155                 return;
156         }
157         d_ptr->error = InvalidBluetoothAdapterError;
158         d_ptr->errorString = tr("Invalid Bluetooth adapter address");
159     }
160 }
161
162 /*!
163
164   Destructor for QBluetoothServiceDiscoveryAgent
165
166 */
167
168 QBluetoothServiceDiscoveryAgent::~QBluetoothServiceDiscoveryAgent()
169 {
170     delete d_ptr;
171 }
172
173 /*!
174     Returns the list of all discovered services.
175 */
176 QList<QBluetoothServiceInfo> QBluetoothServiceDiscoveryAgent::discoveredServices() const
177 {
178     Q_D(const QBluetoothServiceDiscoveryAgent);
179
180     return d->discoveredServices;
181 }
182 /*!
183     Sets the UUID filter to \a uuids.  Only services matching the UUIDs in \a uuids will be
184     returned.
185
186     An empty UUID list is equivalent to a list containing only QBluetoothUuid::PublicBrowseGroup.
187
188     \sa uuidFilter()
189 */
190 void QBluetoothServiceDiscoveryAgent::setUuidFilter(const QList<QBluetoothUuid> &uuids)
191 {
192     Q_D(QBluetoothServiceDiscoveryAgent);
193
194     d->uuidFilter = uuids;
195 }
196
197 /*!
198     This is an overloaded member function, provided for convenience.
199
200     Sets the UUID filter to a list containing the single element \a uuid.
201
202     \sa uuidFilter()
203 */
204 void QBluetoothServiceDiscoveryAgent::setUuidFilter(const QBluetoothUuid &uuid)
205 {
206     Q_D(QBluetoothServiceDiscoveryAgent);
207
208     d->uuidFilter.clear();
209     d->uuidFilter.append(uuid);
210 }
211
212 /*!
213     Returns the UUID filter.
214
215     \sa setUuidFilter()
216 */
217 QList<QBluetoothUuid> QBluetoothServiceDiscoveryAgent::uuidFilter() const
218 {
219     Q_D(const QBluetoothServiceDiscoveryAgent);
220
221     return d->uuidFilter;
222 }
223
224 /*!
225     Sets remote device address to \a address. If \a address is null, services will be discovered
226     on all contactable Bluetooth devices. A new remote address can only be set while there is
227     no service discovery in progress; otherwise this function returns false.
228
229 */
230 bool QBluetoothServiceDiscoveryAgent::setRemoteAddress(const QBluetoothAddress &address)
231 {
232     if (isActive())
233         return false;
234     if (!address.isNull())
235         d_ptr->singleDevice = true;
236     d_ptr->deviceAddress = address;
237
238     return true;
239 }
240
241 /*!
242     Returns the remote device address. If setRemoteAddress is not called, the function
243     will return default QBluetoothAddress.
244
245 */
246 QBluetoothAddress QBluetoothServiceDiscoveryAgent::remoteAddress() const
247 {
248     if (d_ptr->singleDevice == true)
249         return d_ptr->deviceAddress;
250     else
251         return QBluetoothAddress();
252 }
253
254 /*!
255     Starts service discovery. \a mode specifies the type of service discovery to perform.
256
257     On BlackBerry devices, device discovery may lead to pairing requests.
258
259     \sa DiscoveryMode
260 */
261 void QBluetoothServiceDiscoveryAgent::start(DiscoveryMode mode)
262 {
263     Q_D(QBluetoothServiceDiscoveryAgent);
264
265     if (d->discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::Inactive
266             && d->error != InvalidBluetoothAdapterError) {
267         d->setDiscoveryMode(mode);
268         if (d->deviceAddress.isNull()) {
269             d->startDeviceDiscovery();
270         } else {
271             d->discoveredDevices << QBluetoothDeviceInfo(d->deviceAddress, QString(), 0);
272             d->startServiceDiscovery();
273         }
274     }
275 }
276
277 /*!
278     Stops service discovery.
279 */
280 void QBluetoothServiceDiscoveryAgent::stop()
281 {
282     Q_D(QBluetoothServiceDiscoveryAgent);
283
284     switch (d->discoveryState()) {
285     case QBluetoothServiceDiscoveryAgentPrivate::DeviceDiscovery:
286         d->stopDeviceDiscovery();
287         break;
288     case QBluetoothServiceDiscoveryAgentPrivate::ServiceDiscovery:
289         d->stopServiceDiscovery();
290     default:
291         ;
292     }
293
294     d->discoveredDevices.clear();
295 }
296
297 /*!
298     Clears the results of a previous service discovery.
299 */
300 void QBluetoothServiceDiscoveryAgent::clear()
301 {
302     Q_D(QBluetoothServiceDiscoveryAgent);
303
304     d->discoveredDevices.clear();
305     d->discoveredServices.clear();
306     d->uuidFilter.clear();
307 }
308
309 /*!
310     Returns true if service discovery is currently active, otherwise returns false.
311 */
312 bool QBluetoothServiceDiscoveryAgent::isActive() const
313 {
314     Q_D(const QBluetoothServiceDiscoveryAgent);
315
316     return d->state != QBluetoothServiceDiscoveryAgentPrivate::Inactive;
317 }
318
319 /*!
320     Returns the type of error that last occurred. If service discovery is done
321     on a signle address it will return errors that occured while trying to discover
322     services on that device. If the alternate constructor is used and devices are
323     discovered by a scan, errors during service discovery on individual
324     devices are not saved and no signals are emitted. In this case, errors are
325     fairly normal as some devices may not respond to discovery or
326     may no longer be in range.  Such errors are surpressed.  If no services
327     are returned, it can be assumed no services could be discovered.
328
329 */
330 QBluetoothServiceDiscoveryAgent::Error QBluetoothServiceDiscoveryAgent::error() const
331 {
332     Q_D(const QBluetoothServiceDiscoveryAgent);
333
334     return d->error;
335 }
336
337 /*!
338     Returns a human-readable description of the last error that occurred during
339     service discovery on a single device.
340 */
341 QString QBluetoothServiceDiscoveryAgent::errorString() const
342 {
343     Q_D(const QBluetoothServiceDiscoveryAgent);
344     return d->errorString;
345 }
346
347
348 /*!
349     \fn QBluetoothServiceDiscoveryAgent::canceled()
350     Signals the cancellation of the service discovery.
351  */
352
353
354 /*!
355     Starts device discovery.
356 */
357 void QBluetoothServiceDiscoveryAgentPrivate::startDeviceDiscovery()
358 {
359     Q_Q(QBluetoothServiceDiscoveryAgent);
360
361     if (!deviceDiscoveryAgent) {
362 #ifdef QT_BLUEZ_BLUETOOTH
363         deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(m_deviceAdapterAddress, q);
364 #else
365         deviceDiscoveryAgent = new QBluetoothDeviceDiscoveryAgent(q);
366 #endif
367         QObject::connect(deviceDiscoveryAgent, SIGNAL(finished()),
368                          q, SLOT(_q_deviceDiscoveryFinished()));
369         QObject::connect(deviceDiscoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),
370                          q, SLOT(_q_deviceDiscovered(QBluetoothDeviceInfo)));
371         QObject::connect(deviceDiscoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)),
372                          q, SLOT(_q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error)));
373
374     }
375
376     setDiscoveryState(DeviceDiscovery);
377
378     deviceDiscoveryAgent->start();
379 }
380
381 /*!
382     Stops device discovery.
383 */
384 void QBluetoothServiceDiscoveryAgentPrivate::stopDeviceDiscovery()
385 {
386     deviceDiscoveryAgent->stop();
387     delete deviceDiscoveryAgent;
388     deviceDiscoveryAgent = 0;
389
390     setDiscoveryState(Inactive);
391
392     Q_Q(QBluetoothServiceDiscoveryAgent);
393     emit q->canceled();
394 }
395
396 /*!
397     Called when device discovery finishes.
398 */
399 void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryFinished()
400 {
401     if (deviceDiscoveryAgent->error() != QBluetoothDeviceDiscoveryAgent::NoError) {
402         //Forward the device discovery error
403         error = static_cast<QBluetoothServiceDiscoveryAgent::Error>(deviceDiscoveryAgent->error());
404         errorString = deviceDiscoveryAgent->errorString();
405         setDiscoveryState(Inactive);
406         Q_Q(QBluetoothServiceDiscoveryAgent);
407         emit q->error(error);
408         emit q->finished();
409         return;
410     }
411
412 //    discoveredDevices = deviceDiscoveryAgent->discoveredDevices();
413
414     delete deviceDiscoveryAgent;
415     deviceDiscoveryAgent = 0;
416
417     startServiceDiscovery();
418 }
419
420 void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscovered(const QBluetoothDeviceInfo &info)
421 {
422     // look for duplicates, and cached entries
423     for (int i = 0; i < discoveredDevices.count(); i++) {
424         if (discoveredDevices.at(i).address() == info.address())
425             discoveredDevices.removeAt(i);
426     }
427     discoveredDevices.prepend(info);
428 }
429
430 void QBluetoothServiceDiscoveryAgentPrivate::_q_deviceDiscoveryError(QBluetoothDeviceDiscoveryAgent::Error newError)
431 {
432     error = static_cast<QBluetoothServiceDiscoveryAgent::Error>(newError);
433     errorString = deviceDiscoveryAgent->errorString();
434
435     deviceDiscoveryAgent->stop();
436     delete deviceDiscoveryAgent;
437     deviceDiscoveryAgent = 0;
438
439     setDiscoveryState(Inactive);
440     Q_Q(QBluetoothServiceDiscoveryAgent);
441     emit q->error(error);
442     emit q->finished();
443 }
444
445 /*!
446     Starts service discovery for the next device.
447 */
448 void QBluetoothServiceDiscoveryAgentPrivate::startServiceDiscovery()
449 {
450     Q_Q(QBluetoothServiceDiscoveryAgent);
451
452     if (discoveredDevices.isEmpty()) {
453         setDiscoveryState(Inactive);
454         emit q->finished();
455         return;
456     }
457
458     setDiscoveryState(ServiceDiscovery);
459     start(discoveredDevices.at(0).address());
460 }
461
462 /*!
463     Stops service discovery.
464 */
465 void QBluetoothServiceDiscoveryAgentPrivate::stopServiceDiscovery()
466 {
467     stop();
468
469     setDiscoveryState(Inactive);
470 }
471
472 void QBluetoothServiceDiscoveryAgentPrivate::_q_serviceDiscoveryFinished()
473 {
474     if(!discoveredDevices.isEmpty()) {
475         discoveredDevices.removeFirst();
476     }
477
478     startServiceDiscovery();
479 }
480
481 #include "moc_qbluetoothservicediscoveryagent.cpp"
482
483 QT_END_NAMESPACE