summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorJuha Vuolle <juha.vuolle@insta.fi>2022-08-29 12:36:23 +0300
committerJuha Vuolle <juha.vuolle@insta.fi>2022-09-13 09:26:38 +0300
commitbc6e4e28602eeb7a635fde762475136c418e1a4d (patch)
tree23d03238da2d0925deea9433cb254b9186b265e1 /tests
parent29246200389865197d20eb3e84a4a4c1301e706b (diff)
Add Android support for BT LE RSSI read
In addition add manual and autotest support Fixes: QTBUG-69747 Change-Id: I253b8ea7d3dd5b3e2dce65b83c31363673c931b8 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Alex Blasche <alexander.blasche@qt.io>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp29
-rw-r--r--tests/bttestui/btlocaldevice.cpp47
-rw-r--r--tests/bttestui/btlocaldevice.h8
-rw-r--r--tests/bttestui/main.qml5
-rw-r--r--tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp130
5 files changed, 213 insertions, 6 deletions
diff --git a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
index dac7e969..306704b7 100644
--- a/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
+++ b/tests/auto/qlowenergycontroller/tst_qlowenergycontroller.cpp
@@ -49,6 +49,7 @@ private slots:
void tst_readWriteDescriptor();
void tst_customProgrammableDevice();
void tst_errorCases();
+ void tst_rssiError();
private:
void verifyServiceProperties(const QLowEnergyService *info);
bool verifyClientCharacteristicValue(const QByteArray& value);
@@ -2735,6 +2736,34 @@ void tst_QLowEnergyController::tst_writeCharacteristicNoResponse()
QCOMPARE(control->error(), QLowEnergyController::NoError);
}
+using namespace Qt::Literals::StringLiterals;
+
+void tst_QLowEnergyController::tst_rssiError()
+{
+ // Create unconnected/invalid controller instances and verify that
+ // reading RSSI value triggers error signal. For the actual
+ // RSSI read testing see tst_qlowenergycontroller_device
+
+ // Peripheral
+ std::unique_ptr<QLowEnergyController> peripheral{QLowEnergyController::createPeripheral()};
+ QSignalSpy peripheralErrorSpy(peripheral.get(), &QLowEnergyController::errorOccurred);
+ peripheral->readRssi();
+ QTRY_VERIFY(!peripheralErrorSpy.isEmpty());
+ QCOMPARE(peripheralErrorSpy.takeFirst().at(0).value<QLowEnergyController::Error>(),
+ QLowEnergyController::Error::RssiReadError);
+ QCOMPARE(peripheral->error(), QLowEnergyController::Error::RssiReadError);
+
+ // Central
+ QBluetoothDeviceInfo info(QBluetoothAddress{u"11:22:33:44:55:66"_s}, u"invalid"_s, 1);
+ std::unique_ptr<QLowEnergyController> central{QLowEnergyController::createCentral(info)};
+ QSignalSpy centralErrorSpy(central.get(), &QLowEnergyController::errorOccurred);
+ central->readRssi();
+ QTRY_VERIFY(!centralErrorSpy.isEmpty());
+ QCOMPARE(centralErrorSpy.takeFirst().at(0).value<QLowEnergyController::Error>(),
+ QLowEnergyController::Error::RssiReadError);
+ QCOMPARE(central->error(), QLowEnergyController::Error::RssiReadError);
+}
+
QTEST_MAIN(tst_QLowEnergyController)
#include "tst_qlowenergycontroller.moc"
diff --git a/tests/bttestui/btlocaldevice.cpp b/tests/bttestui/btlocaldevice.cpp
index 98647085..e6aa8a8e 100644
--- a/tests/bttestui/btlocaldevice.cpp
+++ b/tests/bttestui/btlocaldevice.cpp
@@ -56,6 +56,7 @@ static constexpr const char* controllerErrorString[] = {
"RemHostClosed",
"AuthError",
"MissingPerm",
+ "RssiError"
};
static constexpr const char* serviceStateString[] = {
@@ -95,6 +96,8 @@ BtLocalDevice::BtLocalDevice(QObject *parent)
deviceAgent = new QBluetoothDeviceDiscoveryAgent(this);
connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
this, &BtLocalDevice::deviceDiscovered);
+ connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::deviceUpdated,
+ this, &BtLocalDevice::deviceUpdated);
connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::finished,
this, &BtLocalDevice::discoveryFinished);
connect(deviceAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred, this,
@@ -266,6 +269,19 @@ void BtLocalDevice::deviceDiscovered(const QBluetoothDeviceInfo &info)
if (info.name() == leRemotePeriphreralDeviceName) {
qDebug() << "#### Matching LE peripheral device found";
leRemotePeripheralDevice = info;
+ latestRSSI = QByteArray::number(info.rssi());
+ emit leChanged();
+ }
+}
+
+void BtLocalDevice::deviceUpdated(const QBluetoothDeviceInfo &info,
+ QBluetoothDeviceInfo::Fields updateFields)
+{
+ if (info.name() == leRemotePeriphreralDeviceName
+ && updateFields & QBluetoothDeviceInfo::Field::RSSI) {
+ qDebug() << "#### LE peripheral RSSI updated during scan";
+ latestRSSI = QByteArray::number(info.rssi());
+ emit leChanged();
}
}
@@ -1067,6 +1083,11 @@ void BtLocalDevice::centralCreate()
return;
}
+ if (deviceAgent && deviceAgent->isActive()) {
+ qDebug() << "###### Stopping device discovery agent";
+ deviceAgent->stop();
+ }
+
leCentralController.reset(QLowEnergyController::createCentral(leRemotePeripheralDevice));
emit leChanged();
@@ -1077,11 +1098,19 @@ void BtLocalDevice::centralCreate()
QObject::connect(leCentralController.get(), &QLowEnergyController::errorOccurred,
[](QLowEnergyController::Error error) {
- qDebug() << "QLowEnergyController peripheral errorOccurred:" << error;
+ qDebug() << "QLowEnergyController central errorOccurred:" << error;
});
QObject::connect(leCentralController.get(), &QLowEnergyController::stateChanged,
[this](QLowEnergyController::ControllerState state) {
- qDebug() << "QLowEnergyController peripheral stateChanged:" << state;
+ qDebug() << "QLowEnergyController central stateChanged:" << state;
+ if (state == QLowEnergyController::UnconnectedState)
+ latestRSSI = "N/A"_ba;
+ emit leChanged();
+ });
+ QObject::connect(leCentralController.get(), &QLowEnergyController::rssiRead,
+ [this](qint16 rssi) {
+ qDebug() << "QLowEnergyController central RSSI updated:" << rssi;
+ latestRSSI = QByteArray::number(rssi);
emit leChanged();
});
}
@@ -1222,6 +1251,7 @@ void BtLocalDevice::centralDelete()
{
qDebug() << "######" << "Delete central" << leCentralController.get();
leCentralController.reset(nullptr);
+ latestRSSI = "(N/A)"_ba;
emit leChanged();
}
@@ -1286,6 +1316,19 @@ QByteArray BtLocalDevice::centralServiceError() const
return serviceErrorString[leCentralService->error()];
}
+void BtLocalDevice::centralReadRSSI() const
+{
+ qDebug() << "######" << "LE central readRSSI";
+ if (!leCentralController)
+ return;
+ leCentralController->readRssi();
+}
+
+QByteArray BtLocalDevice::centralRSSI() const
+{
+ return latestRSSI;
+}
+
QByteArray BtLocalDevice::peripheralState() const
{
if (!lePeripheralController)
diff --git a/tests/bttestui/btlocaldevice.h b/tests/bttestui/btlocaldevice.h
index 0b2a0afd..97e64ed1 100644
--- a/tests/bttestui/btlocaldevice.h
+++ b/tests/bttestui/btlocaldevice.h
@@ -21,12 +21,15 @@ public:
~BtLocalDevice();
Q_PROPERTY(QString hostMode READ hostMode NOTIFY hostModeStateChanged)
Q_PROPERTY(int secFlags READ secFlags WRITE setSecFlags NOTIFY secFlagsChanged)
+
Q_PROPERTY(bool centralExists READ centralExists NOTIFY leChanged);
Q_PROPERTY(bool centralSubscribed READ centralSubscribed NOTIFY leChanged);
Q_PROPERTY(QByteArray centralState READ centralState NOTIFY leChanged);
Q_PROPERTY(QByteArray centralError READ centralError NOTIFY leChanged);
Q_PROPERTY(QByteArray centralServiceState READ centralServiceState NOTIFY leChanged);
Q_PROPERTY(QByteArray centralServiceError READ centralServiceError NOTIFY leChanged);
+ Q_PROPERTY(QByteArray centralRSSI READ centralRSSI NOTIFY leChanged);
+
Q_PROPERTY(QByteArray peripheralState READ peripheralState NOTIFY leChanged);
Q_PROPERTY(QByteArray peripheralError READ peripheralError NOTIFY leChanged);
Q_PROPERTY(QByteArray peripheralServiceState READ peripheralServiceState NOTIFY leChanged);
@@ -58,6 +61,8 @@ public slots:
//QBluetoothDeviceDiscoveryAgent
void deviceDiscovered(const QBluetoothDeviceInfo &info);
+ void deviceUpdated(const QBluetoothDeviceInfo &info,
+ QBluetoothDeviceInfo::Fields updateFields);
void discoveryFinished();
void discoveryCanceled();
void discoveryError(QBluetoothDeviceDiscoveryAgent::Error error);
@@ -113,6 +118,8 @@ public slots:
QByteArray centralServiceState() const;
QByteArray centralError() const;
QByteArray centralServiceError() const;
+ void centralReadRSSI() const;
+ QByteArray centralRSSI() const;
//QLowEnergyController peripheral
void peripheralCreate();
@@ -151,6 +158,7 @@ private:
QLowEnergyAdvertisingData leAdvertisingData;
QLowEnergyServiceData leServiceData;
QBluetoothDeviceInfo leRemotePeripheralDevice;
+ QByteArray latestRSSI = "N/A";
};
#endif // BTLOCALDEVICE_H
diff --git a/tests/bttestui/main.qml b/tests/bttestui/main.qml
index 1fc41e81..f0510fcf 100644
--- a/tests/bttestui/main.qml
+++ b/tests/bttestui/main.qml
@@ -235,6 +235,7 @@ Flickable {
Text { text: "SState:" + device.centralServiceState }
Text { text: "SError:" + device.centralServiceError }
Text { text: "Subscribed: " + device.centralSubscribed }
+ Text { text: "RSSI: " + device.centralRSSI }
}
}
// The ordinal numbers below indicate the typical sequence
@@ -275,6 +276,10 @@ Flickable {
buttonText: "DeleteController"
onClicked: device.centralDelete()
}
+ Button {
+ buttonText: "ReadRSSI"
+ onClicked: device.centralReadRSSI()
+ }
}
Column {
spacing: 8
diff --git a/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp b/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
index 5acf829d..87dab2f1 100644
--- a/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
+++ b/tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp
@@ -37,11 +37,16 @@ static const QLatin1String repeatedWriteTargetCharUuid("2192ee43-6d17-4e78-b286-
static const QLatin1String repeatedWriteNotifyCharUuid("b3f9d1a2-3d55-49c9-8b29-e09cec77ff86");
-
+// With the defines below some test cases are picked on at compile-time as opposed to runtime skips
+// to avoid the lengthy init() and clean() executions
#if defined(QT_ANDROID_BLUETOOTH) || defined(QT_WINRT_BLUETOOTH) || defined(Q_OS_DARWIN)
#define QT_BLUETOOTH_MTU_SUPPORTED
#endif
+#if defined(QT_ANDROID_BLUETOOTH) || defined(Q_OS_DARWIN)
+#define QT_BLUETOOTH_RSSI_SUPPORTED
+#endif
+
#if defined(QT_BLUETOOTH_MTU_SUPPORTED)
static const QLatin1String mtuServiceUuid("9a9483eb-cf4f-4c32-9a6b-794238d5b483");
static const QLatin1String mtuCharUuid("960d7e2a-a850-4a70-8064-cd74e9ccb6ff");
@@ -80,6 +85,9 @@ private slots:
#if defined(QT_BLUETOOTH_MTU_SUPPORTED)
void checkMtuNegotiation();
#endif
+#if defined(QT_BLUETOOTH_RSSI_SUPPORTED)
+ void rssiRead();
+#endif
void readWriteLargeCharacteristic();
void readDuringServiceDiscovery();
void readNotificationAndIndicationProperty();
@@ -229,8 +237,6 @@ void tst_qlowenergycontroller_device::readServerPlatform()
#if defined(QT_BLUETOOTH_MTU_SUPPORTED)
-// Don't use QSKIP here as that
-// would still cause lengthy init() and clean() executions.
void tst_qlowenergycontroller_device::checkMtuNegotiation()
{
// service discovery, including MTU negotiation
@@ -267,9 +273,125 @@ void tst_qlowenergycontroller_device::checkMtuNegotiation()
QCOMPARE(mtu, mController->mtu());
}
#endif
-
#undef QT_BLUETOOTH_MTU_SUPPORTED
+#if defined(QT_BLUETOOTH_RSSI_SUPPORTED)
+
+#define READ_AND_VERIFY_RSSI_VALUE \
+ errorSpy.clear(); \
+ rssiSpy.clear(); \
+ mController->readRssi(); \
+ QTRY_VERIFY(!rssiSpy.isEmpty()); \
+ QVERIFY(errorSpy.isEmpty()); \
+ rssi = rssiSpy.takeFirst().at(0).toInt(); \
+ QCOMPARE_GE(rssi, -127); \
+ QCOMPARE_LE(rssi, 127); \
+
+void tst_qlowenergycontroller_device::rssiRead()
+{
+ QSignalSpy errorSpy(mController.get(), &QLowEnergyController::errorOccurred);
+ QSignalSpy rssiSpy(mController.get(), &QLowEnergyController::rssiRead);
+ int rssi = 1000; // valid values are -127..127
+
+ // 1) Read RSSI in connected state
+ QCOMPARE(mController->state(), QLowEnergyController::ConnectedState);
+ READ_AND_VERIFY_RSSI_VALUE;
+ // Check that the value changes. This might seem flaky with stationary bluetooth
+ // devices but in practice the RSSI constantly fluctuates
+ const int initialRssi = rssi;
+ for (int attempt = 1; attempt < 20; attempt++) {
+ READ_AND_VERIFY_RSSI_VALUE;
+ QTest::qWait(200); // Provide a bit time for the RSSI to change
+ if (rssi != initialRssi)
+ break;
+ }
+ QVERIFY(rssi != initialRssi);
+
+ // 2) Read RSSI while discovering services
+ QVERIFY(mController->services().isEmpty());
+ mController->discoverServices();
+ QCOMPARE(mController->state(), QLowEnergyController::DiscoveringState);
+ READ_AND_VERIFY_RSSI_VALUE;
+
+ // 3) Read RSSI after services have been discovered
+ QTRY_COMPARE(mController->state(), QLowEnergyController::DiscoveredState);
+ READ_AND_VERIFY_RSSI_VALUE;
+
+ // 4) Read RSSI while discovering service details
+ QSharedPointer<QLowEnergyService> service(mController->createServiceObject(
+ QBluetoothUuid(repeatedWriteServiceUuid)));
+ QVERIFY(service != nullptr);
+ service->discoverDetails(QLowEnergyService::FullDiscovery);
+ QCOMPARE(service->state(), QLowEnergyService::ServiceState::RemoteServiceDiscovering);
+ READ_AND_VERIFY_RSSI_VALUE;
+
+ // 5) Read RSSI after service detail discovery
+ QTRY_COMPARE(service->state(), QLowEnergyService::ServiceState::RemoteServiceDiscovered);
+ READ_AND_VERIFY_RSSI_VALUE;
+
+ checkconnectionCounter(mController);
+
+ // 6) Read RSSI amidst characteristic reads and writes
+ QSignalSpy writeSpy(service.get(), &QLowEnergyService::characteristicWritten);
+ QSignalSpy readSpy(service.get(), &QLowEnergyService::characteristicRead);
+ auto characteristic = service->characteristic(QBluetoothUuid(repeatedWriteTargetCharUuid));
+ QByteArray value(8, 'a');
+ service->writeCharacteristic(characteristic, value);
+ READ_AND_VERIFY_RSSI_VALUE;
+ QTRY_VERIFY(!writeSpy.isEmpty());
+
+ service->readCharacteristic(characteristic);
+ READ_AND_VERIFY_RSSI_VALUE;
+ QTRY_VERIFY(!readSpy.isEmpty());
+
+ // A bit of stress-testing to check that read/write and
+ // RSSI reading don't interfere with one another
+ writeSpy.clear();
+ readSpy.clear();
+ rssiSpy.clear();
+ errorSpy.clear();
+ const int TIMES = 5;
+ for (int i = 0; i < TIMES; i++) {
+ mController->readRssi();
+ service->writeCharacteristic(characteristic, value);
+ mController->readRssi();
+ service->readCharacteristic(characteristic);
+ }
+ QTRY_COMPARE(writeSpy.size(), TIMES);
+ QTRY_COMPARE(readSpy.size(), TIMES);
+#if defined(Q_OS_ANDROID)
+ QTRY_COMPARE(rssiSpy.size(), TIMES * 2);
+#else
+ // On darwin several pending requests will get one callback
+ QTRY_COMPARE_GE(rssiSpy.size(), 1);
+#endif
+ QVERIFY(errorSpy.isEmpty());
+
+ // 7) Disconnect the device and verify we get error for RSSI read
+ errorSpy.clear();
+ rssiSpy.clear();
+ mController->disconnectFromDevice();
+ // First right after requesting disconnect
+ mController->readRssi();
+ QTRY_COMPARE(errorSpy.size(), 1);
+ QCOMPARE(errorSpy.takeFirst().at(0).value<QLowEnergyController::Error>(),
+ QLowEnergyController::Error::RssiReadError);
+ QCOMPARE(mController->error(), QLowEnergyController::Error::RssiReadError);
+ QVERIFY(rssiSpy.isEmpty());
+
+ // Then once the disconnection is complete
+ QTRY_COMPARE(mController->state(), QLowEnergyController::UnconnectedState);
+ errorSpy.clear();
+ mController->readRssi();
+ QTRY_COMPARE(errorSpy.size(), 1);
+ QCOMPARE(errorSpy.takeFirst().at(0).value<QLowEnergyController::Error>(),
+ QLowEnergyController::Error::RssiReadError);
+ QCOMPARE(mController->error(), QLowEnergyController::Error::RssiReadError);
+ QVERIFY(rssiSpy.isEmpty());
+}
+#endif
+#undef QT_BLUETOOTH_RSSI_SUPPORTED
+
void tst_qlowenergycontroller_device::checkconnectionCounter(
std::unique_ptr<QLowEnergyController> &mController)
{