summaryrefslogtreecommitdiffstats
path: root/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp')
-rw-r--r--src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp124
1 files changed, 96 insertions, 28 deletions
diff --git a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
index 228c8bb3..53bd856b 100644
--- a/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
+++ b/src/bluetooth/qbluetoothdevicediscoveryagent_android.cpp
@@ -58,6 +58,9 @@ enum {
BtleScanActive = 2
};
+static constexpr auto deviceDiscoveryStartTimeLimit = std::chrono::seconds{5};
+static constexpr short deviceDiscoveryStartMaxAttempts = 6;
+
QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
const QBluetoothAddress &deviceAdapter, QBluetoothDeviceDiscoveryAgent *parent) :
inquiryType(QBluetoothDeviceDiscoveryAgent::GeneralUnlimitedInquiry),
@@ -66,6 +69,7 @@ QBluetoothDeviceDiscoveryAgentPrivate::QBluetoothDeviceDiscoveryAgentPrivate(
m_adapterAddress(deviceAdapter),
m_active(NoScanActive),
leScanTimeout(0),
+ deviceDiscoveryStartAttemptsLeft(deviceDiscoveryStartMaxAttempts),
pendingCancel(false),
pendingStart(false),
lowEnergySearchTimeout(25000),
@@ -112,9 +116,42 @@ QBluetoothDeviceDiscoveryAgent::DiscoveryMethods QBluetoothDeviceDiscoveryAgent:
return (LowEnergyMethod | ClassicMethod);
}
+void QBluetoothDeviceDiscoveryAgentPrivate::classicDiscoveryStartFail()
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+ lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Classic Discovery cannot be started");
+ emit q->error(lastError);
+}
+
+// Sets & emits an error and returns true if bluetooth is off
+bool QBluetoothDeviceDiscoveryAgentPrivate::setErrorIfPowerOff()
+{
+ Q_Q(QBluetoothDeviceDiscoveryAgent);
+
+ const int state = adapter.callMethod<jint>("getState");
+ if (state != 12) { // BluetoothAdapter.STATE_ON
+ lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError;
+ m_active = NoScanActive;
+ errorString = QBluetoothDeviceDiscoveryAgent::tr("Device is powered off");
+ emit q->error(lastError);
+ return true;
+ }
+ return false;
+}
+
+/*
+The Classic/LE discovery method precedence is handled as follows:
+
+If only classic method is set => only classic method is used
+If only LE method is set => only LE method is used
+If both classic and LE methods are set, start classic scan first
+ If classic scan fails to start, start LE scan immediately in the start function
+ Otherwise start LE scan when classic scan completes
+*/
+
void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent::DiscoveryMethods methods)
{
- //TODO Implement discovery method handling (see input parameter)
requestedMethods = methods;
if (pendingCancel) {
@@ -142,13 +179,8 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
return;
}
- const int state = adapter.callMethod<jint>("getState");
- if (state != 12) { // BluetoothAdapter.STATE_ON
- lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError;
- errorString = QBluetoothDeviceDiscoveryAgent::tr("Device is powered off");
- emit q->error(lastError);
+ if (setErrorIfPowerOff())
return;
- }
// check Android v23+ permissions
// -> any device search requires android.permission.ACCESS_COARSE_LOCATION or android.permission.ACCESS_FINE_LOCATION
@@ -247,16 +279,54 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
const bool success = adapter.callMethod<jboolean>("startDiscovery");
if (!success) {
qCDebug(QT_BT_ANDROID) << "Classic Discovery cannot be started";
+ // Check if only classic discovery requested -> error out and return.
+ // Otherwise since LE was also requested => don't return but allow the
+ // function to continue to LE scanning
if (requestedMethods == QBluetoothDeviceDiscoveryAgent::ClassicMethod) {
- //only classic discovery requested -> error out
- lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
- errorString = QBluetoothDeviceDiscoveryAgent::tr("Classic Discovery cannot be started");
-
- emit q->error(lastError);
+ classicDiscoveryStartFail();
return;
- } // else fall through to LE discovery
+ }
} else {
m_active = SDPScanActive;
+ if (!deviceDiscoveryStartTimeout) {
+ // In some bluetooth environments device discovery does not start properly
+ // if it is done shortly after (up to 20 seconds) a full service discovery.
+ // In that case we never get DISOVERY_STARTED action and device discovery never
+ // finishes. Here we use a small timeout to guard it; if we don't get the
+ // 'started' action in time, we restart the query. In the normal case the action
+ // is received in < 1 second. See QTBUG-101066
+ deviceDiscoveryStartTimeout = new QTimer(this);
+ deviceDiscoveryStartTimeout->setInterval(deviceDiscoveryStartTimeLimit);
+ deviceDiscoveryStartTimeout->setSingleShot(true);
+ QObject::connect(receiver, &DeviceDiscoveryBroadcastReceiver::discoveryStarted,
+ deviceDiscoveryStartTimeout, &QTimer::stop);
+ QObject::connect(deviceDiscoveryStartTimeout, &QTimer::timeout, [this]() {
+ deviceDiscoveryStartAttemptsLeft -= 1;
+ qCWarning(QT_BT_ANDROID) << "Discovery start not received, attempts left:"
+ << deviceDiscoveryStartAttemptsLeft;
+ // Check that bluetooth is not switched off
+ if (setErrorIfPowerOff())
+ return;
+ // If this was the last retry attempt, cancel the discovery just in case
+ // as a good cleanup practice
+ if (deviceDiscoveryStartAttemptsLeft <= 0) {
+ qCWarning(QT_BT_ANDROID) << "Classic device discovery failed to start";
+ (void)adapter.callMethod<jboolean>("cancelDiscovery");
+ }
+ // Restart the discovery and retry timer.
+ // The logic below is similar as in the start()
+ if (deviceDiscoveryStartAttemptsLeft > 0 &&
+ adapter.callMethod<jboolean>("startDiscovery"))
+ deviceDiscoveryStartTimeout->start();
+ else if (requestedMethods == QBluetoothDeviceDiscoveryAgent::ClassicMethod)
+ classicDiscoveryStartFail(); // No LE scan requested, scan is done
+ else
+ startLowEnergyScan(); // Continue to LE scan
+ });
+ }
+ deviceDiscoveryStartAttemptsLeft = deviceDiscoveryStartMaxAttempts;
+ deviceDiscoveryStartTimeout->start();
+
qCDebug(QT_BT_ANDROID)
<< "QBluetoothDeviceDiscoveryAgentPrivate::start() - Classic search successfully started.";
return;
@@ -264,9 +334,6 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
}
if (requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod) {
- // LE search only requested or classic discovery failed but lets try LE scan anyway
- Q_ASSERT(requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
-
if (QtAndroidPrivate::androidSdkVersion() < 18) {
qCDebug(QT_BT_ANDROID) << "Skipping Bluetooth Low Energy device scan due to"
"insufficient Android version.";
@@ -276,7 +343,7 @@ void QBluetoothDeviceDiscoveryAgentPrivate::start(QBluetoothDeviceDiscoveryAgent
emit q->error(lastError);
return;
}
-
+ // LE search only requested or classic discovery failed but lets try LE scan anyway
startLowEnergyScan();
}
}
@@ -285,15 +352,23 @@ void QBluetoothDeviceDiscoveryAgentPrivate::stop()
{
Q_Q(QBluetoothDeviceDiscoveryAgent);
+ pendingStart = false;
+
+ if (deviceDiscoveryStartTimeout)
+ deviceDiscoveryStartTimeout->stop();
+
if (m_active == NoScanActive)
return;
if (m_active == SDPScanActive) {
- if (pendingCancel)
+ if (pendingCancel) {
+ // If we had both a pending cancel and a pending start,
+ // we now have only a pending cancel.
+ // The pending start was canceled above.
return;
+ }
pendingCancel = true;
- pendingStart = false;
bool success = adapter.callMethod<jboolean>("cancelDiscovery");
if (!success) {
lastError = QBluetoothDeviceDiscoveryAgent::InputOutputError;
@@ -325,16 +400,9 @@ void QBluetoothDeviceDiscoveryAgentPrivate::processSdpDiscoveryFinished()
start(requestedMethods);
} else {
// check that it didn't finish due to turned off Bluetooth Device
- const int state = adapter.callMethod<jint>("getState");
- if (state != 12) { // BluetoothAdapter.STATE_ON
- m_active = NoScanActive;
- lastError = QBluetoothDeviceDiscoveryAgent::PoweredOffError;
- errorString = QBluetoothDeviceDiscoveryAgent::tr("Device is powered off");
- emit q->error(lastError);
+ if (setErrorIfPowerOff())
return;
- }
-
- // no BTLE scan requested
+ // Since no BTLE scan requested and classic scan is done => finished()
if (!(requestedMethods & QBluetoothDeviceDiscoveryAgent::LowEnergyMethod)) {
m_active = NoScanActive;
emit q->finished();