diff options
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | trial/direct_bt/CMakeLists.txt | 3 | ||||
-rw-r--r-- | trial/direct_bt/dbt_base_client_server.hpp | 65 | ||||
-rw-r--r-- | trial/direct_bt/dbt_client00.hpp | 83 | ||||
-rw-r--r-- | trial/direct_bt/dbt_client_server1x.hpp | 222 | ||||
-rw-r--r-- | trial/direct_bt/dbt_client_test.hpp | 2 | ||||
-rw-r--r-- | trial/direct_bt/dbt_constants.hpp | 67 | ||||
-rw-r--r-- | trial/direct_bt/dbt_endpoint.hpp | 28 | ||||
-rw-r--r-- | trial/direct_bt/dbt_server00.hpp | 173 | ||||
-rw-r--r-- | trial/direct_bt/dbt_server_test.hpp | 4 | ||||
-rw-r--r-- | trial/direct_bt/dbt_utils.hpp | 10 | ||||
-rw-r--r-- | trial/direct_bt/test_client_server00.cpp | 5 | ||||
-rw-r--r-- | trial/direct_bt/test_client_server01_NoEnc.cpp | 75 | ||||
-rw-r--r-- | trial/java/trial/org/direct_bt/DBTClient00.java | 2 | ||||
-rw-r--r-- | trial/java/trial/org/direct_bt/DBTClientServer1x.java | 15 | ||||
-rw-r--r-- | trial/java/trial/org/direct_bt/DBTServer00.java | 10 |
16 files changed, 548 insertions, 217 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index fd484e69..e6127e60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ endif(BUILD_TESTING) if (BUILD_TRIAL) enable_testing () + add_subdirectory (trial/direct_bt) if (BUILDJAVA) configure_file (${CMAKE_CURRENT_SOURCE_DIR}/trial/java/manifest.txt.in ${CMAKE_CURRENT_BINARY_DIR}/trial/java/manifest.txt) add_subdirectory (trial/java) diff --git a/trial/direct_bt/CMakeLists.txt b/trial/direct_bt/CMakeLists.txt index d0ee7d35..73a115e8 100644 --- a/trial/direct_bt/CMakeLists.txt +++ b/trial/direct_bt/CMakeLists.txt @@ -4,9 +4,10 @@ include_directories( ) # These examples use the standard separate compilation -// file(GLOB_RECURSE SOURCES_IDIOMATIC_EXAMPLES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "test_*.cpp") +# file(GLOB_RECURSE SOURCES_IDIOMATIC_EXAMPLES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "test_*.cpp") set( SOURCES_IDIOMATIC_EXAMPLES test_client_server00.cpp + test_client_server01_NoEnc.cpp ) string( REPLACE ".cpp" "" BASENAMES_IDIOMATIC_EXAMPLES "${SOURCES_IDIOMATIC_EXAMPLES}" ) diff --git a/trial/direct_bt/dbt_base_client_server.hpp b/trial/direct_bt/dbt_base_client_server.hpp index 0d50d3d3..6e731a98 100644 --- a/trial/direct_bt/dbt_base_client_server.hpp +++ b/trial/direct_bt/dbt_base_client_server.hpp @@ -30,10 +30,22 @@ #include "dbt_server00.hpp" #include "dbt_client00.hpp" +#include <jau/simple_timer.hpp> + class BaseDBTClientServer { private: static constexpr const bool DEBUG = true; + jau::simple_timer timeour_timer = jau::simple_timer("DBTTrial-Timeout", 1_s /* shutdown timeout */); + + jau::fraction_i64 timeout_func(jau::simple_timer& timer) { + if( !timer.shall_stop() ) { + fprintf(stderr, "DBTTrial Timeout -> abort\n"); + abort(); + } + return 0_s; + } + BaseDBTClientServer() noexcept { if( DEBUG ) { setenv("direct_bt.debug", "true", 1 /* overwrite */); @@ -45,17 +57,17 @@ class BaseDBTClientServer { } void close() { + timeour_timer.stop(); BTManager& manager = BTManager::get(); - if( nullptr != manager ) { - jau::darray<BTAdapterRef> adapters = manager.getAdapters(); - for(BTAdapterRef a : adapters) { - a->stopAdvertising(); - a->stopDiscovery(); - a->setPowered(false); - } - // All implicit via destructor or shutdown hook! - manager.close(); /* implies: adapter.close(); */ + + jau::darray<BTAdapterRef> adapters = manager.getAdapters(); + for(BTAdapterRef a : adapters) { + a->stopAdvertising(); + a->stopDiscovery(); + a->setPowered(false); } + // All implicit via destructor or shutdown hook! + manager.close(); /* implies: adapter.close(); */ } public: @@ -75,20 +87,22 @@ class BaseDBTClientServer { * Ensure * - all adapter are powered off */ - void setupTest() { + void setupTest(const jau::fraction_i64 timeout = 0_s) { + timeour_timer.stop(); BTManager& manager = BTManager::get(); - if( nullptr != manager ) { - jau::darray<BTAdapterRef> adapters = manager.getAdapters(); - for(BTAdapterRef a : adapters) { - a->stopAdvertising(); - a->stopDiscovery(); - REQUIRE( a->setPowered(false) ); - } + jau::darray<BTAdapterRef> adapters = manager.getAdapters(); + for(BTAdapterRef a : adapters) { + a->stopAdvertising(); + a->stopDiscovery(); + REQUIRE( a->setPowered(false) ); } BTDeviceRegistry::clearWaitForDevices(); BTDeviceRegistry::clearProcessedDevices(); BTDeviceRegistry::clearProcessingDevices(); BTSecurityRegistry::clear(); + if( !timeout.is_zero() ) { + timeour_timer.start(timeout, jau::bindMemberFunc(this, &BaseDBTClientServer::timeout_func)); + } } /** @@ -99,17 +113,16 @@ class BaseDBTClientServer { * - clear BTSecurityRegistry */ void cleanupTest() { + timeour_timer.stop(); BTManager& manager = BTManager::get(); - if( nullptr != manager ) { - jau::darray<BTAdapterRef> adapters = manager.getAdapters(); - for(BTAdapterRef a : adapters) { - { // if( false ) { - a->removeAllStatusListener(); - } - a->stopAdvertising(); - a->stopDiscovery(); - REQUIRE( a->setPowered(false) ); + jau::darray<BTAdapterRef> adapters = manager.getAdapters(); + for(BTAdapterRef a : adapters) { + { // if( false ) { + a->removeAllStatusListener(); } + a->stopAdvertising(); + a->stopDiscovery(); + REQUIRE( a->setPowered(false) ); } BTDeviceRegistry::clearWaitForDevices(); BTDeviceRegistry::clearProcessedDevices(); diff --git a/trial/direct_bt/dbt_client00.hpp b/trial/direct_bt/dbt_client00.hpp index 88d65e8d..1f964920 100644 --- a/trial/direct_bt/dbt_client00.hpp +++ b/trial/direct_bt/dbt_client00.hpp @@ -39,7 +39,7 @@ using namespace jau::fractions_i64_literals; * This central BTRole::Master participant works with DBTServer00. */ class DBTClient00 : public DBTClientTest { - private: + public: /** * Disconnect after processing. * @@ -57,11 +57,10 @@ class DBTClient00 : public DBTClientTest { */ bool REMOVE_DEVICE = false; - const bool GATT_VERBOSE = false; - const bool SHOW_UPDATE_EVENTS = false; + DiscoveryPolicy discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY; // default value - const uint64_t timestamp_t0 = getCurrentMilliseconds(); - // const fraction_i64 timestamp_t0 = jau::getMonotonicMicroseconds(); + static const bool GATT_VERBOSE = false; + static const bool SHOW_UPDATE_EVENTS = false; jau::sc_atomic_int deviceReadyCount = 0; @@ -71,9 +70,17 @@ class DBTClient00 : public DBTClientTest { jau::sc_atomic_int completedMeasurements = 0; jau::sc_atomic_int measurementsLeft = 0; + private: + const uint64_t timestamp_t0 = getCurrentMilliseconds(); + // const fraction_i64 timestamp_t0 = jau::getMonotonicMicroseconds(); + const uint8_t cmd_arg = 0x44; class MyAdapterStatusListener : public AdapterStatusListener { + public: + DBTClient00& parent; + + MyAdapterStatusListener(DBTClient00& p) : parent(p) {} void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, const AdapterSetting changedmask, const uint64_t timestamp) override { @@ -101,17 +108,17 @@ class DBTClient00 : public DBTClientTest { if( !BTDeviceRegistry::isDeviceProcessing( device->getAddressAndType() ) && ( !BTDeviceRegistry::isWaitingForAnyDevice() || ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device->getName()) && - ( 0 < measurementsLeft || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) + ( 0 < parent.measurementsLeft || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) ) ) ) { fprintf_td(stderr, "****** Client FOUND__-0: Connecting %s\n", device->toString(true).c_str()); { - const uint64_t td = jau::getCurrentMilliseconds() - timestamp_t0; // adapter-init -> now + const uint64_t td = jau::getCurrentMilliseconds() - parent.timestamp_t0; // adapter-init -> now fprintf_td(stderr, "PERF: adapter-init -> FOUND__-0 %" PRIu64 " ms\n", td); } - std::thread dc(&DBTClient00::connectDiscoveredDevice, device); // @suppress("Invalid arguments") + std::thread dc(&DBTClient00::connectDiscoveredDevice, &parent, device); // @suppress("Invalid arguments") dc.detach(); return true; } else { @@ -121,6 +128,9 @@ class DBTClient00 : public DBTClientTest { } void deviceUpdated(BTDeviceRef device, const EIRDataType updateMask, const uint64_t timestamp) override { + (void)device; + (void)updateMask; + (void)timestamp; } void deviceConnected(BTDeviceRef device, const uint16_t handle, const uint64_t timestamp) override { @@ -193,18 +203,18 @@ class DBTClient00 : public DBTClientTest { (void)timestamp; if( !BTDeviceRegistry::isDeviceProcessing( device->getAddressAndType() ) && ( !BTDeviceRegistry::isWaitingForAnyDevice() || - ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device.getName()) && - ( 0 < measurementsLeft || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) + ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType().address, device->getName()) && + ( 0 < parent.measurementsLeft || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) ) ) ) { - deviceReadyCount++; - fprintf_td(stderr, "****** Client READY-0: Processing[%d] %s\n", deviceReadyCount.load(), device->toString(true).c_str()); + parent.deviceReadyCount++; + fprintf_td(stderr, "****** Client READY-0: Processing[%d] %s\n", parent.deviceReadyCount.load(), device->toString(true).c_str()); BTDeviceRegistry::addToProcessingDevices(device->getAddressAndType(), device->getName()); // Be nice to Test* case, allowing to reach its own listener.deviceReady() added later - std::thread dc(&DBTClient00::processReadyDevice, device); + std::thread dc(&DBTClient00::processReadyDevice, &parent, device); dc.detach(); // processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution! } else { @@ -218,12 +228,8 @@ class DBTClient00 : public DBTClientTest { to_hexstring(handle).c_str(), device->toString(true).c_str()); (void)timestamp; - if( REMOVE_DEVICE ) { - std::thread dc(&DBTClient00::removeDevice, device); // @suppress("Invalid arguments") - dc.detach(); - } else { - BTDeviceRegistry::removeFromProcessingDevices(device->getAddressAndType()); - } + std::thread dc(&DBTClient00::removeDevice, &parent, device); // @suppress("Invalid arguments") + dc.detach(); } std::string toString() const override { @@ -234,11 +240,11 @@ class DBTClient00 : public DBTClientTest { class MyGATTEventListener : public BTGattChar::Listener { private: + DBTClient00& parent; int i, j; public: - - MyGATTEventListener(int i_, int j_) : i(i_), j(j_) {} + MyGATTEventListener(DBTClient00& p, int i_, int j_) : parent(p), i(i_), j(j_) {} void notificationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp) override { if( GATT_VERBOSE ) { @@ -249,7 +255,7 @@ class DBTClient00 : public DBTClientTest { fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } - ++notificationsReceived; + parent.notificationsReceived++; } void indicationReceived(BTGattCharRef charDecl, @@ -264,7 +270,7 @@ class DBTClient00 : public DBTClientTest { fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } - ++indicationsReceived; + parent.indicationsReceived++; } }; @@ -273,19 +279,19 @@ class DBTClient00 : public DBTClientTest { EUI48 useAdapter = EUI48::ALL_DEVICE; BTMode btMode = BTMode::DUAL; BTAdapterRef clientAdapter = nullptr; - MyAdapterStatusListener myAdapterStatusListener = new MyAdapterStatusListener(); + std::shared_ptr<AdapterStatusListener> myAdapterStatusListener = std::make_shared<MyAdapterStatusListener>(*this); public: - DBTClient00(const std::string& adapterName, const EUI48 useAdapter, const BTMode btMode) { - this->adapterName = adapterName; - this->useAdapter = useAdapter; - this->btMode = btMode; + DBTClient00(const std::string& adapterName_, const EUI48 useAdapter_, const BTMode btMode_) { + this->adapterName = adapterName_; + this->useAdapter = useAdapter_; + this->btMode = btMode_; } std::string getName() override { return adapterName; } - void setAdapter(BTAdapterRef clientAdapter) override { - this->clientAdapter = clientAdapter; + void setAdapter(BTAdapterRef clientAdapter_) override { + this->clientAdapter = clientAdapter_; } BTAdapterRef getAdapter() override { return clientAdapter; } @@ -411,7 +417,7 @@ class DBTClient00 : public DBTClientTest { const jau::TROOctets& resp = cmd.getResponse(); if( 1 == resp.size() && resp.get_uint8_nc(0) == cmd_arg ) { fprintf_td(stderr, "Client Success: %s -> %s (echo response)\n", cmd.toString().c_str(), resp.toString().c_str()); - ++completedGATTCommands; + completedGATTCommands++; } else { fprintf_td(stderr, "Client Failure: %s -> %s (different response)\n", cmd.toString().c_str(), resp.toString().c_str()); } @@ -460,7 +466,7 @@ class DBTClient00 : public DBTClientTest { bool cccdEnableResult[2]; if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) { // ClientCharConfigDescriptor (CCD) is available - std::shared_ptr<BTGattChar::Listener> cl = std::make_shared<MyGATTEventListener>(i, j); + std::shared_ptr<BTGattChar::Listener> cl = std::make_shared<MyGATTEventListener>(*this, i, j); bool clAdded = serviceChar->addCharListener( cl ); if( GATT_VERBOSE ) { fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", @@ -480,7 +486,7 @@ class DBTClient00 : public DBTClientTest { do { success = completedGATTCommands >= 1 && ( notificationsReceived >= 2 || indicationsReceived >= 2 ); if( !success ) { - const fraction_i64 td = ( getMonotonicTime() - t0 ).to_fraction(); + const fraction_i64 td = ( getMonotonicTime() - t0 ).to_fraction_i64(); timeout = 3_s < td; if( !timeout ) { std::this_thread::sleep_for(std::chrono::milliseconds(17)); @@ -552,10 +558,10 @@ class DBTClient00 : public DBTClientTest { } if( success ) { - ++completedMeasurements; + completedMeasurements++; } if( 0 < measurementsLeft ) { - --measurementsLeft; + measurementsLeft--; } fprintf_td(stderr, "****** Client Processing Ready Device: Success %d; Measurements completed %d" ", left %d; Received notitifications %d, indications %d" @@ -575,8 +581,7 @@ class DBTClient00 : public DBTClientTest { } } - static DiscoveryPolicy discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY; // default value - static bool le_scan_active = true; // default value + static const bool le_scan_active = true; // default value static const uint16_t le_scan_interval = 24; // default value static const uint16_t le_scan_window = 24; // default value static const uint8_t filter_policy = 0; // default value @@ -595,12 +600,12 @@ class DBTClient00 : public DBTClientTest { } HCIStatusCode stopDiscovery(BTAdapterRef adapter, const std::string& msg) override { - if( !useAdapter.equals(EUI48::ALL_DEVICE) && !useAdapter.equals(adapter->getAddressAndType().address) ) { + if( useAdapter != EUI48::ALL_DEVICE && useAdapter != adapter->getAddressAndType().address ) { fprintf_td(stderr, "****** Client Stop discovery (%s): Adapter not selected: %s\n", msg.c_str(), adapter->toString().c_str()); return HCIStatusCode::FAILED; } - HCIStatusCode status = adapter.stopDiscovery(); + HCIStatusCode status = adapter->stopDiscovery(); fprintf_td(stderr, "****** Client Stop discovery (%s) result: %s\n", msg.c_str(), to_string(status).c_str()); return status; } diff --git a/trial/direct_bt/dbt_client_server1x.hpp b/trial/direct_bt/dbt_client_server1x.hpp new file mode 100644 index 00000000..3947cab6 --- /dev/null +++ b/trial/direct_bt/dbt_client_server1x.hpp @@ -0,0 +1,222 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <iostream> +#include <cassert> +#include <cinttypes> +#include <cstring> + +#include "dbt_base_client_server.hpp" + +using namespace direct_bt; + +/** + * Testing a full Bluetooth server and client lifecycle of operations, requiring two BT adapter: + * - start server advertising + * - start client discovery and connect to server when discovered + * - client/server processing of connection when ready + * - client disconnect + * - server stop advertising + * - security-level: NONE, ENC_ONLY freshly-paired and ENC_ONLY pre-paired + * - reuse server-adapter for client-mode discovery (just toggle on/off) + */ +class DBTClientServer1x { + private: + std::mutex mtx_sync; + BTDeviceRef lastCompletedDevice = nullptr; + PairingMode lastCompletedDevicePairingMode = PairingMode::NONE; + BTSecurityLevel lastCompletedDeviceSecurityLevel = BTSecurityLevel::NONE; + EInfoReport lastCompletedDeviceEIR; + + public: + void test8x_fullCycle(const std::string& suffix, const bool server_client_order, + const bool serverSC, const BTSecurityLevel secLevelServer, const bool serverShallHaveKeys, + const BTSecurityLevel secLevelClient, const bool clientShallHaveKeys) + { + (void)serverShallHaveKeys; + + BTManager & manager = BTManager::get(); + { + jau::darray<BTAdapterRef> adapters = manager.getAdapters(); + jau::fprintf_td(stderr, "Adapter: Count %u\n", adapters.size()); + + for(jau::nsize_t i=0; i<adapters.size(); i++) { + jau::fprintf_td(stderr, "%u: %s\n", i, adapters[i]->toString().c_str()); + } + REQUIRE( adapters.size() >= 2 ); + } + + std::shared_ptr<DBTServer00> server = std::make_shared<DBTServer00>("S-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL, serverSC, secLevelServer); + server->servingConnectionsLeft = 1; + + std::shared_ptr<DBTClient00> client = std::make_shared<DBTClient00>("C-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL); + + ChangedAdapterSetCallback myChangedAdapterSetFunc = DBTEndpoint::initChangedAdapterSetListener(manager, + server_client_order ? std::vector<DBTEndpointRef>{ server, client } : std::vector<DBTEndpointRef>{ client, server } ); + + const std::string serverName = server->getName(); + BTDeviceRegistry::addToWaitForDevices( serverName ); + { + BTSecurityRegistry::Entry * sec = BTSecurityRegistry::getOrCreate(serverName); + sec->sec_level = secLevelClient; + } + client->KEEP_CONNECTED = false; // default, auto-disconnect after work is done + client->REMOVE_DEVICE = false; // default, test side-effects + client->measurementsLeft = 1; + client->discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED; + + lastCompletedDevice = nullptr; + lastCompletedDevicePairingMode = PairingMode::NONE; + lastCompletedDeviceSecurityLevel = BTSecurityLevel::NONE; + lastCompletedDeviceEIR.clear(); + class MyAdapterStatusListener : public AdapterStatusListener { + private: + DBTClientServer1x& parent; + public: + MyAdapterStatusListener(DBTClientServer1x& p) : parent(p) {} + + void deviceReady(BTDeviceRef device, const uint64_t timestamp) override { + (void)timestamp; + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor + parent.lastCompletedDevice = device; + parent.lastCompletedDevicePairingMode = device->getPairingMode(); + parent.lastCompletedDeviceSecurityLevel = device->getConnSecurityLevel(); + parent.lastCompletedDeviceEIR = *device->getEIR(); + fprintf_td(stderr, "XXXXXX Client Ready: %s\n", device->toString(true).c_str()); + } + + std::string toString() const override { return "DBTClientServer1x::Client"; } + }; + std::shared_ptr<AdapterStatusListener> clientAdapterStatusListener = std::make_shared<MyAdapterStatusListener>(*this); + REQUIRE( true == client->getAdapter()->addStatusListener(clientAdapterStatusListener) ); + + // + // Server start + // + DBTEndpoint::checkInitializedState(server); + DBTServerTest::startAdvertising(server, false /* current_exp_advertising_state */, "test"+suffix+"_startAdvertising"); + + // + // Client start + // + DBTEndpoint::checkInitializedState(client); + DBTClientTest::startDiscovery(client, false /* current_exp_discovering_state */, "test"+suffix+"_startDiscovery"); + + bool done = false; + do { + { + const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + done = ! ( 1 > server->servedConnections || + 1 > client->completedMeasurements || + nullptr == lastCompletedDevice || + lastCompletedDevice->getConnected() ); + } + if( !done ) { + jau::sleep_for( 88_ms ); + } + + } while( !done ); + { + const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + REQUIRE( 1 == server->servedConnections ); + REQUIRE( 1 == client->completedMeasurements ); + REQUIRE( nullptr != lastCompletedDevice ); + REQUIRE( EIRDataType::NONE != lastCompletedDeviceEIR.getEIRDataMask() ); + REQUIRE( false == lastCompletedDevice->getConnected() ); + } + + // + // Client stop + // + DBTClientTest::stopDiscovery(client, true /* current_exp_discovering_state */, "test"+suffix+"_stopDiscovery"); + client->close("test"+suffix+"_close"); + + // + // Server stop + // + DBTServerTest::stop(server, false /* current_exp_advertising_state */, "test"+suffix+"_stopAdvertising"); + server->close("test"+suffix+"_close"); + + // + // Validating Security Mode + // + SMPKeyBin clientKeys = SMPKeyBin::read(DBTConstants::CLIENT_KEY_PATH, *lastCompletedDevice, true /* verbose */); + REQUIRE( true == clientKeys.isValid() ); + const BTSecurityLevel clientKeysSecLevel = clientKeys.getSecLevel(); + REQUIRE( secLevelClient == clientKeysSecLevel); + { + if( clientShallHaveKeys ) { + // Using encryption: pre-paired + REQUIRE( PairingMode::PRE_PAIRED == lastCompletedDevicePairingMode ); + REQUIRE( BTSecurityLevel::ENC_ONLY == lastCompletedDeviceSecurityLevel ); // pre-paired fixed level, no auth + } else if( BTSecurityLevel::NONE < secLevelClient ) { + // Using encryption: Newly paired + REQUIRE( PairingMode::PRE_PAIRED != lastCompletedDevicePairingMode ); + REQUIRE_MSG( "PairingMode client "+to_string(lastCompletedDevicePairingMode)+" not > NONE", PairingMode::NONE < lastCompletedDevicePairingMode ); + REQUIRE_MSG( "SecurityLevel client "+to_string(lastCompletedDeviceSecurityLevel)+" not >= "+to_string(secLevelClient), secLevelClient <= lastCompletedDeviceSecurityLevel ); + } else { + // No encryption: No pairing + REQUIRE( PairingMode::NONE == lastCompletedDevicePairingMode ); + REQUIRE( BTSecurityLevel::NONE == lastCompletedDeviceSecurityLevel ); + } + } + + // + // Validating EIR + // + { + const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + fprintf_td(stderr, "lastCompletedDevice.connectedEIR: %s\n", lastCompletedDeviceEIR.toString().c_str()); + REQUIRE( EIRDataType::NONE != lastCompletedDeviceEIR.getEIRDataMask() ); + REQUIRE( true == lastCompletedDeviceEIR.isSet(EIRDataType::FLAGS) ); + REQUIRE( true == lastCompletedDeviceEIR.isSet(EIRDataType::SERVICE_UUID) ); + REQUIRE( true == lastCompletedDeviceEIR.isSet(EIRDataType::NAME) ); + REQUIRE( true == lastCompletedDeviceEIR.isSet(EIRDataType::CONN_IVAL) ); + REQUIRE( serverName == lastCompletedDeviceEIR.getName() ); + { + const EInfoReport eir = *lastCompletedDevice->getEIR(); + fprintf_td(stderr, "lastCompletedDevice.currentEIR: %s\n", eir.toString().c_str()); + REQUIRE( EIRDataType::NONE == eir.getEIRDataMask() ); + REQUIRE( 0 == eir.getName().length()); + } + } + + // + // Now reuse adapter for client mode -> Start discovery + Stop Discovery + // + { + const BTAdapterRef adapter = server->getAdapter(); + { // if( false ) { + adapter->removeAllStatusListener(); + } + + DBTEndpoint::startDiscovery(adapter, false /* current_exp_discovering_state */); + + DBTEndpoint::stopDiscovery(adapter, true /* current_exp_discovering_state */); + } + + const int count = manager.removeChangedAdapterSetCallback(myChangedAdapterSetFunc); + fprintf_td(stderr, "****** EOL Removed ChangedAdapterSetCallback %d\n", count); + } +}; diff --git a/trial/direct_bt/dbt_client_test.hpp b/trial/direct_bt/dbt_client_test.hpp index aacaebfd..106538b4 100644 --- a/trial/direct_bt/dbt_client_test.hpp +++ b/trial/direct_bt/dbt_client_test.hpp @@ -57,7 +57,7 @@ class DBTClientTest : public DBTEndpoint { REQUIRE( current_exp_discovering_state == adapter->isDiscovering() ); REQUIRE( BTRole::Master == adapter->getRole() ); - REQUIRE( HCIStatusCode::SUCCESS == client->stopDiscovery(client.getAdapter(), msg) ); + REQUIRE( HCIStatusCode::SUCCESS == client->stopDiscovery(client->getAdapter(), msg) ); while( adapter->isDiscovering() ) { // pending action std::this_thread::sleep_for(std::chrono::milliseconds(100)); } diff --git a/trial/direct_bt/dbt_constants.hpp b/trial/direct_bt/dbt_constants.hpp index 4cd53b41..bcc84f56 100644 --- a/trial/direct_bt/dbt_constants.hpp +++ b/trial/direct_bt/dbt_constants.hpp @@ -22,36 +22,51 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#ifndef DBT_CONSTANTS_HPP +#define DBT_CONSTANTS_HPP + #include <cinttypes> #include <cstring> #include <memory> -#include <jau/uuid.hpp> +#include <direct_bt/DirectBT.hpp> class DBTConstants { - /** - * C++20 we could use `constexpr std::string` - * - * C++17 we have to use `const char *`, `std::string_view` or `extern const std::string`. - */ - inline constexpr const char CLIENT_KEY_PATH[] = "client_keys"; - - inline constexpr const char SERVER_KEY_PATH[] = "server_keys"; - - static const jau::uuid128_t DataServiceUUID = jau::uuid128_t("d0ca6bf3-3d50-4760-98e5-fc5883e93712"); - static const jau::uuid128_t StaticDataUUID = jau::uuid128_t("d0ca6bf3-3d51-4760-98e5-fc5883e93712"); - static const jau::uuid128_t CommandUUID = jau::uuid128_t("d0ca6bf3-3d52-4760-98e5-fc5883e93712"); - static const jau::uuid128_t ResponseUUID = jau::uuid128_t("d0ca6bf3-3d53-4760-98e5-fc5883e93712"); - static const jau::uuid128_t PulseDataUUID = jau::uuid128_t("d0ca6bf3-3d54-4760-98e5-fc5883e93712"); - - - /** - * Success handshake command data, where client is signaling successful completion of test to server. - */ - inline constexpr const std::vector<uint8_t> SuccessHandshakeCommandData = { 0xaa, 0xff, 0xff, 0xee }; - - /** - * Fail handshake command data, where client is signaling unsuccessful completion of test to server. - */ - inline constexpr const std::vector<uint8_t> FailHandshakeCommandData = { 0x00, 0xea, 0xea, 0xff }; + public: + /** + * C++20 we could use `constexpr std::string` + * + * C++17 we have to use `const char *`, `std::string_view` or `extern const std::string`. + */ + static constexpr const char CLIENT_KEY_PATH[] = "client_keys"; + + static constexpr const char SERVER_KEY_PATH[] = "server_keys"; + + static const jau::uuid128_t DataServiceUUID; + static const jau::uuid128_t StaticDataUUID; + static const jau::uuid128_t CommandUUID; + static const jau::uuid128_t ResponseUUID; + static const jau::uuid128_t PulseDataUUID; + + + /** + * Success handshake command data, where client is signaling successful completion of test to server. + */ + static const std::vector<uint8_t> SuccessHandshakeCommandData; + + /** + * Fail handshake command data, where client is signaling unsuccessful completion of test to server. + */ + static const std::vector<uint8_t> FailHandshakeCommandData; }; + +const jau::uuid128_t DBTConstants::DataServiceUUID = jau::uuid128_t("d0ca6bf3-3d50-4760-98e5-fc5883e93712"); +const jau::uuid128_t DBTConstants::StaticDataUUID = jau::uuid128_t("d0ca6bf3-3d51-4760-98e5-fc5883e93712"); +const jau::uuid128_t DBTConstants::CommandUUID = jau::uuid128_t("d0ca6bf3-3d52-4760-98e5-fc5883e93712"); +const jau::uuid128_t DBTConstants::ResponseUUID = jau::uuid128_t("d0ca6bf3-3d53-4760-98e5-fc5883e93712"); +const jau::uuid128_t DBTConstants::PulseDataUUID = jau::uuid128_t("d0ca6bf3-3d54-4760-98e5-fc5883e93712"); + +const std::vector<uint8_t> DBTConstants::SuccessHandshakeCommandData = { 0xaa, 0xff, 0xff, 0xee }; +const std::vector<uint8_t> DBTConstants::FailHandshakeCommandData = { 0x00, 0xea, 0xea, 0xff }; + +#endif /* DBT_CONSTANTS_HPP */ diff --git a/trial/direct_bt/dbt_endpoint.hpp b/trial/direct_bt/dbt_endpoint.hpp index 2ed40448..df5ee714 100644 --- a/trial/direct_bt/dbt_endpoint.hpp +++ b/trial/direct_bt/dbt_endpoint.hpp @@ -31,6 +31,7 @@ #include <cstring> #include <catch2/catch_amalgamated.hpp> +#include <jau/test/catch2_ext.hpp> #include <direct_bt/DirectBT.hpp> @@ -81,10 +82,10 @@ class DBTEndpoint { static void checkInitializedState(const DBTEndpointRef endp) { BTAdapterRef adapter = endp->getAdapter(); - REQUIRE( true == adapter.isInitialized() ); - REQUIRE( true == adapter.isPowered() ); - REQUIRE( BTRole::Master == adapter.getRole() ); - REQUIRE( 4 <= adapter.getBTMajorVersion() ); + REQUIRE( true == adapter->isInitialized() ); + REQUIRE( true == adapter->isPowered() ); + REQUIRE( BTRole::Master == adapter->getRole() ); + REQUIRE( 4 <= adapter->getBTMajorVersion() ); } static std::mutex mtx_cas_endpts; @@ -101,7 +102,7 @@ class DBTEndpoint { } } } - jau::fprintf_td(stderr, "****** Adapter ADDED__: Ignored: %s\n" + adapter->toString().c_str()); + jau::fprintf_td(stderr, "****** Adapter ADDED__: Ignored: %s\n", adapter->toString().c_str()); } else { for(DBTEndpointRef endpt : cas_endpts ) { if( nullptr != endpt->getAdapter() && *adapter == *endpt->getAdapter() ) { @@ -110,18 +111,18 @@ class DBTEndpoint { return true; } } - jau::fprintf_td(stderr, "****** Adapter REMOVED: Ignored: %s\n" + adapter->toString().c_str()); + jau::fprintf_td(stderr, "****** Adapter REMOVED: Ignored: %s\n", adapter->toString().c_str()); } - int count = BTManager::get().removeChangedAdapterSetCallback(myChangedAdapterSetFunc); + BTManager::get().removeChangedAdapterSetCallback(myChangedAdapterSetFunc); return true; } - static ChangedAdapterSetCallback initChangedAdapterSetListener(BTManager& manager, std::vector<DBTEndpointRef>& endpts) { + static ChangedAdapterSetCallback initChangedAdapterSetListener(BTManager& manager, std::vector<DBTEndpointRef> endpts) { const std::lock_guard<std::mutex> lock(mtx_cas_endpts); // RAII-style acquire and relinquish via destructor - cas_endpts = endpts; - ChangedAdapterSetCallback casc = jau::bindPlainFunc(this, &DBTEndpoint::myChangedAdapterSetFunc); - manager.addChangedAdapterSetListener(casc); - for(DBTEndpointRef endpt : endpts ) { + cas_endpts = std::move( endpts ); + ChangedAdapterSetCallback casc = jau::bindPlainFunc(&DBTEndpoint::myChangedAdapterSetFunc); + manager.addChangedAdapterSetCallback(casc); + for(DBTEndpointRef endpt : cas_endpts ) { REQUIRE( nullptr != endpt->getAdapter() ); } return casc; @@ -155,4 +156,7 @@ class DBTEndpoint { } }; +std::mutex DBTEndpoint::mtx_cas_endpts; +std::vector<DBTEndpointRef> DBTEndpoint::cas_endpts; + #endif /* DBT_ENDPOINT_HPP_ */ diff --git a/trial/direct_bt/dbt_server00.hpp b/trial/direct_bt/dbt_server00.hpp index 21ddcfa5..5bcce73c 100644 --- a/trial/direct_bt/dbt_server00.hpp +++ b/trial/direct_bt/dbt_server00.hpp @@ -60,8 +60,8 @@ class DBTServer00 : public DBTServerTest { return p; } - const bool GATT_VERBOSE = false; - bool SHOW_UPDATE_EVENTS = false; + static const bool GATT_VERBOSE = false; + static const bool SHOW_UPDATE_EVENTS = false; const std::string adapterShortName = "TDev1Srv"; std::string adapterName = "TestDev1_Srv"; @@ -71,7 +71,9 @@ class DBTServer00 : public DBTServerTest { BTSecurityLevel adapterSecurityLevel = BTSecurityLevel::UNSET; // DBGattServerRef dbGattServer = std::make_shared<DBGattServer>( - DBGattServerRef dbGattServer = new DBGattServer( + // DBGattServerRef dbGattServer = new DBGattServer( + // DBGattServerRef dbGattServer( new DBGattServer( + DBGattServerRef dbGattServer = std::make_shared<DBGattServer>( /* services: */ jau::make_darray( // DBGattService std::make_shared<DBGattService> ( true /* primary */, @@ -150,9 +152,12 @@ class DBTServer00 : public DBTServerTest { std::mutex mtx_sync; BTDeviceRef connectedDevice; + public: jau::sc_atomic_int servingConnectionsLeft = 1; jau::sc_atomic_int servedConnections = 0; + private: + void setDevice(BTDeviceRef cd) { const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor connectedDevice = cd; @@ -169,6 +174,10 @@ class DBTServer00 : public DBTServerTest { } class MyAdapterStatusListener : public AdapterStatusListener { + public: + DBTServer00& parent; + + MyAdapterStatusListener(DBTServer00& p) : parent(p) {} void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, const AdapterSetting changedmask, const uint64_t timestamp) override { @@ -207,9 +216,9 @@ class DBTServer00 : public DBTServerTest { void deviceConnected(BTDeviceRef device, const uint16_t handle, const uint64_t timestamp) override { fprintf_td(stderr, "****** Server CONNECTED: %s\n", device->toString(true).c_str()); - const bool available = nullptr == getDevice(); + const bool available = nullptr == parent.getDevice(); if( available ) { - setDevice(device); + parent.setDevice(device); BTDeviceRegistry::addToProcessingDevices(device->getAddressAndType(), device->getName()); } (void)handle; @@ -279,16 +288,16 @@ class DBTServer00 : public DBTServerTest { } void deviceDisconnected(BTDeviceRef device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override { - servedConnections = servedConnections + 1; + parent.servedConnections++; fprintf_td(stderr, "****** Server DISCONNECTED (count %zu): Reason 0x%X (%s), old handle %s: %s\n", - servedConnections.load(), static_cast<uint8_t>(reason), to_string(reason).c_str(), + parent.servedConnections.load(), static_cast<uint8_t>(reason), to_string(reason).c_str(), to_hexstring(handle).c_str(), device->toString(true).c_str()); - const bool match = matches(device); + const bool match = parent.matches(device); if( match ) { - setDevice(nullptr); + parent.setDevice(nullptr); } - std::thread sd(DBTServer00::processDisconnectedDevice, this, device); // @suppress("Invalid arguments") + std::thread sd(&DBTServer00::processDisconnectedDevice, &parent, device); // @suppress("Invalid arguments") sd.detach(); (void)timestamp; } @@ -301,6 +310,7 @@ class DBTServer00 : public DBTServerTest { class MyGATTServerListener : public DBGattServer::Listener { private: + DBTServer00& parent; std::thread pulseSenderThread; jau::sc_atomic_bool stopPulseSenderFlag = false; @@ -311,31 +321,19 @@ class DBTServer00 : public DBTServerTest { uint16_t usedMTU = BTGattHandler::number(BTGattHandler::Defaults::MIN_ATT_MTU); - void clear() { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor - - handlePulseDataNotify = 0; - handlePulseDataIndicate = 0; - handleResponseDataNotify = 0; - handleResponseDataIndicate = 0; - - dbGattServer->resetGattClientCharConfig(DBTConstants::DataServiceUUID, DBTConstants::PulseDataUUID); - dbGattServer->resetGattClientCharConfig(DBTConstants::DataServiceUUID, DBTConstants::ResponseUUID); - } - bool shallStopPulseSender() { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor return stopPulseSenderFlag; } void pulseSender() { { - const BTDeviceRef connectedDevice_ = getDevice(); + const BTDeviceRef connectedDevice_ = parent.getDevice(); const std::string connectedDeviceStr = nullptr != connectedDevice_ ? connectedDevice_->toString() : "n/a"; fprintf_td(stderr, "****** Server GATT::PULSE Start %s\n", connectedDeviceStr); } while( !shallStopPulseSender() ) { - BTDeviceRef connectedDevice_ = getDevice(); + BTDeviceRef connectedDevice_ = parent.getDevice(); if( nullptr != connectedDevice_ && connectedDevice_->getConnected() ) { if( 0 != handlePulseDataNotify || 0 != handlePulseDataIndicate ) { std::string data( "Dynamic Data Example. Elapsed Milliseconds: "+jau::to_decstring(environment::getElapsedMillisecond(), ',', 9) ); @@ -356,14 +354,14 @@ class DBTServer00 : public DBTServerTest { } } { - const BTDeviceRef connectedDevice_ = getDevice(); + const BTDeviceRef connectedDevice_ = parent.getDevice(); const std::string connectedDeviceStr = nullptr != connectedDevice_ ? connectedDevice_->toString() : "n/a"; fprintf_td(stderr, "****** Server GATT::PULSE End %s\n", connectedDeviceStr); } } void sendResponse(jau::POctets data) { - BTDeviceRef connectedDevice_ = getDevice(); + BTDeviceRef connectedDevice_ = parent.getDevice(); if( nullptr != connectedDevice_ && connectedDevice_->getConnected() ) { if( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) { if( 0 != handleResponseDataNotify ) { @@ -381,15 +379,28 @@ class DBTServer00 : public DBTServerTest { } public: - MyGATTServerListener() - : pulseSenderThread(&MyGATTServerListener::pulseSender, this) + + MyGATTServerListener(DBTServer00& p) + : parent(p), pulseSenderThread(&MyGATTServerListener::pulseSender, this) { } ~MyGATTServerListener() noexcept { close(); } + void clear() { + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor + + handlePulseDataNotify = 0; + handlePulseDataIndicate = 0; + handleResponseDataNotify = 0; + handleResponseDataIndicate = 0; + + parent.dbGattServer->resetGattClientCharConfig(DBTConstants::DataServiceUUID, DBTConstants::PulseDataUUID); + parent.dbGattServer->resetGattClientCharConfig(DBTConstants::DataServiceUUID, DBTConstants::ResponseUUID); + } + void close() noexcept { { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor clear(); stopPulseSenderFlag = true; } @@ -399,17 +410,17 @@ class DBTServer00 : public DBTServerTest { } void connected(BTDeviceRef device, const uint16_t initialMTU) override { - const bool match = matches(device); + const bool match = parent.matches(device); fprintf_td(stderr, "****** Server GATT::connected(match %d): initMTU %d, %s\n", match, (int)initialMTU, device->toString().c_str()); if( match ) { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor usedMTU = initialMTU; } } void disconnected(BTDeviceRef device) override { - const bool match = matches(device); + const bool match = parent.matches(device); fprintf_td(stderr, "****** Server GATT::disconnected(match %d): %s\n", match, device->toString().c_str()); if( match ) { clear(); @@ -417,10 +428,10 @@ class DBTServer00 : public DBTServerTest { } void mtuChanged(BTDeviceRef device, const uint16_t mtu) override { - const bool match = matches(device); + const bool match = parent.matches(device); const uint16_t usedMTU_old = usedMTU; if( match ) { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor usedMTU = mtu; } fprintf_td(stderr, "****** Server GATT::mtuChanged(match %d): %d -> %d, %s\n", @@ -428,7 +439,7 @@ class DBTServer00 : public DBTServerTest { } bool readCharValue(BTDeviceRef device, DBGattServiceRef s, DBGattCharRef c) override { - const bool match = matches(device); + const bool match = parent.matches(device); if( GATT_VERBOSE ) { fprintf_td(stderr, "****** Server GATT::readCharValue(match %d): to %s, from\n %s\n %s\n", match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str()); @@ -437,7 +448,7 @@ class DBTServer00 : public DBTServerTest { } bool readDescValue(BTDeviceRef device, DBGattServiceRef s, DBGattCharRef c, DBGattDescRef d) override { - const bool match = matches(device); + const bool match = parent.matches(device); if( GATT_VERBOSE ) { fprintf_td(stderr, "****** Server GATT::readDescValue(match %d): to %s, from\n %s\n %s\n %s\n", match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str()); @@ -446,7 +457,7 @@ class DBTServer00 : public DBTServerTest { } bool writeCharValue(BTDeviceRef device, DBGattServiceRef s, DBGattCharRef c, const jau::TROOctets & value, const uint16_t value_offset) override { - const bool match = matches(device); + const bool match = parent.matches(device); if( GATT_VERBOSE ) { fprintf_td(stderr, "****** Server GATT::writeCharValue(match %d): %s '%s' @ %u from %s, to\n %s\n %s\n", match, value.toString().c_str(), jau::dfa_utf8_decode( value.get_ptr(), value.size() ).c_str(), @@ -456,7 +467,7 @@ class DBTServer00 : public DBTServerTest { return match; } void writeCharValueDone(BTDeviceRef device, DBGattServiceRef s, DBGattCharRef c) override { - const bool match = matches(device); + const bool match = parent.matches(device); const jau::TROOctets& value = c->getValue(); bool isFinalHandshake = false; @@ -465,14 +476,14 @@ class DBTServer00 : public DBTServerTest { ( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) ) { isFinalHandshake = ( DBTConstants::SuccessHandshakeCommandData.size() == value.size() && - 0 == ::memcmp(DBTConstants::SuccessHandshakeCommandData.data(), value.data(), value.size()) ) || + 0 == ::memcmp(DBTConstants::SuccessHandshakeCommandData.data(), value.get_ptr(), value.size()) ) || ( DBTConstants::FailHandshakeCommandData.size() == value.size() && - 0 == ::memcmp(DBTConstants::FailHandshakeCommandData.data(), value.data(), value.size()) ); + 0 == ::memcmp(DBTConstants::FailHandshakeCommandData.data(), value.get_ptr(), value.size()) ); if( isFinalHandshake ) { - servedConnections++; // we assume this to be server, i.e. connected, SMP done and GATT action .. - if( servingConnectionsLeft > 0 ) { - servingConnectionsLeft--; // ditto + parent.servedConnections++; // we assume this to be server, i.e. connected, SMP done and GATT action .. + if( parent.servingConnectionsLeft > 0 ) { + parent.servingConnectionsLeft--; // ditto } } jau::POctets value2(value); @@ -481,13 +492,13 @@ class DBTServer00 : public DBTServerTest { } if( GATT_VERBOSE || isFinalHandshake ) { fprintf_td(stderr, "****** Server GATT::writeCharValueDone(match %d, finalCmd %d, served %d, left %d): From %s, to\n %s\n %s\n Char-Value: %s\n", - match, isFinalHandshake, servedConnections.load(), servingConnectionsLeft.load(), + match, isFinalHandshake, parent.servedConnections.load(), parent.servingConnectionsLeft.load(), device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), value.toString().c_str()); } } bool writeDescValue(BTDeviceRef device, DBGattServiceRef s, DBGattCharRef c, DBGattDescRef d, const jau::TROOctets & value, const uint16_t value_offset) override { - const bool match = matches(device); + const bool match = parent.matches(device); if( GATT_VERBOSE ) { fprintf_td(stderr, "****** Server GATT::writeDescValue(match %d): %s '%s' @ %u from %s\n %s\n %s\n %s\n", match, value.toString().c_str(), jau::dfa_utf8_decode( value.get_ptr(), value.size() ).c_str(), @@ -498,7 +509,7 @@ class DBTServer00 : public DBTServerTest { } void writeDescValueDone(BTDeviceRef device, DBGattServiceRef s, DBGattCharRef c, DBGattDescRef d) override { if( GATT_VERBOSE ) { - const bool match = matches(device); + const bool match = parent.matches(device); const jau::TROOctets& value = d->getValue(); fprintf_td(stderr, "****** Server GATT::writeDescValueDone(match %d): From %s\n %s\n %s\n %s\n Desc-Value: %s\n", match, device->toString().c_str(), s->toString().c_str(), c->toString().c_str(), d->toString().c_str(), value.toString().c_str()); @@ -506,7 +517,7 @@ class DBTServer00 : public DBTServerTest { } void clientCharConfigChanged(BTDeviceRef device, DBGattServiceRef s, DBGattCharRef c, DBGattDescRef d, const bool notificationEnabled, const bool indicationEnabled) override { - const bool match = matches(device); + const bool match = parent.matches(device); if( GATT_VERBOSE ) { const jau::TROOctets& value = d->getValue(); fprintf_td(stderr, "****** GATT::clientCharConfigChanged(match %d): notify %d, indicate %d from %s\n %s\n %s\n %s\n Desc-Value: %s\n", @@ -515,11 +526,11 @@ class DBTServer00 : public DBTServerTest { } if( match ) { if( c->getValueType()->equivalent( DBTConstants::PulseDataUUID ) ) { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor handlePulseDataNotify = notificationEnabled ? c->getValueHandle() : 0; handlePulseDataIndicate = indicationEnabled ? c->getValueHandle() : 0; } else if( c->getValueType()->equivalent( DBTConstants::ResponseUUID ) ) { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor handleResponseDataNotify = notificationEnabled ? c->getValueHandle() : 0; handleResponseDataIndicate = indicationEnabled ? c->getValueHandle() : 0; } @@ -527,36 +538,36 @@ class DBTServer00 : public DBTServerTest { } }; - std::shared_ptr<MyGATTServerListener> gattServerListener = std::make_shared<MyGATTServerListener>(); - std::shared_ptr<AdapterStatusListener> myAdapterStatusListener = std::make_shared<MyAdapterStatusListener>(); + std::shared_ptr<MyGATTServerListener> gattServerListener = std::make_shared<MyGATTServerListener>(*this); + std::shared_ptr<AdapterStatusListener> myAdapterStatusListener = std::make_shared<MyAdapterStatusListener>(*this); BTAdapterRef serverAdapter = nullptr; public: - DBTServer00(const std::string& adapterName, const jau::EUI48& useAdapter, const BTMode btMode, const bool use_SC, const BTSecurityLevel adapterSecurityLevel) { - this->adapterName = adapterName; - this->useAdapter = useAdapter; - this->btMode = btMode; - this->use_SC = use_SC; - this->adapterSecurityLevel = adapterSecurityLevel; + DBTServer00(const std::string& adapterName_, const jau::EUI48& useAdapter_, const BTMode btMode_, const bool use_SC_, const BTSecurityLevel adapterSecurityLevel_) { + this->adapterName = adapterName_; + this->useAdapter = useAdapter_; + this->btMode = btMode_; + this->use_SC = use_SC_; + this->adapterSecurityLevel = adapterSecurityLevel_; - dbGattServer.addListener( gattServerListener ); + dbGattServer->addListener( gattServerListener ); } - DBTServer00(const std::string& adapterName, const jau::EUI48& useAdapter, const BTSecurityLevel adapterSecurityLevel) - : DBTServer00(adapterName, useAdapter, BTMode::DUAL, true /* SC */, adapterSecurityLevel) + DBTServer00(const std::string& adapterName_, const jau::EUI48& useAdapter_, const BTSecurityLevel adapterSecurityLevel_) + : DBTServer00(adapterName_, useAdapter_, BTMode::DUAL, true /* SC */, adapterSecurityLevel_) { } - DBTServer00(const std::string& adapterName, const BTSecurityLevel adapterSecurityLevel) - : DBTServer00(adapterName, EUI48::ALL_DEVICE, BTMode::DUAL, true /* SC */, adapterSecurityLevel) + DBTServer00(const std::string& adapterName_, const BTSecurityLevel adapterSecurityLevel_) + : DBTServer00(adapterName_, EUI48::ALL_DEVICE, BTMode::DUAL, true /* SC */, adapterSecurityLevel_) { } std::string getName() override { return adapterName; } BTSecurityLevel getSecurityLevel() override { return adapterSecurityLevel; } - void setAdapter(BTAdapterRef serverAdapter) override { - this->serverAdapter = serverAdapter; + void setAdapter(BTAdapterRef serverAdapter_) override { + this->serverAdapter = serverAdapter_; } BTAdapterRef getAdapter() override { return serverAdapter; } @@ -587,29 +598,21 @@ class DBTServer00 : public DBTServerTest { fprintf_td(stderr, "****** Server Close.0: %s\n", msg.c_str()); stop(msg); gattServerListener->close(); - dbGattServer.close(); - serverAdapter.removeStatusListener( myAdapterStatusListener ); + // dbGattServer = nullptr; // keep alive + serverAdapter->removeStatusListener( myAdapterStatusListener ); fprintf_td(stderr, "****** Server Close.X: %s\n", msg.c_str()); } private: - HCIStatusCode stopAdvertising(BTAdapter *a, std::string msg) { - if( useAdapter != EUI48::ALL_DEVICE && useAdapter != a->getAddressAndType().address ) { - fprintf_td(stderr, "****** Server Stop advertising (%s): Adapter not selected: %s\n", msg.c_str(), a->toString().c_str()); - return false; - } - HCIStatusCode status = a->stopAdvertising(); - fprintf_td(stderr, "****** Server Stop advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str()); + HCIStatusCode stopAdvertising(std::string msg) { + HCIStatusCode status = serverAdapter->stopAdvertising(); + fprintf_td(stderr, "****** Server Stop advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), serverAdapter->toString().c_str()); return status; } public: - HCIStatusCode startAdvertising(BTAdapter *a, std::string msg) override { - if( useAdapter != EUI48::ALL_DEVICE && useAdapter != a->getAddressAndType().address ) { - fprintf_td(stderr, "****** Server Start advertising (%s): Adapter not selected: %s\n", msg.c_str(), a->toString().c_str()); - return false; - } + HCIStatusCode startAdvertising(const std::string& msg) override { EInfoReport eir; EIRDataType adv_mask = EIRDataType::FLAGS | EIRDataType::SERVICE_UUID; EIRDataType scanrsp_mask = EIRDataType::NAME | EIRDataType::CONN_IVAL; @@ -620,23 +623,23 @@ class DBTServer00 : public DBTServerTest { eir.addService(DBTConstants::DataServiceUUID); eir.setServicesComplete(false); - eir.setName(a->getName()); + eir.setName(serverAdapter->getName()); eir.setConnInterval(8, 12); // 10ms - 15ms DBGattCharRef gattDevNameChar = dbGattServer->findGattChar( jau::uuid16_t(GattServiceType::GENERIC_ACCESS), jau::uuid16_t(GattCharacteristicType::DEVICE_NAME) ); if( nullptr != gattDevNameChar ) { - std::string aname = a->getName(); + std::string aname = serverAdapter->getName(); gattDevNameChar->setValue(reinterpret_cast<uint8_t*>(aname.data()), aname.size(), 0); } fprintf_td(stderr, "****** Start advertising (%s): EIR %s\n", msg.c_str(), eir.toString().c_str()); fprintf_td(stderr, "****** Start advertising (%s): adv %s, scanrsp %s\n", msg.c_str(), to_string(adv_mask).c_str(), to_string(scanrsp_mask).c_str()); - HCIStatusCode status = a->startAdvertising(dbGattServer, eir, adv_mask, scanrsp_mask, + HCIStatusCode status = serverAdapter->startAdvertising(dbGattServer, eir, adv_mask, scanrsp_mask, adv_interval_min, adv_interval_max, adv_type, adv_chan_map, filter_policy); - fprintf_td(stderr, "****** Server Start advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), a->toString().c_str()); + fprintf_td(stderr, "****** Server Start advertising (%s) result: %s: %s\n", msg.c_str(), to_string(status).c_str(), serverAdapter->toString().c_str()); if( GATT_VERBOSE ) { fprintf_td(stderr, "%s", dbGattServer->toFullString().c_str()); } @@ -649,14 +652,14 @@ class DBTServer00 : public DBTServerTest { servedConnections.load(), device->toString().c_str()); // already unpaired - stopAdvertising(&device->getAdapter(), "device-disconnected"); + stopAdvertising("device-disconnected"); device->remove(); BTDeviceRegistry::removeFromProcessingDevices(device->getAddressAndType()); std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait a little (FIXME: Fast restart of advertising error) if( servingConnectionsLeft > 0 ) { - startAdvertising(&device->getAdapter(), "device-disconnected"); + startAdvertising("device-disconnected"); } fprintf_td(stderr, "****** Server Disonnected Device: End %s\n", device->toString().c_str()); diff --git a/trial/direct_bt/dbt_server_test.hpp b/trial/direct_bt/dbt_server_test.hpp index eb6b3d8d..f3acff40 100644 --- a/trial/direct_bt/dbt_server_test.hpp +++ b/trial/direct_bt/dbt_server_test.hpp @@ -42,7 +42,7 @@ class DBTServerTest : public DBTEndpoint { static void startAdvertising(DBTServerTestRef server, const bool current_exp_advertising_state, const std::string& msg) { BTAdapterRef adapter = server->getAdapter(); REQUIRE( current_exp_advertising_state == adapter->isAdvertising() ); - REQUIRE( false == adapter.isDiscovering() ); + REQUIRE( false == adapter->isDiscovering() ); REQUIRE( HCIStatusCode::SUCCESS == server->startAdvertising(msg) ); REQUIRE( true == adapter->isAdvertising() ); @@ -58,7 +58,7 @@ class DBTServerTest : public DBTEndpoint { REQUIRE( BTRole::Slave == adapter->getRole() ); // kept // Stopping advertising and serving even if stopped must be OK! - REQUIRE( HCIStatusCode::SUCCESS == server.stop(msg) ); + REQUIRE( HCIStatusCode::SUCCESS == server->stop(msg) ); REQUIRE( false == adapter->isAdvertising() ); REQUIRE( false == adapter->isDiscovering() ); REQUIRE( BTRole::Slave == adapter->getRole() ); // kept diff --git a/trial/direct_bt/dbt_utils.hpp b/trial/direct_bt/dbt_utils.hpp index b7dcc4c8..2a9a132a 100644 --- a/trial/direct_bt/dbt_utils.hpp +++ b/trial/direct_bt/dbt_utils.hpp @@ -31,13 +31,12 @@ #include <cstdint> #include <cstdio> +#include "dbt_constants.hpp" + #include <sys/stat.h> #include <sys/types.h> #include <dirent.h> -#include <jau/debug.hpp> - -#include "dbt_constants.hpp" class DBTUtils { private: @@ -146,6 +145,7 @@ class DBTUtils { * @return true only if the file or the directory with content has been deleted, otherwise false */ static bool remove(const std::string& file, const bool recursive) { + (void) recursive; if( 0 != ::remove( file.c_str() ) ) { if( ENOENT == errno ) { // not existing @@ -193,8 +193,8 @@ class DBTUtils { } static bool rmKeyFolder() { - if( remove( DBTConstants::CLIENT_KEY_PATH ) ) { - if( remove( DBTConstants::SERVER_KEY_PATH ) ) { + if( remove( DBTConstants::CLIENT_KEY_PATH, true ) ) { + if( remove( DBTConstants::SERVER_KEY_PATH, true ) ) { return true; } } diff --git a/trial/direct_bt/test_client_server00.cpp b/trial/direct_bt/test_client_server00.cpp index dcad320e..17454b1d 100644 --- a/trial/direct_bt/test_client_server00.cpp +++ b/trial/direct_bt/test_client_server00.cpp @@ -86,11 +86,10 @@ TEST_CASE( "BTManager Bringup Trial 00", "[trial][BTManager][bringup]" ) { base_test_framework.setupTest(); BTManager & manager = BTManager::get(); - REQUIRE( manager.getAdapterCount() >= 1 ); const std::string serverName = "TestDBTCS00-S-T10"; - std::shared_ptr<DBTServer00> server(serverName, EUI48::ALL_DEVICE, BTMode::DUAL, true /* SC */, BTSecurityLevel::NONE); + std::shared_ptr<DBTServer00> server = std::make_shared<DBTServer00>(serverName, EUI48::ALL_DEVICE, BTMode::DUAL, true /* SC */, BTSecurityLevel::NONE); server->servingConnectionsLeft = 1; ChangedAdapterSetCallback myChangedAdapterSetFunc = DBTEndpoint::initChangedAdapterSetListener(manager, { server }); @@ -121,7 +120,7 @@ TEST_CASE( "BTManager Bringup Trial 00", "[trial][BTManager][bringup]" ) { DBTEndpoint::stopDiscovery(adapter, true /* current_exp_discovering_state */); } - REQUIRE( 1 == manager.removeChangedAdapterSetListener(myChangedAdapterSetFunc) ); + REQUIRE( 1 == manager.removeChangedAdapterSetCallback(myChangedAdapterSetFunc) ); base_test_framework.cleanupTest(); } diff --git a/trial/direct_bt/test_client_server01_NoEnc.cpp b/trial/direct_bt/test_client_server01_NoEnc.cpp new file mode 100644 index 00000000..c679e746 --- /dev/null +++ b/trial/direct_bt/test_client_server01_NoEnc.cpp @@ -0,0 +1,75 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#define CATCH_CONFIG_RUNNER +// #define CATCH_CONFIG_MAIN +#include <catch2/catch_amalgamated.hpp> +#include <jau/test/catch2_ext.hpp> + +#include "dbt_client_server1x.hpp" + +using namespace direct_bt; + +// Singleton test framework, alive until test program ends +static BaseDBTClientServer& base_test_framework = BaseDBTClientServer::get(); + +/** + * Testing a full Bluetooth server and client lifecycle of operations, requiring two BT adapter: + * - operating w/o encryption + * - start server advertising + * - start client discovery and connect to server when discovered + * - client/server processing of connection when ready + * - client disconnect + * - server stop advertising + * - security-level: NONE, ENC_ONLY freshly-paired and ENC_ONLY pre-paired + * - reuse server-adapter for client-mode discovery (just toggle on/off) + */ +class TestDBTClientServer01_NoEnc : public DBTClientServer1x { + private: + static constexpr const bool serverSC = true; + + public: + void test00_FullCycle_EncNone() { + base_test_framework.setupTest( 40_s ); + { + const bool serverShallHaveKeys = false; + const bool clientShallHaveKeys = false; + test8x_fullCycle("00", true /* server_client_order */, serverSC, + BTSecurityLevel::NONE, serverShallHaveKeys, BTSecurityLevel::NONE, clientShallHaveKeys); + } + base_test_framework.cleanupTest(); + } + + void test01_FullCycle_EncNone() { + base_test_framework.setupTest( 40_s ); + { + const bool serverShallHaveKeys = false; + const bool clientShallHaveKeys = false; + test8x_fullCycle("01", true /* server_client_order */, serverSC, + BTSecurityLevel::NONE, serverShallHaveKeys, BTSecurityLevel::NONE, clientShallHaveKeys); + } + base_test_framework.cleanupTest(); + } + +}; diff --git a/trial/java/trial/org/direct_bt/DBTClient00.java b/trial/java/trial/org/direct_bt/DBTClient00.java index 48597722..1714b131 100644 --- a/trial/java/trial/org/direct_bt/DBTClient00.java +++ b/trial/java/trial/org/direct_bt/DBTClient00.java @@ -583,7 +583,7 @@ public class DBTClient00 implements DBTClientTest { } DiscoveryPolicy discoveryPolicy = DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_READY; // default value - boolean le_scan_active = true; // default value + static final boolean le_scan_active = true; // default value static final short le_scan_interval = (short)24; // 15ms, default value static final short le_scan_window = (short)24; // 15ms, default value static final byte filter_policy = (byte)0; // default value diff --git a/trial/java/trial/org/direct_bt/DBTClientServer1x.java b/trial/java/trial/org/direct_bt/DBTClientServer1x.java index 2a36395d..fe571825 100644 --- a/trial/java/trial/org/direct_bt/DBTClientServer1x.java +++ b/trial/java/trial/org/direct_bt/DBTClientServer1x.java @@ -79,10 +79,11 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { if( null == manager ) { return; } - - final List<BTAdapter> adapters = manager.getAdapters(); - BTUtils.println(System.err, "Adapter: Count "+adapters.size()+": "+adapters.toString()); - Assert.assertTrue("Adapter count not >= 2 but "+adapters.size(), adapters.size() >= 2); + { + final List<BTAdapter> adapters = manager.getAdapters(); + BTUtils.println(System.err, "Adapter: Count "+adapters.size()+": "+adapters.toString()); + Assert.assertTrue("Adapter count not >= 2 but "+adapters.size(), adapters.size() >= 2); + } final DBTServer00 server = new DBTServer00("S-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL, serverSC, secLevelServer); server.servingConnectionsLeft.set(1); @@ -113,7 +114,7 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { lastCompletedDevice = device; lastCompletedDevicePairingMode = device.getPairingMode(); lastCompletedDeviceSecurityLevel = device.getConnSecurityLevel(); - lastCompletedDeviceEIR = device.getEIR(); + lastCompletedDeviceEIR = device.getEIR().clone(); BTUtils.println(System.err, "XXXXXX Client Ready: "+device); } }; @@ -191,10 +192,10 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { Assert.assertTrue( lastCompletedDeviceEIR.isSet(EIRDataTypeSet.DataType.CONN_IVAL) ); Assert.assertEquals(serverName, lastCompletedDeviceEIR.getName()); { - final EInfoReport eir = lastCompletedDevice.getEIR(); + final EInfoReport eir = lastCompletedDevice.getEIR().clone(); BTUtils.println(System.err, "lastCompletedDevice.currentEIR: "+eir.toString()); Assert.assertEquals(0, eir.getEIRDataMask().mask); - Assert.assertEquals(0, lastCompletedDeviceEIR.getName().length()); + Assert.assertEquals(0, eir.getName().length()); } // diff --git a/trial/java/trial/org/direct_bt/DBTServer00.java b/trial/java/trial/org/direct_bt/DBTServer00.java index df3c14fb..e7a7bbf9 100644 --- a/trial/java/trial/org/direct_bt/DBTServer00.java +++ b/trial/java/trial/org/direct_bt/DBTServer00.java @@ -633,16 +633,12 @@ public class DBTServer00 implements DBTServerTest { BTUtils.println(System.err, "****** Server Close.0: "+msg); stop(msg); gattServerListener.close(); - dbGattServer.close(); + // dbGattServer.close(); // keep alive serverAdapter.removeStatusListener( myAdapterStatusListener ); BTUtils.println(System.err, "****** Server Close.X: "+msg); } private HCIStatusCode stopAdvertising(final String msg) { - if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(serverAdapter.getAddressAndType().address) ) { - BTUtils.fprintf_td(System.err, "****** Server Stop advertising (%s): Adapter not selected: %s\n", msg, serverAdapter.toString()); - return HCIStatusCode.FAILED; - } final HCIStatusCode status = serverAdapter.stopAdvertising(); BTUtils.println(System.err, "****** Server Stop advertising ("+msg+") result: "+status+": "+serverAdapter.toString()); return status; @@ -650,10 +646,6 @@ public class DBTServer00 implements DBTServerTest { @Override public HCIStatusCode startAdvertising(final String msg) { - if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(serverAdapter.getAddressAndType().address) ) { - BTUtils.fprintf_td(System.err, "****** Server Start advertising (%s): Adapter not selected: %s\n", msg, serverAdapter.toString()); - return HCIStatusCode.FAILED; - } final EInfoReport eir = new EInfoReport(); final EIRDataTypeSet adv_mask = new EIRDataTypeSet(); final EIRDataTypeSet scanrsp_mask = new EIRDataTypeSet(); |