diff options
author | Juha Vuolle <juha.vuolle@insta.fi> | 2022-08-29 12:36:23 +0300 |
---|---|---|
committer | Juha Vuolle <juha.vuolle@insta.fi> | 2022-09-13 09:26:38 +0300 |
commit | bc6e4e28602eeb7a635fde762475136c418e1a4d (patch) | |
tree | 23d03238da2d0925deea9433cb254b9186b265e1 /tests | |
parent | 29246200389865197d20eb3e84a4a4c1301e706b (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.cpp | 29 | ||||
-rw-r--r-- | tests/bttestui/btlocaldevice.cpp | 47 | ||||
-rw-r--r-- | tests/bttestui/btlocaldevice.h | 8 | ||||
-rw-r--r-- | tests/bttestui/main.qml | 5 | ||||
-rw-r--r-- | tests/manual/qlowenergycontroller/tst_qlowenergycontroller_device.cpp | 130 |
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) { |