6#include "android/servicediscoverybroadcastreceiver_p.h"
7#include "android/localdevicebroadcastreceiver_p.h"
8#include "android/androidutils_p.h"
9#include "android/jni_android_p.h"
11#include <QCoreApplication>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/QLoggingCategory>
14#include <QtCore/QTimer>
15#include <QtCore/QJniEnvironment>
16#include <QtBluetooth/QBluetoothHostInfo>
17#include <QtBluetooth/QBluetoothLocalDevice>
18#include <QtBluetooth/QBluetoothServiceDiscoveryAgent>
22Q_DECLARE_LOGGING_CATEGORY(QT_BT_ANDROID)
24static constexpr auto uuidFetchTimeLimit = std::chrono::seconds{4};
27 QBluetoothServiceDiscoveryAgent *qp,
const QBluetoothAddress &deviceAdapter)
40 bool createAdapter =
true;
41 if (!deviceAdapter.isNull()) {
42 const QList<QBluetoothHostInfo> devices = QBluetoothLocalDevice::allDevices();
43 if (devices.isEmpty()) {
44 createAdapter =
false;
46 auto match = [deviceAdapter](
const QBluetoothHostInfo& info) {
47 return info.address() == deviceAdapter;
50 auto result = std::find_if(devices.begin(), devices.end(), match);
51 if (result == devices.end())
52 createAdapter =
false;
57
58
59
62 btAdapter = getDefaultBluetoothAdapter();
64 if (!btAdapter.isValid())
65 qCWarning(QT_BT_ANDROID) <<
"Platform does not support Bluetooth";
67 qRegisterMetaType<QList<QBluetoothUuid> >();
73 receiver->unregisterReceiver();
76 if (localDeviceReceiver) {
77 localDeviceReceiver->unregisterReceiver();
78 delete localDeviceReceiver;
84 Q_Q(QBluetoothServiceDiscoveryAgent);
86 if (!ensureAndroidPermission(QBluetoothPermission::Access)) {
87 qCWarning(QT_BT_ANDROID) <<
"Service discovery start() failed due to missing permissions";
88 error = QBluetoothServiceDiscoveryAgent::MissingPermissionsError;
89 errorString = QBluetoothServiceDiscoveryAgent::tr(
90 "Failed to start service discovery due to missing permissions.");
91 emit q->errorOccurred(error);
96 if (!btAdapter.isValid()) {
97 if (m_deviceAdapterAddress.isNull()) {
98 error = QBluetoothServiceDiscoveryAgent::UnknownError;
99 errorString = QBluetoothServiceDiscoveryAgent::tr(
"Platform does not support Bluetooth");
103 error = QBluetoothServiceDiscoveryAgent::InvalidBluetoothAdapterError;
104 errorString = QBluetoothServiceDiscoveryAgent::tr(
"Invalid Bluetooth adapter address");
108 discoveredDevices.clear();
109 emit q->errorOccurred(error);
115 QJniObject inputString = QJniObject::fromString(address.toString());
116 QJniObject remoteDevice =
117 btAdapter.callMethod<QtJniTypes::BluetoothDevice>(
"getRemoteDevice",
118 inputString.object<jstring>());
119 if (!remoteDevice.isValid()) {
123 error = QBluetoothServiceDiscoveryAgent::InputOutputError;
124 errorString = QBluetoothServiceDiscoveryAgent::tr(
"Cannot create Android BluetoothDevice");
126 qCWarning(QT_BT_ANDROID) <<
"Cannot start SDP for" << discoveredDevices.at(0).name()
127 <<
"(" << address.toString() <<
")";
128 emit q->errorOccurred(error);
135 if (mode == QBluetoothServiceDiscoveryAgent::MinimalDiscovery) {
136 qCDebug(QT_BT_ANDROID) <<
"Minimal discovery on (" << discoveredDevices.at(0).name()
137 <<
")" << address.toString() ;
140 const QJniArray parcelUuidArray =
141 remoteDevice.callMethod<QtJniTypes::ParcelUuid[]>(
"getUuids");
143 if (!parcelUuidArray.isValid()) {
145 error = QBluetoothServiceDiscoveryAgent::InputOutputError;
146 errorString = QBluetoothServiceDiscoveryAgent::tr(
"Cannot obtain service uuids");
147 emit q->errorOccurred(error);
149 qCWarning(QT_BT_ANDROID) <<
"Cannot retrieve SDP UUIDs for" << discoveredDevices.at(0).name()
150 <<
"(" << address.toString() <<
")";
155 const QList<QBluetoothUuid> results = ServiceDiscoveryBroadcastReceiver::convertParcelableArray(parcelUuidArray);
156 populateDiscoveredServices(discoveredDevices.at(0), results);
160 qCDebug(QT_BT_ANDROID) <<
"Full discovery on (" << discoveredDevices.at(0).name()
161 <<
")" << address.toString();
165 receiver =
new ServiceDiscoveryBroadcastReceiver();
166 QObject::connect(receiver, &ServiceDiscoveryBroadcastReceiver::uuidFetchFinished,
167 q, [
this](
const QBluetoothAddress &address,
const QList<QBluetoothUuid>& uuids) {
168 this->_q_processFetchedUuids(address, uuids);
172 if (!localDeviceReceiver) {
173 localDeviceReceiver =
new LocalDeviceBroadcastReceiver();
174 QObject::connect(localDeviceReceiver, &LocalDeviceBroadcastReceiver::hostModeStateChanged,
175 q, [
this](QBluetoothLocalDevice::HostMode state){
176 this->_q_hostModeStateChanged(state);
180 jboolean result = remoteDevice.callMethod<jboolean>(
"fetchUuidsWithSdp");
183 receiver->unregisterReceiver();
184 receiver->deleteLater();
186 qCWarning(QT_BT_ANDROID) <<
"Cannot start dynamic fetch.";
195 discoveredDevices.clear();
199 receiver->unregisterReceiver();
200 receiver->deleteLater();
204 Q_Q(QBluetoothServiceDiscoveryAgent);
210 const QBluetoothAddress &address,
const QList<QBluetoothUuid> &uuids)
213 if (discoveredDevices.isEmpty())
217 if (address.isNull() || uuids.isEmpty()) {
218 if (discoveredDevices.size() == 1) {
219 Q_Q(QBluetoothServiceDiscoveryAgent);
220 QTimer::singleShot(uuidFetchTimeLimit, q, [
this]() {
221 this->_q_fetchUuidsTimeout();
229 if (QT_BT_ANDROID().isDebugEnabled()) {
230 qCDebug(QT_BT_ANDROID) <<
"Found UUID for" << address.toString()
231 <<
"\ncount: " << uuids.size();
234 for (
const QBluetoothUuid &uuid : uuids)
235 result += uuid.toString() + QLatin1String(
"**");
236 qCDebug(QT_BT_ANDROID) << result;
240
241
242
243
244
245
247 if (sdpCache.contains(address)) {
249 QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > pair = sdpCache.take(address);
252 populateDiscoveredServices(pair.first, uuids);
254 if (discoveredDevices.size() == 1 && sdpCache.isEmpty()) {
260 QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > pair;
261 pair.first = discoveredDevices.at(0);
264 if (pair.first.address() != address)
267 sdpCache.insert(address, pair);
271 if (discoveredDevices.size() == 1) {
272 Q_Q(QBluetoothServiceDiscoveryAgent);
273 QTimer::singleShot(uuidFetchTimeLimit, q, [
this]() {
274 this->_q_fetchUuidsTimeout();
286
287
288
289
290
291
292
293
294
295
296
297
298
300 Q_Q(QBluetoothServiceDiscoveryAgent);
303 bool haveSppClass =
false;
304 QVarLengthArray<qsizetype> customUuids;
306 for (qsizetype i = 0; i < uuids.size(); ++i) {
307 const QBluetoothUuid uuid = uuids.at(i);
313 haveSppClass |= uuid == QBluetoothUuid::ServiceClassUuid::SerialPort;
316 if (uuid.minimumSize() == 16)
317 customUuids.append(i);
320 auto rfcommProtocolDescriptorList = []() -> QBluetoothServiceInfo::Sequence {
321 QBluetoothServiceInfo::Sequence protocol;
322 protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::Rfcomm))
323 << QVariant::fromValue(0);
327 auto sppProfileDescriptorList = []() -> QBluetoothServiceInfo::Sequence {
328 QBluetoothServiceInfo::Sequence profileSequence;
329 QBluetoothServiceInfo::Sequence classId;
330 classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort));
331 classId << QVariant::fromValue(quint16(0x100));
332 profileSequence.append(QVariant::fromValue(classId));
333 return profileSequence;
336 for (qsizetype i = 0; i < uuids.size(); ++i) {
337 const QBluetoothUuid &uuid = uuids.at(i);
342 serviceInfo.setDevice(remoteDevice);
344 QBluetoothServiceInfo::Sequence protocolDescriptorList;
346 QBluetoothServiceInfo::Sequence protocol;
347 protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::L2cap));
348 protocolDescriptorList.append(QVariant::fromValue(protocol));
351 if (customUuids.contains(i) && haveSppClass) {
355 protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList()));
358 serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,
359 sppProfileDescriptorList());
361 QBluetoothServiceInfo::Sequence classId;
363 classId << QVariant::fromValue(uuid);
364 classId << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::SerialPort));
365 serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
367 serviceInfo.setServiceName(QBluetoothServiceDiscoveryAgent::tr(
"Serial Port Profile"));
368 serviceInfo.setServiceUuid(uuid);
369 }
else if (uuid == QBluetoothUuid{QBluetoothUuid::ServiceClassUuid::SerialPort}) {
371 protocolDescriptorList.append(QVariant::fromValue(rfcommProtocolDescriptorList()));
374 serviceInfo.setAttribute(QBluetoothServiceInfo::BluetoothProfileDescriptorList,
375 sppProfileDescriptorList());
379 serviceInfo.setServiceUuid(uuid);
380 }
else if (customUuids.contains(i)) {
382 serviceInfo.setServiceUuid(uuid);
385 serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList, protocolDescriptorList);
386 QBluetoothServiceInfo::Sequence publicBrowse;
387 publicBrowse << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::PublicBrowseGroup));
388 serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, publicBrowse);
390 if (!customUuids.contains(i)) {
392 QBluetoothServiceInfo::Sequence classId;
393 classId << QVariant::fromValue(uuid);
394 serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceClassIds, classId);
395 auto clsId = QBluetoothUuid::ServiceClassUuid(uuid.toUInt16());
396 serviceInfo.setServiceName(QBluetoothUuid::serviceClassToString(clsId));
400 if (!uuidFilter.isEmpty()) {
401 bool match = uuidFilter.contains(serviceInfo.serviceUuid());
402 match |= uuidFilter.contains(QBluetoothSocketPrivateAndroid::reverseUuid(serviceInfo.serviceUuid()));
403 for (
const auto &uuid : std::as_const(uuidFilter)) {
404 match |= serviceInfo.serviceClassUuids().contains(uuid);
405 match |= serviceInfo.serviceClassUuids().contains(QBluetoothSocketPrivateAndroid::reverseUuid(uuid));
413 if (!isDuplicatedService(serviceInfo)) {
414 discoveredServices << serviceInfo;
418 QMetaObject::invokeMethod(q,
"serviceDiscovered", Qt::QueuedConnection,
419 Q_ARG(QBluetoothServiceInfo, serviceInfo));
427 if (discoveredDevices.isEmpty())
431 if (!sdpCache.isEmpty()) {
432 QPair<QBluetoothDeviceInfo,QList<QBluetoothUuid> > pair;
433 const QList<QBluetoothAddress> keys = sdpCache.keys();
434 for (
const QBluetoothAddress &key : keys) {
435 pair = sdpCache.take(key);
436 populateDiscoveredServices(pair.first, pair.second);
440 Q_ASSERT(sdpCache.isEmpty());
444 receiver->unregisterReceiver();
445 receiver->deleteLater();
453 if (discoveryState() == QBluetoothServiceDiscoveryAgentPrivate::ServiceDiscovery &&
454 state == QBluetoothLocalDevice::HostPoweredOff ) {
456 discoveredDevices.clear();
458 error = QBluetoothServiceDiscoveryAgent::PoweredOffError;
459 errorString = QBluetoothServiceDiscoveryAgent::tr(
"Device is powered off");
463 receiver->unregisterReceiver();
464 receiver->deleteLater();
468 Q_Q(QBluetoothServiceDiscoveryAgent);
469 emit q->errorOccurred(error);
470 _q_serviceDiscoveryFinished();
~QBluetoothServiceDiscoveryAgentPrivate()
void _q_serviceDiscoveryFinished()
QBluetoothServiceDiscoveryAgentPrivate(QBluetoothServiceDiscoveryAgent *qp, const QBluetoothAddress &deviceAdapter)