summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2023-01-17 15:14:00 +0100
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-01-18 10:22:49 +0000
commita1d54b78d33c9216d8915209403088526e9acfe6 (patch)
tree6c4db1cb619bf62d41fc214400b39470deae8ecd
parent1abcd7501220d4f3e248426a1ac1ce2920f7ef9c (diff)
Android LE Peripheral: properly update characteristic and descriptor local values
Android backend did not update the serivice's characteristic and descriptor values after executing writeCharacteristic/writeDescriptor. As a result, the connected central saw the correct updated value, while local request still returned the old value. This patch fixes it by properly updating the local values when the write operations complete successfully. Change-Id: Ie09299b6c72bbf92ab6c824c3ca23f0136f0457e Reviewed-by: Juha Vuolle <juha.vuolle@qt.io> (cherry picked from commit e5d1b776b1445eb6fde6ce33b54fd98e9fb49994) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/bluetooth/qlowenergycontroller_android.cpp4
-rw-r--r--tests/manual/CMakeLists.txt1
-rw-r--r--tests/manual/qlowenergycontroller_peripheral/CMakeLists.txt59
-rw-r--r--tests/manual/qlowenergycontroller_peripheral/tst_qlowenergycontroller_peripheral.cpp143
4 files changed, 207 insertions, 0 deletions
diff --git a/src/bluetooth/qlowenergycontroller_android.cpp b/src/bluetooth/qlowenergycontroller_android.cpp
index 2d7a7eae..2d7839ad 100644
--- a/src/bluetooth/qlowenergycontroller_android.cpp
+++ b/src/bluetooth/qlowenergycontroller_android.cpp
@@ -247,6 +247,8 @@ void QLowEnergyControllerPrivateAndroid::writeCharacteristic(
"writeCharacteristic",
service->androidService.object<QtJniTypes::BluetoothGattService>(),
charUuid.object<QtJniTypes::UUID>(), payload);
+ if (result)
+ service->characteristicList[charHandle].value = newValue;
}
}
}
@@ -292,6 +294,8 @@ void QLowEnergyControllerPrivateAndroid::writeDescriptor(
service->androidService.object<QtJniTypes::BluetoothGattService>(),
charUuid.object<QtJniTypes::UUID>(), descUuid.object<QtJniTypes::UUID>(),
payload);
+ if (result)
+ service->characteristicList[charHandle].descriptorList[descHandle].value = newValue;
}
}
}
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt
index e53c7e10..2cce14f6 100644
--- a/tests/manual/CMakeLists.txt
+++ b/tests/manual/CMakeLists.txt
@@ -3,5 +3,6 @@
if(TARGET Qt::Bluetooth)
add_subdirectory(qlowenergycontroller)
+ add_subdirectory(qlowenergycontroller_peripheral)
endif()
diff --git a/tests/manual/qlowenergycontroller_peripheral/CMakeLists.txt b/tests/manual/qlowenergycontroller_peripheral/CMakeLists.txt
new file mode 100644
index 00000000..abd84bb8
--- /dev/null
+++ b/tests/manual/qlowenergycontroller_peripheral/CMakeLists.txt
@@ -0,0 +1,59 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16...3.21)
+
+if(NOT TARGET Qt::Bluetooth)
+ # for standalone build (and the only way for iOS)
+ project(tst_qlowenergycontroller_peripheral LANGUAGES CXX)
+
+ set(CMAKE_AUTOMOC ON)
+
+ find_package(Qt6 REQUIRED COMPONENTS Bluetooth Core Test Gui)
+
+ qt_add_executable(tst_qlowenergycontroller_peripheral
+ tst_qlowenergycontroller_peripheral.cpp
+ )
+ target_link_libraries(tst_qlowenergycontroller_peripheral
+ PUBLIC
+ Qt::Core
+ Qt::Bluetooth
+ Qt::Test
+ Qt::Gui
+ )
+
+else()
+
+ qt_internal_add_test(tst_qlowenergycontroller_peripheral
+ SOURCES
+ tst_qlowenergycontroller_peripheral.cpp
+ LIBRARIES
+ Qt::Bluetooth
+ )
+
+ qt_internal_extend_target(tst_qlowenergycontroller_peripheral
+ CONDITION ANDROID AND NOT ANDROID_EMBEDDED
+ DEFINES
+ QT_ANDROID_BLUETOOTH
+ )
+
+endif()
+
+set_target_properties(tst_qlowenergycontroller_peripheral PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+if(APPLE)
+ # Ninja has trouble with relative paths, convert to absolute as a workaround
+ get_filename_component(SHARED_PLIST_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../shared ABSOLUTE)
+ if(IOS)
+ set_target_properties(tst_qlowenergycontroller_peripheral PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST "${SHARED_PLIST_DIR}/Info.ios.plist"
+ )
+ else()
+ set_target_properties(tst_qlowenergycontroller_peripheral PROPERTIES
+ MACOSX_BUNDLE_INFO_PLIST "${SHARED_PLIST_DIR}/Info.macos.plist"
+ )
+ endif()
+endif()
diff --git a/tests/manual/qlowenergycontroller_peripheral/tst_qlowenergycontroller_peripheral.cpp b/tests/manual/qlowenergycontroller_peripheral/tst_qlowenergycontroller_peripheral.cpp
new file mode 100644
index 00000000..0d7efa07
--- /dev/null
+++ b/tests/manual/qlowenergycontroller_peripheral/tst_qlowenergycontroller_peripheral.cpp
@@ -0,0 +1,143 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include <QTest>
+
+#include <QBluetoothLocalDevice>
+#include <QLowEnergyController>
+#include <QLowEnergyServiceData>
+#include <QLowEnergyCharacteristicData>
+#include <QLowEnergyDescriptorData>
+
+using namespace Qt::Literals::StringLiterals;
+
+static constexpr auto leServiceUuid{"10f5e37c-ac16-11eb-ae5c-93d3a763feed"_L1};
+static constexpr auto leCharUuid1{"11f4f68e-ac16-11eb-9956-cfe55a8ccafe"_L1};
+static const qsizetype leCharacteristicSize = 4; // Set to 1...512 bytes
+static QByteArray leCharacteristicValue = QByteArray{leCharacteristicSize, 1};
+static QByteArray leDescriptorValue = "a descriptor value"_ba;
+
+class tst_qlowenergycontroller_peripheral : public QObject
+{
+ Q_OBJECT
+private slots:
+ void initTestCase();
+ void init();
+ void cleanup();
+
+ void localCharacteristicReadAfterUpdate();
+ void localDescriptorReadAfterUpdate();
+
+private:
+ QBluetoothHostInfo mDevice;
+ std::unique_ptr<QLowEnergyController> mController;
+ QList<QSharedPointer<QLowEnergyService>> mServices;
+};
+
+void tst_qlowenergycontroller_peripheral::initTestCase()
+{
+#ifndef Q_OS_IOS
+ auto devices = QBluetoothLocalDevice::allDevices();
+ if (devices.isEmpty())
+ QSKIP("Failed to find local adapter");
+ else
+ mDevice = devices.back();
+#endif // Q_OS_IOS
+}
+
+void tst_qlowenergycontroller_peripheral::init()
+{
+ QList<QLowEnergyServiceData> serviceDefinitions;
+
+ QLowEnergyServiceData sd;
+ sd.setType(QLowEnergyServiceData::ServiceTypePrimary);
+ sd.setUuid(QBluetoothUuid(leServiceUuid));
+
+ QLowEnergyCharacteristicData charData;
+ charData.setUuid(QBluetoothUuid(leCharUuid1));
+ charData.setValue(leCharacteristicValue);
+ charData.setValueLength(leCharacteristicSize, leCharacteristicSize);
+ charData.setProperties(QLowEnergyCharacteristic::PropertyType::Read
+ | QLowEnergyCharacteristic::PropertyType::Write
+ | QLowEnergyCharacteristic::PropertyType::Notify
+ | QLowEnergyCharacteristic::ExtendedProperty);
+
+ const QLowEnergyDescriptorData clientConfig(
+ QBluetoothUuid::DescriptorType::ClientCharacteristicConfiguration,
+ QLowEnergyCharacteristic::CCCDDisable);
+ charData.addDescriptor(clientConfig);
+
+ const QLowEnergyDescriptorData userDescription(
+ QBluetoothUuid::DescriptorType::CharacteristicUserDescription,
+ leDescriptorValue);
+ charData.addDescriptor(userDescription);
+
+ const QLowEnergyDescriptorData extendedProperties(
+ QBluetoothUuid::DescriptorType::CharacteristicExtendedProperties,
+ // From bluetooth specs: length 2 bytes
+ // bit 0: reliable write, bit 1: writable auxiliaries
+ QByteArray::fromHex("0300"));
+ charData.addDescriptor(extendedProperties);
+
+ sd.addCharacteristic(charData);
+
+ serviceDefinitions << sd;
+
+
+#ifndef Q_OS_IOS
+ mController.reset(QLowEnergyController::createPeripheral(mDevice.address()));
+#else
+ mController.reset(QLowEnergyController::createPeripheral());
+#endif // Q_OS_IOS
+ QVERIFY(mController);
+
+ for (const auto &serviceData : serviceDefinitions) {
+ mServices.emplaceBack(mController->addService(serviceData));
+ }
+}
+
+void tst_qlowenergycontroller_peripheral::cleanup()
+{
+ if (mController)
+ mController->stopAdvertising();
+
+ mController.reset();
+ mServices.clear();
+}
+
+void tst_qlowenergycontroller_peripheral::localCharacteristicReadAfterUpdate()
+{
+#ifdef Q_OS_WINDOWS
+ QSKIP("Windows does not support peripheral");
+#endif
+ auto characteristic = mServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
+ QVERIFY(characteristic.isValid());
+ const auto initialValue = characteristic.value();
+ auto newValue = initialValue;
+ newValue[0] = newValue[0] + 1;
+ mServices[0]->writeCharacteristic(characteristic, newValue);
+ QTRY_COMPARE(characteristic.value(), newValue);
+}
+
+void tst_qlowenergycontroller_peripheral::localDescriptorReadAfterUpdate()
+{
+#ifdef Q_OS_WINDOWS
+ QSKIP("Windows does not support peripheral");
+#elif defined Q_OS_DARWIN
+ QSKIP("Apple devices do not support descriptor value update");
+#endif
+ auto characteristic = mServices[0]->characteristic(QBluetoothUuid(leCharUuid1));
+ QVERIFY(characteristic.isValid());
+ auto descriptor = characteristic.descriptor(
+ QBluetoothUuid::DescriptorType::CharacteristicUserDescription);
+ QVERIFY(descriptor.isValid());
+ const auto initialValue = descriptor.value();
+ auto newValue = initialValue;
+ newValue[0] = newValue[0] + 1;
+ mServices[0]->writeDescriptor(descriptor, newValue);
+ QCOMPARE(descriptor.value(), newValue);
+}
+
+QTEST_MAIN(tst_qlowenergycontroller_peripheral)
+
+#include "tst_qlowenergycontroller_peripheral.moc"