diff options
18 files changed, 431 insertions, 98 deletions
diff --git a/.qmake.conf b/.qmake.conf index 33a2710f..4ef70163 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,3 @@ load(qt_build_config) -CONFIG += qt_example_installs MODULE_VERSION = 5.7.0 diff --git a/examples/nfc/poster/doc/images/qml-poster-example.png b/examples/nfc/poster/doc/images/qml-poster-example.png Binary files differindex d3ead72e..8ed2292c 100644 --- a/examples/nfc/poster/doc/images/qml-poster-example.png +++ b/examples/nfc/poster/doc/images/qml-poster-example.png diff --git a/examples/nfc/poster/doc/src/poster.qdoc b/examples/nfc/poster/doc/src/poster.qdoc index a569f47a..26320f56 100644 --- a/examples/nfc/poster/doc/src/poster.qdoc +++ b/examples/nfc/poster/doc/src/poster.qdoc @@ -31,7 +31,7 @@ \brief A QML example about reading and displaying NFC Data Exchange Format (NDEF) messages. The QML Poster example displays the contents of specifically formatted NFC Data -Exchange Format (NDEF) messages read from an NFC Tag. The NDEF message should +Exchange Format (NDEF) messages read from an NFC Tag. The NDEF message must contain a URI record, an optional \c image/* MIME record, and one or more localized Text records. @@ -39,5 +39,24 @@ localized Text records. \include examples-run.qdocinc +\section1 Applying NDEF Filters + +The example is designed to display the content of a very specific type of NFC tag. +The tag must contain at least one URI record and one text record. If those two +record types do not exist, nothing will happen. Such filtering is applied via the +\l NearField type's filter property. The property accepts a list of \l NdefFilter objects. + +\snippet poster/poster.qml QML NDEF filtering + +\section1 Processing Found NDEF Messages + +Once an appropriate tag is found, the \l NearField::messageRecords property reflects the content. +It transports the list of found NDEF records. The QML snippet below +demonstrates how these records can be accessed: + +\snippet poster/poster.qml messageRecordsChanged 1 +\snippet poster/poster.qml messageRecordsChanged 2 +\snippet poster/poster.qml messageRecordsChanged 3 + \sa {Qt NFC} */ diff --git a/examples/nfc/poster/poster.qml b/examples/nfc/poster/poster.qml index a70a998b..d57ed5d9 100644 --- a/examples/nfc/poster/poster.qml +++ b/examples/nfc/poster/poster.qml @@ -62,13 +62,18 @@ Rectangle { } } + //! [QML NDEF filtering] filter: [ NdefFilter { type: "U"; typeNameFormat: NdefRecord.NfcRtd; maximum: 1 }, NdefFilter { type: "T"; typeNameFormat: NdefRecord.NfcRtd }, NdefFilter { typeNameFormat: NdefRecord.Mime; minimum: 0; maximum: 1 } ] + //! [QML NDEF filtering] + + //! [messageRecordsChanged 1] onMessageRecordsChanged: { + //! [messageRecordsChanged 1] posterText.text = ""; posterImage.source = ""; posterUrl.text = ""; @@ -76,6 +81,7 @@ Rectangle { var currentLocaleMatch = NdefTextRecord.LocaleMatchedNone; var i; var found = false; + //! [messageRecordsChanged 2] for (i = 0; i < messageRecords.length; ++i) { switch (messageRecords[i].typeNameFormat) { case NdefRecord.NfcRtd: @@ -102,18 +108,24 @@ Rectangle { if (!found) console.warn("Unknown NFC tag detected. Cannot display content.") } + //! [messageRecordsChanged 2] root.state = "show"; + //! [messageRecordsChanged 3] } + //! [messageRecordsChanged 3] } Text { id: touchText anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - text: "Touch an NFC tag" + text: "Touch an NFC tag with at least one Text and one URI record." font.bold: true font.pointSize: 18 + wrapMode: Text.WordWrap + width: root.width*0.75 + horizontalAlignment: Text.AlignHCenter } Image { diff --git a/src/bluetooth/bluez/bluez5_helper.cpp b/src/bluetooth/bluez/bluez5_helper.cpp index 2d431367..8871a872 100644 --- a/src/bluetooth/bluez/bluez5_helper.cpp +++ b/src/bluetooth/bluez/bluez5_helper.cpp @@ -266,8 +266,24 @@ void QtBluezDiscoveryManager::PropertiesChanged(const QString &interface, && d->references.contains(propIface->path()) && changed_properties.contains(QStringLiteral("Discovering"))) { bool isDiscovering = changed_properties.value(QStringLiteral("Discovering")).toBool(); - if (!isDiscovering) - removeAdapterFromMonitoring(propIface->path()); + if (!isDiscovering) { + + /* + Once we stop the Discovering flag will switch a few ms later. This comes through this code + path. If a new device discovery is started while we are still + waiting for the flag change signal, then the new device discovery will be aborted prematurely. + To compensate we check whether there was renewed interest. + */ + + AdapterData *data = d->references[propIface->path()]; + if (!data) { + removeAdapterFromMonitoring(propIface->path()); + } else { + OrgBluezAdapter1Interface iface(QStringLiteral("org.bluez"), propIface->path(), + QDBusConnection::systemBus()); + iface.StartDiscovery(); + } + } } } diff --git a/src/bluetooth/doc/src/bluetooth-index.qdoc b/src/bluetooth/doc/src/bluetooth-index.qdoc index 8ea4c7f9..51c79ca4 100644 --- a/src/bluetooth/doc/src/bluetooth-index.qdoc +++ b/src/bluetooth/doc/src/bluetooth-index.qdoc @@ -63,6 +63,19 @@ import statement in your \c .qml file: \section1 Related Information +\section2 Building Qt Bluetooth + +Despite the fact that the module can be built for all Qt platforms, +the module is not ported to all of them. Not supported platforms such as Windows desktop +employ a fake or dummy backend which is automatically selected when the +platform is not supported. The dummy backend reports appropriate error messages +and values which allow the Qt Bluetooth developer to detect at runtime that the +current platform is not supported. The dummy backend is also selected on Linux if +BlueZ development headers are not found during build time or Qt was built without +Qt D-Bus support. + +The usage of the dummy backend is highlighted via an appropriate warning while building and running. + \section2 Guides \list \li \l {Qt Bluetooth Overview}{Classic Bluetooth Overview} diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp index b1cbdae5..5f163dfd 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp @@ -264,6 +264,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLowEnergyScan() env->ExceptionClear(); m_active = NoScanActive; emit q->finished(); + return; } leScanner.setField<jlong>("qtObject", reinterpret_cast<long>(receiver)); @@ -274,6 +275,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::startLowEnergyScan() qCWarning(QT_BT_ANDROID) << "Cannot start BTLE device scanner"; m_active = NoScanActive; emit q->finished(); + return; } if (!leScanTimeout) { diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp index 165e7603..0243d31f 100644 --- a/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp +++ b/src/bluetooth/qbluetoothdevicediscoveryagent_bluez.cpp @@ -98,12 +98,19 @@ QBluetoothDeviceDiscoveryAgentPrivate::~QBluetoothDeviceDiscoveryAgentPrivate() delete adapterBluez5; } +//TODO: Qt6 remove the pendingCancel/pendingStart logic as it is cumbersome. +// It is a behavior change across all platforms and was initially done +// for Bluez. The behavior should be similar to QBluetoothServiceDiscoveryAgent +// PendingCancel creates issues whereby the agent is still shutting down +// but isActive() below already returns false. This means the isActive() is +// out of sync with the finished() and cancel() signal. + bool QBluetoothDeviceDiscoveryAgentPrivate::isActive() const { if (pendingStart) return true; if (pendingCancel) - return false; + return false; //TODO Qt6: remove pending[Cancel|Start] logic (see comment above) return (adapter || adapterBluez5); } @@ -201,8 +208,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start() errorString = discoveryReply.error().message(); lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError; Q_Q(QBluetoothDeviceDiscoveryAgent); - emit q->error(lastError); qCDebug(QT_BT_BLUEZ) << Q_FUNC_INFO << "ERROR: " << errorString; + emit q->error(lastError); return; } } @@ -421,8 +428,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_propertyChanged(const QString &na adapter->deleteLater(); adapter = 0; - emit q->canceled(); pendingCancel = false; + emit q->canceled(); } else if (pendingStart) { adapter->deleteLater(); adapter = 0; @@ -431,6 +438,10 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_propertyChanged(const QString &na pendingCancel = false; start(); } else { + // happens when agent is created while other agent called StopDiscovery() + if (!adapter) + return; + if (useExtendedDiscovery) { useExtendedDiscovery = false; /* We don't use the Start/StopDiscovery combo here @@ -498,8 +509,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_discoveryFinished() adapterBluez5 = 0; if (pendingCancel && !pendingStart) { - emit q->canceled(); pendingCancel = false; + emit q->canceled(); } else if (pendingStart) { pendingStart = false; pendingCancel = false; @@ -517,7 +528,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::_q_discoveryInterrupted(const QStrin return; if (path == adapterBluez5->path()) { - qCWarning(QT_BT_BLUEZ) << "Device discovery aborted due to unexpected adapter changes"; + qCWarning(QT_BT_BLUEZ) << "Device discovery aborted due to unexpected adapter changes from another process."; if (discoveryTimer) discoveryTimer->stop(); diff --git a/src/bluetooth/qbluetoothservicediscoveryagent.cpp b/src/bluetooth/qbluetoothservicediscoveryagent.cpp index fa1aba9d..cd28cc25 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent.cpp @@ -149,9 +149,9 @@ QT_BEGIN_NAMESPACE local default Bluetooth adapter. */ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent) -: QObject(parent), d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(QBluetoothAddress())) + : QObject(parent), + d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, QBluetoothAddress())) { - d_ptr->q_ptr = this; } /*! @@ -167,9 +167,9 @@ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent \sa error() */ QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent) -: QObject(parent), d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(deviceAdapter)) + : QObject(parent), + d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, deviceAdapter)) { - d_ptr->q_ptr = this; if (!deviceAdapter.isNull()) { const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices(); foreach (const QBluetoothHostInfo &hostInfo, localDevices) { diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp index 478efd93..ba5bcb0a 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_android.cpp @@ -54,11 +54,13 @@ QT_BEGIN_NAMESPACE Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID) QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( - const QBluetoothAddress &/*deviceAdapter*/) + QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &/*deviceAdapter*/) : error(QBluetoothServiceDiscoveryAgent::NoError), state(Inactive), deviceDiscoveryAgent(0), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), - singleDevice(false), receiver(0), localDeviceReceiver(0) + singleDevice(false), receiver(0), localDeviceReceiver(0), + q_ptr(qp) + { QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices(); Q_ASSERT(devices.count() <= 1); //Android only supports one device at the moment diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp index e9c3d1b1..672dcf0d 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_bluez.cpp @@ -68,10 +68,12 @@ static inline void convertAddress(quint64 from, quint8 (&to)[6]) to[5] = (from >> 40) & 0xff; } -QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &deviceAdapter) +QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( + QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter) : error(QBluetoothServiceDiscoveryAgent::NoError), m_deviceAdapterAddress(deviceAdapter), state(Inactive), deviceDiscoveryAgent(0), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), singleDevice(false), - manager(0), managerBluez5(0), adapter(0), device(0), sdpScannerProcess(0) + manager(0), managerBluez5(0), adapter(0), device(0), sdpScannerProcess(0), + q_ptr(qp) { if (isBluez5()) { managerBluez5 = new OrgFreedesktopDBusObjectManagerInterface( @@ -218,15 +220,23 @@ void QBluetoothServiceDiscoveryAgentPrivate::runExternalSdpScan( sdpScannerProcess = new QProcess(q); sdpScannerProcess->setReadChannel(QProcess::StandardOutput); + if (QT_BT_BLUEZ().isDebugEnabled()) + sdpScannerProcess->setProcessChannelMode(QProcess::ForwardedErrorChannel); sdpScannerProcess->setProgram(fileInfo.canonicalFilePath()); q->connect(sdpScannerProcess, SIGNAL(finished(int,QProcess::ExitStatus)), q, SLOT(_q_sdpScannerDone(int,QProcess::ExitStatus))); - } QStringList arguments; arguments << remoteAddress.toString() << localAddress.toString(); + // No filter implies PUBLIC_BROWSE_GROUP based SDP scan + if (!uuidFilter.isEmpty()) { + arguments << QLatin1String("-u"); // cmd line option for list of uuids + foreach (const QBluetoothUuid& uuid, uuidFilter) + arguments << uuid.toString(); + } + sdpScannerProcess->setArguments(arguments); sdpScannerProcess->start(); } diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm index be579114..71877fc5 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm +++ b/src/bluetooth/qbluetoothservicediscoveryagent_osx.mm @@ -68,7 +68,8 @@ public: ServiceDiscovery, }; - QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &localAddress); + QBluetoothServiceDiscoveryAgentPrivate(QBluetoothServiceDiscoveryAgent *qp, + const QBluetoothAddress &localAddress); void startDeviceDiscovery(); void stopDeviceDiscovery(); @@ -116,8 +117,9 @@ private: OSXBluetooth::ObjCScopedPointer<ObjCServiceInquiry> serviceInquiry; }; -QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &localAddress) : - q_ptr(0), +QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( + QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &localAddress) : + q_ptr(qp), error(QBluetoothServiceDiscoveryAgent::NoError), singleDevice(false), localAdapterAddress(localAddress), @@ -429,15 +431,15 @@ void QBluetoothServiceDiscoveryAgentPrivate::serviceDiscoveryFinished() } QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(QObject *parent) -: QObject(parent), d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(QBluetoothAddress())) +: QObject(parent), + d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, QBluetoothAddress())) { - d_ptr->q_ptr = this; } QBluetoothServiceDiscoveryAgent::QBluetoothServiceDiscoveryAgent(const QBluetoothAddress &deviceAdapter, QObject *parent) -: QObject(parent), d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(deviceAdapter)) +: QObject(parent), + d_ptr(new QBluetoothServiceDiscoveryAgentPrivate(this, deviceAdapter)) { - d_ptr->q_ptr = this; if (!deviceAdapter.isNull()) { const QList<QBluetoothHostInfo> localDevices = QBluetoothLocalDevice::allDevices(); foreach (const QBluetoothHostInfo &hostInfo, localDevices) { diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.cpp b/src/bluetooth/qbluetoothservicediscoveryagent_p.cpp index c7780e7f..948fdf3e 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.cpp +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.cpp @@ -45,10 +45,14 @@ QT_BEGIN_NAMESPACE -QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &deviceAdapter) - : error(QBluetoothServiceDiscoveryAgent::NoError), state(Inactive), - deviceDiscoveryAgent(0), mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), - singleDevice(false) +QBluetoothServiceDiscoveryAgentPrivate::QBluetoothServiceDiscoveryAgentPrivate( + QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter) + : error(QBluetoothServiceDiscoveryAgent::NoError), + state(Inactive), + deviceDiscoveryAgent(0), + mode(QBluetoothServiceDiscoveryAgent::MinimalDiscovery), + singleDevice(false), + q_ptr(qp) { #ifndef QT_IOS_BLUETOOTH printDummyWarning(); diff --git a/src/bluetooth/qbluetoothservicediscoveryagent_p.h b/src/bluetooth/qbluetoothservicediscoveryagent_p.h index 42bb5598..47231d8f 100644 --- a/src/bluetooth/qbluetoothservicediscoveryagent_p.h +++ b/src/bluetooth/qbluetoothservicediscoveryagent_p.h @@ -93,7 +93,8 @@ public: ServiceDiscovery, }; - QBluetoothServiceDiscoveryAgentPrivate(const QBluetoothAddress &deviceAdapter); + QBluetoothServiceDiscoveryAgentPrivate(QBluetoothServiceDiscoveryAgent *qp, + const QBluetoothAddress &deviceAdapter); ~QBluetoothServiceDiscoveryAgentPrivate(); void startDeviceDiscovery(); diff --git a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp index 9521e3b8..f2486e81 100644 --- a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp +++ b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel.cpp @@ -102,7 +102,10 @@ public: m_discoveryMode(QDeclarativeBluetoothDiscoveryModel::MinimalServiceDiscovery), m_running(false), m_runningRequested(true), - m_componentCompleted(false) + m_componentCompleted(false), + m_currentState(QDeclarativeBluetoothDiscoveryModel::IdleAction), + m_nextState(QDeclarativeBluetoothDiscoveryModel::IdleAction), + m_wasDirectDeviceAgentCancel(false) { } ~QDeclarativeBluetoothDiscoveryModelPrivate() @@ -128,12 +131,33 @@ public: bool m_runningRequested; bool m_componentCompleted; QString m_remoteAddress; + + QDeclarativeBluetoothDiscoveryModel::Action m_currentState; + QDeclarativeBluetoothDiscoveryModel::Action m_nextState; + bool m_wasDirectDeviceAgentCancel; }; QDeclarativeBluetoothDiscoveryModel::QDeclarativeBluetoothDiscoveryModel(QObject *parent) : QAbstractListModel(parent), d(new QDeclarativeBluetoothDiscoveryModelPrivate) { + d->m_deviceAgent = new QBluetoothDeviceDiscoveryAgent(this); + connect(d->m_deviceAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), + this, SLOT(deviceDiscovered(QBluetoothDeviceInfo))); + connect(d->m_deviceAgent, SIGNAL(finished()), this, SLOT(finishedDiscovery())); + connect(d->m_deviceAgent, SIGNAL(canceled()), this, SLOT(finishedDiscovery())); + connect(d->m_deviceAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), + this, SLOT(errorDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::Error))); + d->m_deviceAgent->setObjectName("DeviceDiscoveryAgent"); + + d->m_serviceAgent = new QBluetoothServiceDiscoveryAgent(this); + connect(d->m_serviceAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), + this, SLOT(serviceDiscovered(QBluetoothServiceInfo))); + connect(d->m_serviceAgent, SIGNAL(finished()), this, SLOT(finishedDiscovery())); + connect(d->m_serviceAgent, SIGNAL(canceled()), this, SLOT(finishedDiscovery())); + connect(d->m_serviceAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)), + this, SLOT(errorDiscovery(QBluetoothServiceDiscoveryAgent::Error))); + d->m_serviceAgent->setObjectName("ServiceDiscoveryAgent"); QHash<int, QByteArray> roleNames; roleNames = QAbstractItemModel::roleNames(); @@ -180,8 +204,8 @@ void QDeclarativeBluetoothDiscoveryModel::errorDeviceDiscovery(QBluetoothDeviceD //QBluetoothDeviceDiscoveryAgent::finished() signal is not emitted in case of an error //Note that this behavior is different from QBluetoothServiceDiscoveryAgent. - //This reset the models running flag. - setRunning(false); + //This resets the models running flag. + finishedDiscovery(); } void QDeclarativeBluetoothDiscoveryModel::clearModel() @@ -337,7 +361,35 @@ void QDeclarativeBluetoothDiscoveryModel::deviceDiscovered(const QBluetoothDevic void QDeclarativeBluetoothDiscoveryModel::finishedDiscovery() { - setRunning(false); + QDeclarativeBluetoothDiscoveryModel::Action previous = d->m_currentState; + d->m_currentState = IdleAction; + + switch (previous) { + case IdleAction: + // last transition didn't even start + // can happen when start() or stop() immediately returned + // usually this happens within a current transitionToNextAction call + break; + case StopAction: + qCDebug(QT_BT_QML) << "Agent cancel detected"; + transitionToNextAction(); + break; + default: // all other + qCDebug(QT_BT_QML) << "Discovery finished" << sender()->objectName(); + + //TODO Qt6 This hack below is once again due to the pendingCancel logic + // because QBluetoothDeviceDiscoveryAgent::isActive() is not reliable. + // In toggleStartStop() we need to know whether the stop() is delayed or immediate. + // isActive() cannot be used. Hence we have to wait for the canceled() signal. + // Android, WinRT and Bluez5 are immediate, Bluez4 is always delayed. + // The immediate case is what we catch here. + if (sender() == d->m_deviceAgent && d->m_nextState == StopAction) { + d->m_wasDirectDeviceAgentCancel = true; + return; + } + setRunning(false); + break; + } } /*! @@ -367,6 +419,131 @@ void QDeclarativeBluetoothDiscoveryModel::setDiscoveryMode(DiscoveryMode discove emit discoveryModeChanged(); } + +void QDeclarativeBluetoothDiscoveryModel::updateNextAction(Action action) +{ + qCDebug(QT_BT_QML) << "New action queue:" + << d->m_currentState << d->m_nextState << action; + + if (action == IdleAction) + return; + + switch (d->m_nextState) { + case IdleAction: + d->m_nextState = action; + return; + case StopAction: + qWarning() << "Invalid Stop state when processing new action" << action; + return; + case DeviceDiscoveryAction: + case MinimalServiceDiscoveryAction: + case FullServiceDiscoveryAction: + if (action == StopAction) // cancel out previous start call + d->m_nextState = IdleAction; + else + qWarning() << "Ignoring new DMF state while another DMF state is scheduled."; + return; + } +} + +void QDeclarativeBluetoothDiscoveryModel::transitionToNextAction() +{ + qCDebug(QT_BT_QML) << "Before transition change:" << d->m_currentState << d->m_nextState; + bool isRunning; + switch (d->m_currentState) { + case IdleAction: + switch (d->m_nextState) { + case IdleAction: break; // nothing to do + case StopAction: d->m_nextState = IdleAction; break; // clear, nothing to do + case DeviceDiscoveryAction: + case MinimalServiceDiscoveryAction: + case FullServiceDiscoveryAction: + Action temp = d->m_nextState; + clearModel(); + isRunning = toggleStartStop(d->m_nextState); + d->m_nextState = IdleAction; + if (isRunning) { + d->m_currentState = temp; + } else { + if (temp != DeviceDiscoveryAction ) + errorDiscovery(d->m_serviceAgent->error()); + d->m_running = false; + } + } + break; + case StopAction: + break; // do nothing, StopAction cleared by finished()/cancelled()/error() handlers + case DeviceDiscoveryAction: + case MinimalServiceDiscoveryAction: + case FullServiceDiscoveryAction: + switch (d->m_nextState) { + case IdleAction: break; + case StopAction: + isRunning = toggleStartStop(StopAction); + (isRunning) ? d->m_currentState = StopAction : d->m_currentState = IdleAction; + d->m_nextState = IdleAction; + break; + default: + Q_ASSERT(false); // should never happen + break; + } + + break; + } + + qCDebug(QT_BT_QML) << "After transition change:" << d->m_currentState << d->m_nextState; +} + +// Returns true if the agent is active +// this can be used to detect whether the agent still needs time to +// perform the requested action. +bool QDeclarativeBluetoothDiscoveryModel::toggleStartStop(Action action) +{ + Q_ASSERT(action != IdleAction); + switch (action) { + case DeviceDiscoveryAction: + Q_ASSERT(!d->m_deviceAgent->isActive() && !d->m_serviceAgent->isActive()); + d->m_deviceAgent->start(); + return d->m_deviceAgent->isActive(); + case MinimalServiceDiscoveryAction: + case FullServiceDiscoveryAction: + Q_ASSERT(!d->m_deviceAgent->isActive() && !d->m_serviceAgent->isActive()); + d->m_serviceAgent->setRemoteAddress(QBluetoothAddress(d->m_remoteAddress)); + d->m_serviceAgent->clear(); + + if (!d->m_uuid.isEmpty()) + d->m_serviceAgent->setUuidFilter(QBluetoothUuid(d->m_uuid)); + + if (action == FullServiceDiscoveryAction) { + qCDebug(QT_BT_QML) << "Full Discovery"; + d->m_serviceAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery); + } else { + qCDebug(QT_BT_QML) << "Minimal Discovery"; + d->m_serviceAgent->start(QBluetoothServiceDiscoveryAgent::MinimalDiscovery); + } + return d->m_serviceAgent->isActive(); + case StopAction: + Q_ASSERT(d->m_currentState != StopAction && d->m_currentState != IdleAction); + if (d->m_currentState == DeviceDiscoveryAction) { + d->m_deviceAgent->stop(); + + // TODO Qt6 Crude hack below + // cannot use isActive() below due to pendingCancel logic + // we always wait for canceled() signal coming through or check + // for directly invoked cancel() response caused by stop() above + bool stillActive = !d->m_wasDirectDeviceAgentCancel; + d->m_wasDirectDeviceAgentCancel = false; + return stillActive; + } else { + d->m_serviceAgent->stop(); + return d->m_serviceAgent->isActive(); + } + default: + return true; + } +} + + /*! \qmlproperty bool BluetoothDiscoveryModel::running @@ -392,55 +569,23 @@ void QDeclarativeBluetoothDiscoveryModel::setRunning(bool running) d->m_running = running; - if (!running) { - if (d->m_deviceAgent) - d->m_deviceAgent->stop(); - if (d->m_serviceAgent) - d->m_serviceAgent->stop(); + Action nextAction = IdleAction; + if (running) { + if (discoveryMode() == MinimalServiceDiscovery) + nextAction = MinimalServiceDiscoveryAction; + else if (discoveryMode() == FullServiceDiscovery) + nextAction = FullServiceDiscoveryAction; + else + nextAction = DeviceDiscoveryAction; } else { - clearModel(); - d->m_error = NoError; - if (d->m_discoveryMode == DeviceDiscovery) { - if (!d->m_deviceAgent) { - d->m_deviceAgent = new QBluetoothDeviceDiscoveryAgent(this); - connect(d->m_deviceAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)), this, SLOT(deviceDiscovered(QBluetoothDeviceInfo))); - connect(d->m_deviceAgent, SIGNAL(finished()), this, SLOT(finishedDiscovery())); - connect(d->m_deviceAgent, SIGNAL(canceled()), this, SLOT(finishedDiscovery())); - connect(d->m_deviceAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(errorDeviceDiscovery(QBluetoothDeviceDiscoveryAgent::Error))); - } - d->m_deviceAgent->start(); - } else { - if (!d->m_serviceAgent) { - d->m_serviceAgent = new QBluetoothServiceDiscoveryAgent(this); - connect(d->m_serviceAgent, SIGNAL(serviceDiscovered(QBluetoothServiceInfo)), this, SLOT(serviceDiscovered(QBluetoothServiceInfo))); - connect(d->m_serviceAgent, SIGNAL(finished()), this, SLOT(finishedDiscovery())); - connect(d->m_serviceAgent, SIGNAL(canceled()), this, SLOT(finishedDiscovery())); - connect(d->m_serviceAgent, SIGNAL(error(QBluetoothServiceDiscoveryAgent::Error)), this, SLOT(errorDiscovery(QBluetoothServiceDiscoveryAgent::Error))); - } - - d->m_serviceAgent->setRemoteAddress(QBluetoothAddress(d->m_remoteAddress)); - d->m_serviceAgent->clear(); - - if (!d->m_uuid.isEmpty()) - d->m_serviceAgent->setUuidFilter(QBluetoothUuid(d->m_uuid)); - - if (discoveryMode() == FullServiceDiscovery) { - //qDebug() << "Full Discovery"; - d->m_serviceAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery); - } else { - //qDebug() << "Minimal Discovery"; - d->m_serviceAgent->start(QBluetoothServiceDiscoveryAgent::MinimalDiscovery); - } - - // we could not start service discovery - if (!d->m_serviceAgent->isActive()) { - d->m_running = false; - errorDiscovery(d->m_serviceAgent->error()); - return; - } - } + nextAction = StopAction; } + Q_ASSERT(nextAction != IdleAction); + updateNextAction(nextAction); + transitionToNextAction(); + + qCDebug(QT_BT_QML) << "Running state:" << d->m_running; emit runningChanged(); } diff --git a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h index ff6725d3..a3cfdcf2 100644 --- a/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h +++ b/src/imports/bluetooth/qdeclarativebluetoothdiscoverymodel_p.h @@ -117,6 +117,8 @@ public: DiscoveryMode discoveryMode() const; void setDiscoveryMode(DiscoveryMode discovery); + // TODO Qt 6 This property behaves synchronously but should really be + // asynchronous. The agents start/stop/restart is not immediate. bool running() const; void setRunning(bool running); @@ -145,8 +147,22 @@ private slots: private: void clearModel(); + enum Action { + IdleAction = 0, + StopAction, + DeviceDiscoveryAction, + MinimalServiceDiscoveryAction, + FullServiceDiscoveryAction + }; + + bool toggleStartStop(Action action); + void updateNextAction(Action action); + void transitionToNextAction(); + private: QDeclarativeBluetoothDiscoveryModelPrivate* d; + friend class QDeclarativeBluetoothDiscoveryModelPrivate; + }; #endif // QDECLARATIVECONTACTMODEL_P_H diff --git a/src/nfc/qnearfieldtagtype1.cpp b/src/nfc/qnearfieldtagtype1.cpp index f365b1a3..5d46b8c5 100644 --- a/src/nfc/qnearfieldtagtype1.cpp +++ b/src/nfc/qnearfieldtagtype1.cpp @@ -82,7 +82,8 @@ public: QNearFieldTagType1Private(QNearFieldTagType1 *q) : q_ptr(q), m_readNdefMessageState(NotReadingNdefMessage), m_tlvReader(0), - m_writeNdefMessageState(NotWritingNdefMessage) + m_writeNdefMessageState(NotWritingNdefMessage), + m_tlvWriter(0) { } QNearFieldTagType1 *q_ptr; @@ -170,11 +171,13 @@ void QNearFieldTagType1Private::progressToNextNdefReadMessageState() } m_readNdefMessageState = NdefReadReadingTlv; + delete m_tlvReader; m_tlvReader = new QTlvReader(q); // fall through } case NdefReadReadingTlv: + Q_ASSERT(m_tlvReader); while (!m_tlvReader->atEnd()) { if (!m_tlvReader->readNext()) break; @@ -246,11 +249,13 @@ void QNearFieldTagType1Private::progressToNextNdefWriteMessageState() } m_writeNdefMessageState = NdefWriteReadingTlv; + delete m_tlvReader; m_tlvReader = new QTlvReader(q); // fall through } case NdefWriteReadingTlv: + Q_ASSERT(m_tlvReader); while (!m_tlvReader->atEnd()) { if (!m_tlvReader->readNext()) break; @@ -270,6 +275,7 @@ void QNearFieldTagType1Private::progressToNextNdefWriteMessageState() // fall through case NdefWriteWritingTlv: + delete m_tlvWriter; m_tlvWriter = new QTlvWriter(q); // write old TLVs @@ -288,6 +294,7 @@ void QNearFieldTagType1Private::progressToNextNdefWriteMessageState() // fall through case NdefWriteWritingTlvFlush: // flush the writer + Q_ASSERT(m_tlvWriter); if (m_tlvWriter->process(true)) { m_nextExpectedRequestId = QNearFieldTarget::RequestId(); m_writeNdefMessageState = NotWritingNdefMessage; diff --git a/src/tools/sdpscanner/main.cpp b/src/tools/sdpscanner/main.cpp index bf978c0d..edca8fa3 100644 --- a/src/tools/sdpscanner/main.cpp +++ b/src/tools/sdpscanner/main.cpp @@ -40,6 +40,7 @@ #include <QtCore/QByteArray> #include <QtCore/QDebug> #include <stdio.h> +#include <string> #include <bluetooth/bluetooth.h> #include <bluetooth/sdp.h> #include <bluetooth/sdp_lib.h> @@ -52,11 +53,14 @@ void usage() { fprintf(stderr, "Usage:\n"); - fprintf(stderr, "\tsdpscanner <remote bdaddr> <local bdaddr> [Options]\n\n"); + fprintf(stderr, "\tsdpscanner <remote bdaddr> <local bdaddr> [Options] ({uuids})\n\n"); fprintf(stderr, "Performs an SDP scan on remote device, using the SDP server\n" "represented by the local Bluetooth device.\n\n" "Options:\n" - " -p Show scan results in human-readable form\n"); + " -p Show scan results in human-readable form\n" + " -u [list of uuids] List of uuids which should be scanned for.\n" + " Each uuid must be enclosed in {}.\n" + " If the list is empty PUBLIC_BROWSE_GROUP scan is used.\n"); } #define BUFFER_SIZE 1024 @@ -275,6 +279,7 @@ int main(int argc, char **argv) } bool showHumanReadable = false; + std::vector<std::string> targetServices; for (int i = 3; i < argc; i++) { if (argv[i][0] != '-') { @@ -287,12 +292,56 @@ int main(int argc, char **argv) case 'p': showHumanReadable = true; break; + case 'u': + i++; + + for ( ; i < argc && argv[i][0] == '{'; i++) + targetServices.push_back(argv[i]); + + i--; // outer loop increments again + break; default: fprintf(stderr, "Wrong argument: %s\n", argv[i]); usage(); return RETURN_USAGE; + } + } + std::vector<uuid_t> uuids; + for (std::vector<std::string>::const_iterator iter = targetServices.cbegin(); + iter != targetServices.cend(); ++iter) { + + uint128_t temp128; + uint16_t field1, field2, field3, field5; + uint32_t field0, field4; + + fprintf(stderr, "Target scan for %s\n", (*iter).c_str()); + if (sscanf((*iter).c_str(), "{%08x-%04hx-%04hx-%04hx-%08x%04hx}", &field0, + &field1, &field2, &field3, &field4, &field5) != 6) { + fprintf(stderr, "Skipping invalid uuid: %s\n", ((*iter).c_str())); + continue; } + + // we need uuid_t conversion based on + // http://www.spinics.net/lists/linux-bluetooth/msg20356.html + field0 = htonl(field0); + field4 = htonl(field4); + field1 = htons(field1); + field2 = htons(field2); + field3 = htons(field3); + field5 = htons(field5); + + uint8_t* temp = (uint8_t*) &temp128; + memcpy(&temp[0], &field0, 4); + memcpy(&temp[4], &field1, 2); + memcpy(&temp[6], &field2, 2); + memcpy(&temp[8], &field3, 2); + memcpy(&temp[10], &field4, 4); + memcpy(&temp[14], &field5, 2); + + uuid_t sdpUuid; + sdp_uuid128_create(&sdpUuid, &temp128); + uuids.push_back(sdpUuid); } sdp_session_t *session = sdp_connect( &local, &remote, SDP_RETRY_IF_BUSY); @@ -307,27 +356,52 @@ int main(int argc, char **argv) } // set the filter for service matches - uuid_t publicBrowseGroupUuid; - sdp_uuid16_create(&publicBrowseGroupUuid, PUBLIC_BROWSE_GROUP); - sdp_list_t *serviceFilter; - serviceFilter = sdp_list_append(0, &publicBrowseGroupUuid); + if (uuids.empty()) { + fprintf(stderr, "Using PUBLIC_BROWSE_GROUP for SDP search\n"); + uuid_t publicBrowseGroupUuid; + sdp_uuid16_create(&publicBrowseGroupUuid, PUBLIC_BROWSE_GROUP); + uuids.push_back(publicBrowseGroupUuid); + } uint32_t attributeRange = 0x0000ffff; //all attributes sdp_list_t *attributes; attributes = sdp_list_append(0, &attributeRange); - sdp_list_t *sdpResults, *previous; - result = sdp_service_search_attr_req(session, serviceFilter, + sdp_list_t *sdpResults, *sdpIter; + sdp_list_t *totalResults = NULL; + sdp_list_t* serviceFilter; + + for (uint i = 0; i < uuids.size(); ++i) { + serviceFilter = sdp_list_append(0, &uuids[i]); + result = sdp_service_search_attr_req(session, serviceFilter, SDP_ATTR_REQ_RANGE, attributes, &sdpResults); - sdp_list_free(attributes, 0); - sdp_list_free(serviceFilter, 0); + sdp_list_free(serviceFilter, 0); + if (result != 0) { + fprintf(stderr, "sdp_service_search_attr_req failed\n"); + sdp_list_free(attributes, 0); + sdp_close(session); + return RETURN_SDP_ERROR; + } - if (result != 0) { - fprintf(stderr, "sdp_service_search_attr_req failed\n"); - sdp_close(session); - return RETURN_SDP_ERROR; + if (!sdpResults) + continue; + + if (!totalResults) { + totalResults = sdpResults; + sdpIter = totalResults; + } else { + // attach each new result list to the end of totalResults + sdpIter->next = sdpResults; + } + + while (sdpIter->next) // skip to end of list + sdpIter = sdpIter->next; } + sdp_list_free(attributes, 0); + + // start XML generation from the front + sdpResults = totalResults; QByteArray total; while (sdpResults) { @@ -336,9 +410,9 @@ int main(int argc, char **argv) const QByteArray xml = parseSdpRecord(record); total += xml; - previous = sdpResults; + sdpIter = sdpResults; sdpResults = sdpResults->next; - free(previous); + free(sdpIter); sdp_record_free(record); } |