diff options
author | Sven Gothel <[email protected]> | 2022-05-05 22:55:52 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-05-05 22:55:52 +0200 |
commit | b3d62d2336744481bc83377d780d9ac79f534207 (patch) | |
tree | b608ab8dc98fabf1b14ab4515a070dcc856f40aa | |
parent | 53159c09d953475e1816f1c0c5be6c3cdb76b545 (diff) |
Trial (Java + C++): DBTEndpoint, DBTClientTest, DBTServerTest: Add abstraction to plug-in any client/server instance into DBTClientServer1x
Further add TestDBTProvokeClientServer_i470 (java trial, copied from native trial)
32 files changed, 2098 insertions, 441 deletions
diff --git a/trial/direct_bt/dbt_client00.hpp b/trial/direct_bt/dbt_client00.hpp index abf56906..047a8f3b 100644 --- a/trial/direct_bt/dbt_client00.hpp +++ b/trial/direct_bt/dbt_client00.hpp @@ -30,7 +30,7 @@ #include "dbt_client_test.hpp" class DBTClient00; -typedef std::shared_ptr<DBTClient00> DBTClientRef; +typedef std::shared_ptr<DBTClient00> DBTClient00Ref; using namespace jau; using namespace jau::fractions_i64_literals; @@ -39,22 +39,9 @@ using namespace jau::fractions_i64_literals; * This central BTRole::Master participant works with DBTServer00. */ class DBTClient00 : public DBTClientTest { - public: - /** - * Disconnect after processing. - * - * Default is `false`. - */ + private: bool KEEP_CONNECTED = false; - /** - * Remove device when disconnecting. - * - * This removes the device from all instances within adapter - * and hence all potential side-effects of the current instance. - * - * Default is `false`, since it is good to test whether such side-effects exists. - */ bool REMOVE_DEVICE = false; DiscoveryPolicy discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY; // default value @@ -64,13 +51,14 @@ class DBTClient00 : public DBTClientTest { jau::sc_atomic_int deviceReadyCount = 0; + jau::sc_atomic_int disconnectCount = 0; jau::sc_atomic_int notificationsReceived = 0; jau::sc_atomic_int indicationsReceived = 0; jau::sc_atomic_int completedGATTCommands = 0; - jau::sc_atomic_int completedMeasurements = 0; + jau::sc_atomic_int completedMeasurementsTotal = 0; + jau::sc_atomic_int completedMeasurementsSuccess = 0; jau::sc_atomic_int measurementsLeft = 0; - private: const uint64_t timestamp_t0 = getCurrentMilliseconds(); // const fraction_i64 timestamp_t0 = jau::getMonotonicMicroseconds(); @@ -228,6 +216,8 @@ class DBTClient00 : public DBTClientTest { to_hexstring(handle).c_str(), device->toString(true).c_str()); (void)timestamp; + parent.disconnectCount++; + std::thread dc(&DBTClient00::removeDevice, &parent, device); // @suppress("Invalid arguments") dc.detach(); } @@ -296,6 +286,32 @@ class DBTClient00 : public DBTClientTest { BTAdapterRef getAdapter() override { return clientAdapter; } + void setProtocolSessionsLeft(const int v) override { + measurementsLeft = v; + } + int getProtocolSessionsLeft() override { + return measurementsLeft; + } + int getProtocolSessionsDoneTotal() override { + return completedMeasurementsTotal; + } + int getProtocolSessionsDoneSuccess() override { + return completedMeasurementsSuccess; + } + int getDisconnectCount() override { + return disconnectCount; + } + + void setDiscoveryPolicy(const DiscoveryPolicy v) override { + discoveryPolicy = v; + } + void setKeepConnected(const bool v) override { + KEEP_CONNECTED = v; + } + void setRemoveDevice(const bool v) override { + REMOVE_DEVICE = v; + } + private: void resetLastProcessingStats() { completedGATTCommands = 0; @@ -489,7 +505,7 @@ class DBTClient00 : public DBTClientTest { const fraction_i64 td = ( getMonotonicTime() - t0 ).to_fraction_i64(); timeout = 3_s < td; if( !timeout ) { - std::this_thread::sleep_for(std::chrono::milliseconds(17)); + jau::sleep_for( 17_ms ); } } } while( !success && !timeout ); @@ -524,9 +540,6 @@ class DBTClient00 : public DBTClientTest { } // cmd.close(); // done via dtor } - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - success = true; } catch ( std::exception & e ) { fprintf_td(stderr, "****** Client Processing Ready Device: Exception.2 caught for %s: %s\n", device->toString().c_str(), e.what()); } @@ -557,8 +570,9 @@ class DBTClient00 : public DBTClientTest { } } + completedMeasurementsTotal++; if( success ) { - completedMeasurements++; + completedMeasurementsSuccess++; } if( 0 < measurementsLeft ) { measurementsLeft--; @@ -566,7 +580,7 @@ class DBTClient00 : public DBTClientTest { fprintf_td(stderr, "****** Client Processing Ready Device: Success %d; Measurements completed %d" ", left %d; Received notitifications %d, indications %d" "; Completed GATT commands %d: %s\n", - success, completedMeasurements.load(), + success, completedMeasurementsSuccess.load(), measurementsLeft.load(), notificationsReceived.load(), indicationsReceived.load(), completedGATTCommands.load(), device->getAddressAndType().toString().c_str()); } diff --git a/trial/direct_bt/dbt_client01.hpp b/trial/direct_bt/dbt_client01.hpp index 7795bdb0..ce05e392 100644 --- a/trial/direct_bt/dbt_client01.hpp +++ b/trial/direct_bt/dbt_client01.hpp @@ -22,15 +22,15 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef DBT_CLIENT00_HPP_ -#define DBT_CLIENT00_HPP_ +#ifndef DBT_CLIENT01_HPP_ +#define DBT_CLIENT01_HPP_ #include "dbt_constants.hpp" #include "dbt_client_test.hpp" class DBTClient01; -typedef std::shared_ptr<DBTClient01> DBTClientRef; +typedef std::shared_ptr<DBTClient01> DBTClient01Ref; using namespace jau; using namespace jau::fractions_i64_literals; @@ -64,10 +64,12 @@ class DBTClient01 : public DBTClientTest { jau::sc_atomic_int deviceReadyCount = 0; + jau::sc_atomic_int disconnectCount = 0; jau::sc_atomic_int notificationsReceived = 0; jau::sc_atomic_int indicationsReceived = 0; jau::sc_atomic_int completedGATTCommands = 0; - jau::sc_atomic_int completedMeasurements = 0; + jau::sc_atomic_int completedMeasurementsTotal = 0; + jau::sc_atomic_int completedMeasurementsSuccess = 0; jau::sc_atomic_int measurementsLeft = 0; private: @@ -214,6 +216,8 @@ class DBTClient01 : public DBTClientTest { to_hexstring(handle).c_str(), device->toString(true).c_str()); (void)timestamp; + parent.disconnectCount++; + std::thread dc(&DBTClient01::removeDevice, &parent, device); // @suppress("Invalid arguments") dc.detach(); } @@ -282,6 +286,32 @@ class DBTClient01 : public DBTClientTest { BTAdapterRef getAdapter() override { return clientAdapter; } + void setProtocolSessionsLeft(const int v) override { + measurementsLeft = v; + } + int getProtocolSessionsLeft() override { + return measurementsLeft; + } + int getProtocolSessionsDoneTotal() override { + return completedMeasurementsTotal; + } + int getProtocolSessionsDoneSuccess() override { + return completedMeasurementsSuccess; + } + int getDisconnectCount() override { + return disconnectCount; + } + + void setDiscoveryPolicy(const DiscoveryPolicy v) override { + discoveryPolicy = v; + } + void setKeepConnected(const bool v) override { + KEEP_CONNECTED = v; + } + void setRemoveDevice(const bool v) override { + REMOVE_DEVICE = v; + } + private: void resetLastProcessingStats() { completedGATTCommands = 0; @@ -475,7 +505,7 @@ class DBTClient01 : public DBTClientTest { const fraction_i64 td = ( getMonotonicTime() - t0 ).to_fraction_i64(); timeout = 3_s < td; if( !timeout ) { - std::this_thread::sleep_for(std::chrono::milliseconds(17)); + jau::sleep_for( 17_ms ); } } } while( !success && !timeout ); @@ -510,9 +540,6 @@ class DBTClient01 : public DBTClientTest { } // cmd.close(); // done via dtor } - - std::this_thread::sleep_for(std::chrono::milliseconds(1000)); - success = true; } catch ( std::exception & e ) { fprintf_td(stderr, "****** Client Processing Ready Device: Exception.2 caught for %s: %s\n", device->toString().c_str(), e.what()); } @@ -538,8 +565,9 @@ class DBTClient01 : public DBTClientTest { } } + completedMeasurementsTotal++; if( success ) { - completedMeasurements++; + completedMeasurementsSuccess++; } if( 0 < measurementsLeft ) { measurementsLeft--; @@ -547,7 +575,7 @@ class DBTClient01 : public DBTClientTest { fprintf_td(stderr, "****** Client Processing Ready Device: Success %d; Measurements completed %d" ", left %d; Received notitifications %d, indications %d" "; Completed GATT commands %d: %s\n", - success, completedMeasurements.load(), + success, completedMeasurementsSuccess.load(), measurementsLeft.load(), notificationsReceived.load(), indicationsReceived.load(), completedGATTCommands.load(), device->getAddressAndType().toString().c_str()); } @@ -652,4 +680,4 @@ class DBTClient01 : public DBTClientTest { } }; -#endif /* DBT_CLIENT00_HPP_ */ +#endif /* DBT_CLIENT01_HPP_ */ diff --git a/trial/direct_bt/dbt_client_server1x.hpp b/trial/direct_bt/dbt_client_server1x.hpp index 045e9328..4ed2a0cc 100644 --- a/trial/direct_bt/dbt_client_server1x.hpp +++ b/trial/direct_bt/dbt_client_server1x.hpp @@ -54,10 +54,22 @@ class DBTClientServer1x { public: void test8x_fullCycle(const std::string& suffix, const int protocolSessionCount, const bool server_client_order, - const bool serverSC, const BTSecurityLevel secLevelServer, const bool serverShallHaveKeys, - const BTSecurityLevel secLevelClient, const bool clientShallHaveKeys) + const bool serverSC, const BTSecurityLevel secLevelServer, const ExpectedPairing serverExpPairing, + const BTSecurityLevel secLevelClient, const ExpectedPairing clientExpPairing) { - (void)serverShallHaveKeys; + std::shared_ptr<DBTServer00> server = std::make_shared<DBTServer00>("S-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL, serverSC, secLevelServer); + std::shared_ptr<DBTClient00> client = std::make_shared<DBTClient00>("C-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL); + test8x_fullCycle(suffix, protocolSessionCount, server_client_order, + server, secLevelServer, serverExpPairing, + client, secLevelClient, clientExpPairing); + } + + void test8x_fullCycle(const std::string& suffix, const int protocolSessionCount, const bool server_client_order, + std::shared_ptr<DBTServerTest> server, const BTSecurityLevel secLevelServer, const ExpectedPairing serverExpPairing, + std::shared_ptr<DBTClientTest> client, const BTSecurityLevel secLevelClient, const ExpectedPairing clientExpPairing) + { + (void)secLevelServer; + (void)serverExpPairing; const jau::fraction_timespec t0 = jau::getMonotonicTime(); @@ -72,10 +84,12 @@ class DBTClientServer1x { REQUIRE( adapters.size() >= 2 ); } - std::shared_ptr<DBTServer00> server = std::make_shared<DBTServer00>("S-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL, serverSC, secLevelServer); - server->servingProtocolSessionsLeft = protocolSessionCount; + server->setProtocolSessionsLeft(protocolSessionCount); - std::shared_ptr<DBTClient00> client = std::make_shared<DBTClient00>("C-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL); + client->setProtocolSessionsLeft( protocolSessionCount ); + client->setKeepConnected( false ); // default, auto-disconnect after work is done + client->setRemoveDevice( false ); // default, test side-effects + client->setDiscoveryPolicy( DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED ); ChangedAdapterSetCallback myChangedAdapterSetFunc = DBTEndpoint::initChangedAdapterSetListener(manager, server_client_order ? std::vector<DBTEndpointRef>{ server, client } : std::vector<DBTEndpointRef>{ client, server } ); @@ -86,10 +100,6 @@ class DBTClientServer1x { 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 = protocolSessionCount; - client->discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED; { const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor @@ -140,12 +150,12 @@ class DBTClientServer1x { do { { const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor - done = ! ( protocolSessionCount > server->servedProtocolSessionsTotal || - protocolSessionCount > client->completedMeasurements || + done = ! ( protocolSessionCount > server->getProtocolSessionsDoneSuccess() || + protocolSessionCount > client->getProtocolSessionsDoneSuccess() || nullptr == lastCompletedDevice || lastCompletedDevice->getConnected() ); } - max_connections_hit = ( protocolSessionCount * DBTConstants::max_connections_per_session ) <= server->disconnectCount; + max_connections_hit = ( protocolSessionCount * DBTConstants::max_connections_per_session ) <= server->getDisconnectCount(); test_duration = ( jau::getMonotonicTime() - t0 ).to_fraction_i64(); timeout = 0_s < timeout_value && timeout_value <= test_duration + 500_ms; // let's timeout here before our timeout timer if( !done && !max_connections_hit && !timeout ) { @@ -158,23 +168,25 @@ class DBTClientServer1x { fprintf_td(stderr, "****** Test Stats: duration %" PRIi64 " ms, timeout[hit %d, value %s sec], max_connections hit %d\n", test_duration.to_ms(), timeout, timeout_value.to_string(true).c_str(), max_connections_hit); fprintf_td(stderr, " Server ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", - server->servedProtocolSessionsSuccess.load(), server->servedProtocolSessionsTotal.load(), protocolSessionCount, - server->disconnectCount.load(), ( protocolSessionCount * DBTConstants::max_connections_per_session )); - fprintf_td(stderr, " Client ProtocolSessions success %d \n", - client->completedMeasurements.load()); + server->getProtocolSessionsDoneSuccess(), server->getProtocolSessionsDoneTotal(), protocolSessionCount, + server->getDisconnectCount(), ( protocolSessionCount * DBTConstants::max_connections_per_session )); + fprintf_td(stderr, " Client ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", + client->getProtocolSessionsDoneSuccess(), client->getProtocolSessionsDoneTotal(), protocolSessionCount, + client->getDisconnectCount(), ( protocolSessionCount * DBTConstants::max_connections_per_session )); fprintf_td(stderr, "\n\n"); REQUIRE( false == max_connections_hit ); REQUIRE( false == timeout ); { const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor - REQUIRE( protocolSessionCount == server->servedProtocolSessionsTotal ); - REQUIRE( protocolSessionCount == server->servedProtocolSessionsSuccess ); - REQUIRE( protocolSessionCount == client->completedMeasurements ); + REQUIRE( protocolSessionCount <= server->getProtocolSessionsDoneTotal() ); + REQUIRE( protocolSessionCount == server->getProtocolSessionsDoneSuccess() ); + REQUIRE( protocolSessionCount <= client->getProtocolSessionsDoneTotal() ); + REQUIRE( protocolSessionCount == client->getProtocolSessionsDoneSuccess() ); REQUIRE( nullptr != lastCompletedDevice ); REQUIRE( EIRDataType::NONE != lastCompletedDeviceEIR.getEIRDataMask() ); REQUIRE( false == lastCompletedDevice->getConnected() ); - REQUIRE( ( protocolSessionCount * DBTConstants::max_connections_per_session ) > server->disconnectCount ); + REQUIRE( ( protocolSessionCount * DBTConstants::max_connections_per_session ) > server->getDisconnectCount() ); } // @@ -197,15 +209,19 @@ class DBTClientServer1x { const BTSecurityLevel clientKeysSecLevel = clientKeys.getSecLevel(); REQUIRE( secLevelClient == clientKeysSecLevel); { - if( clientShallHaveKeys ) { + if( ExpectedPairing::PREPAIRED == clientExpPairing && BTSecurityLevel::NONE < secLevelClient ) { // 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 ) { + } else if( ExpectedPairing::NEW_PAIRING == clientExpPairing && 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 if( ExpectedPairing::DONT_CARE == clientExpPairing && BTSecurityLevel::NONE < secLevelClient ) { + // Any encryption, any pairing + 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 ); diff --git a/trial/direct_bt/dbt_client_test.hpp b/trial/direct_bt/dbt_client_test.hpp index 5671180b..d97bb3b3 100644 --- a/trial/direct_bt/dbt_client_test.hpp +++ b/trial/direct_bt/dbt_client_test.hpp @@ -33,6 +33,30 @@ typedef std::shared_ptr<DBTClientTest> DBTClientTestRef; class DBTClientTest : public DBTEndpoint { public: + /** + * Set DiscoveryPolicy + * + * Default is {@link DiscoveryPolicy#PAUSE_CONNECTED_UNTIL_READY} + */ + virtual void setDiscoveryPolicy(const DiscoveryPolicy v) = 0; + + /** + * Set disconnect after processing. + * + * Default is `false`. + */ + virtual void setKeepConnected(const bool v) = 0; + + /** + * Set remove device when disconnecting. + * + * This removes the device from all instances within adapter + * and hence all potential side-effects of the current instance. + * + * Default is `false`, since it is good to test whether such side-effects exists. + */ + virtual void setRemoveDevice(const bool v) = 0; + virtual HCIStatusCode startDiscovery(const std::string& msg) = 0; virtual HCIStatusCode stopDiscovery(const std::string& msg) = 0; diff --git a/trial/direct_bt/dbt_constants.hpp b/trial/direct_bt/dbt_constants.hpp index 2dff7285..0218d21f 100644 --- a/trial/direct_bt/dbt_constants.hpp +++ b/trial/direct_bt/dbt_constants.hpp @@ -62,6 +62,12 @@ class DBTConstants { static const std::vector<uint8_t> FailHandshakeCommandData; }; +enum class ExpectedPairing { + DONT_CARE, + NEW_PAIRING, + PREPAIRED +}; + 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"); diff --git a/trial/direct_bt/dbt_endpoint.hpp b/trial/direct_bt/dbt_endpoint.hpp index 3d7c352c..4146ceb6 100644 --- a/trial/direct_bt/dbt_endpoint.hpp +++ b/trial/direct_bt/dbt_endpoint.hpp @@ -68,6 +68,12 @@ class DBTEndpoint { virtual void close(const std::string& msg) = 0; + virtual void setProtocolSessionsLeft(const int v) = 0; + virtual int getProtocolSessionsLeft() = 0; + virtual int getProtocolSessionsDoneTotal() = 0; + virtual int getProtocolSessionsDoneSuccess() = 0; + virtual int getDisconnectCount() = 0; + /** * Initialize the given adapter for this endpoint. * diff --git a/trial/direct_bt/dbt_server00.hpp b/trial/direct_bt/dbt_server00.hpp index 362b0141..9565e0b1 100644 --- a/trial/direct_bt/dbt_server00.hpp +++ b/trial/direct_bt/dbt_server00.hpp @@ -70,9 +70,11 @@ class DBTServer00 : public DBTServerTest { bool use_SC = true; BTSecurityLevel adapterSecurityLevel = BTSecurityLevel::UNSET; - // DBGattServerRef dbGattServer = std::make_shared<DBGattServer>( - // DBGattServerRef dbGattServer = new DBGattServer( - // DBGattServerRef dbGattServer( new DBGattServer( + jau::sc_atomic_int disconnectCount = 0; + jau::sc_atomic_int servedProtocolSessionsTotal = 0; + jau::sc_atomic_int servedProtocolSessionsSuccess = 0; + jau::sc_atomic_int servingProtocolSessionsLeft = 1; + DBGattServerRef dbGattServer = std::make_shared<DBGattServer>( /* services: */ jau::make_darray( // DBGattService @@ -152,14 +154,6 @@ class DBTServer00 : public DBTServerTest { std::mutex mtx_sync; BTDeviceRef connectedDevice; - public: - jau::sc_atomic_int disconnectCount = 0; - jau::sc_atomic_int servedProtocolSessionsTotal = 0; - jau::sc_atomic_int servedProtocolSessionsSuccess = 0; - jau::sc_atomic_int servingProtocolSessionsLeft = 1; - - private: - void setDevice(BTDeviceRef cd) { const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor connectedDevice = cd; @@ -618,6 +612,22 @@ class DBTServer00 : public DBTServerTest { fprintf_td(stderr, "****** Server Close.X: %s\n", msg.c_str()); } + void setProtocolSessionsLeft(const int v) override { + servingProtocolSessionsLeft = v; + } + int getProtocolSessionsLeft() override { + return servingProtocolSessionsLeft; + } + int getProtocolSessionsDoneTotal() override { + return servedProtocolSessionsTotal; + } + int getProtocolSessionsDoneSuccess() override { + return servedProtocolSessionsSuccess; + } + int getDisconnectCount() override { + return disconnectCount; + } + private: HCIStatusCode stopAdvertising(std::string msg) { HCIStatusCode status = serverAdapter->stopAdvertising(); diff --git a/trial/direct_bt/dbt_server01.hpp b/trial/direct_bt/dbt_server01.hpp index 33e34807..911aaf4d 100644 --- a/trial/direct_bt/dbt_server01.hpp +++ b/trial/direct_bt/dbt_server01.hpp @@ -22,15 +22,15 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#ifndef DBT_SERVER00_HPP_ -#define DBT_SERVER00_HPP_ +#ifndef DBT_SERVER01_HPP_ +#define DBT_SERVER01_HPP_ #include "dbt_constants.hpp" #include "dbt_server_test.hpp" class DBTServer01; -typedef std::shared_ptr<DBTServer01> DBTServer00Ref; +typedef std::shared_ptr<DBTServer01> DBTServer01Ref; using namespace jau; @@ -70,9 +70,11 @@ class DBTServer01 : public DBTServerTest { bool use_SC = true; BTSecurityLevel adapterSecurityLevel = BTSecurityLevel::UNSET; - // DBGattServerRef dbGattServer = std::make_shared<DBGattServer>( - // DBGattServerRef dbGattServer = new DBGattServer( - // DBGattServerRef dbGattServer( new DBGattServer( + jau::sc_atomic_int disconnectCount = 0; + jau::sc_atomic_int servedProtocolSessionsTotal = 0; + jau::sc_atomic_int servedProtocolSessionsSuccess = 0; + jau::sc_atomic_int servingProtocolSessionsLeft = 1; + DBGattServerRef dbGattServer = std::make_shared<DBGattServer>( /* services: */ jau::make_darray( // DBGattService @@ -152,14 +154,6 @@ class DBTServer01 : public DBTServerTest { std::mutex mtx_sync; BTDeviceRef connectedDevice; - public: - jau::sc_atomic_int disconnectCount = 0; - jau::sc_atomic_int servedProtocolSessionsTotal = 0; - jau::sc_atomic_int servedProtocolSessionsSuccess = 0; - jau::sc_atomic_int servingProtocolSessionsLeft = 1; - - private: - void setDevice(BTDeviceRef cd) { const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor connectedDevice = cd; @@ -387,6 +381,17 @@ class DBTServer01 : public DBTServerTest { } } + bool onceDisconnect = true; + + void disconnectDevice() { + jau::sleep_for( 300_ms ); + BTDeviceRef connectedDevice_ = parent.getDevice(); + fprintf_td(stderr, "****** Server GATT::disconnectDevice(sessions [%d ok / %d total], left %d): client %s\n", + parent.servedProtocolSessionsSuccess.load(), parent.servedProtocolSessionsTotal.load(), parent.servingProtocolSessionsLeft.load(), + connectedDevice_->toString().c_str()); + connectedDevice_->disconnect(); + } + public: MyGATTServerListener(DBTServer01& p) @@ -418,17 +423,7 @@ class DBTServer01 : public DBTServerTest { } } - bool onceDisconnect = true; - - void disconnectDevice() { - jau::sleep_for( 300_ms ); - BTDeviceRef connectedDevice_ = parent.getDevice(); - fprintf_td(stderr, "****** Server GATT::disconnectDevice(sessions [%d ok / %d total], left %d): client %s\n", - parent.servedProtocolSessionsSuccess.load(), parent.servedProtocolSessionsTotal.load(), parent.servingProtocolSessionsLeft.load(), - connectedDevice_->toString().c_str()); - connectedDevice_->disconnect(); - } - + public: void connected(BTDeviceRef device, const uint16_t initialMTU) override { const bool match = parent.matches(device); fprintf_td(stderr, "****** Server GATT::connected(match %d): initMTU %d, %s\n", @@ -637,6 +632,22 @@ class DBTServer01 : public DBTServerTest { fprintf_td(stderr, "****** Server Close.X: %s\n", msg.c_str()); } + void setProtocolSessionsLeft(const int v) override { + servingProtocolSessionsLeft = v; + } + int getProtocolSessionsLeft() override { + return servingProtocolSessionsLeft; + } + int getProtocolSessionsDoneTotal() override { + return servedProtocolSessionsTotal; + } + int getProtocolSessionsDoneSuccess() override { + return servedProtocolSessionsSuccess; + } + int getDisconnectCount() override { + return disconnectCount; + } + private: HCIStatusCode stopAdvertising(std::string msg) { HCIStatusCode status = serverAdapter->stopAdvertising(); @@ -789,4 +800,4 @@ class DBTServer01 : public DBTServerTest { } }; -#endif // DBT_SERVER00_HPP_ +#endif // DBT_SERVER01_HPP_ diff --git a/trial/direct_bt/test_client_server00.cpp b/trial/direct_bt/test_client_server00.cpp index 2c342cbf..e4d39908 100644 --- a/trial/direct_bt/test_client_server00.cpp +++ b/trial/direct_bt/test_client_server00.cpp @@ -97,7 +97,7 @@ TEST_CASE( "Server StartStop and SwitchRole Trial 10", "[trial][startstop][switc const std::string serverName = "TestDBTCS00-S-T10"; std::shared_ptr<DBTServer00> server = std::make_shared<DBTServer00>(serverName, EUI48::ALL_DEVICE, BTMode::DUAL, true /* SC */, BTSecurityLevel::NONE); - server->servingProtocolSessionsLeft = 1; + server->setProtocolSessionsLeft(1); ChangedAdapterSetCallback myChangedAdapterSetFunc = DBTEndpoint::initChangedAdapterSetListener(manager, { server }); diff --git a/trial/direct_bt/test_client_server10_NoEnc.cpp b/trial/direct_bt/test_client_server10_NoEnc.cpp index 51b4dca8..ada2271a 100644 --- a/trial/direct_bt/test_client_server10_NoEnc.cpp +++ b/trial/direct_bt/test_client_server10_NoEnc.cpp @@ -53,10 +53,10 @@ class TestDBTClientServer10_NoEnc : public DBTClientServer1x { void test00_FullCycle_EncNone() { base_test_framework.setupTest( 20_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::DONT_CARE; + const ExpectedPairing clientExpPairing = ExpectedPairing::DONT_CARE; test8x_fullCycle("10", 1 /* protocolSessionCount */, true /* server_client_order */, serverSC, - BTSecurityLevel::NONE, serverShallHaveKeys, BTSecurityLevel::NONE, clientShallHaveKeys); + BTSecurityLevel::NONE, serverExpPairing, BTSecurityLevel::NONE, clientExpPairing); } base_test_framework.cleanupTest(); } @@ -64,10 +64,10 @@ class TestDBTClientServer10_NoEnc : public DBTClientServer1x { void test01_FullCycle_EncNone() { base_test_framework.setupTest( 30_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::DONT_CARE; + const ExpectedPairing clientExpPairing = ExpectedPairing::DONT_CARE; test8x_fullCycle("11", 2 /* protocolSessionCount */, true /* server_client_order */, serverSC, - BTSecurityLevel::NONE, serverShallHaveKeys, BTSecurityLevel::NONE, clientShallHaveKeys); + BTSecurityLevel::NONE, serverExpPairing, BTSecurityLevel::NONE, clientExpPairing); } base_test_framework.cleanupTest(); } diff --git a/trial/direct_bt/test_client_server12_NoEnc.cpp b/trial/direct_bt/test_client_server12_NoEnc.cpp index 37c5b4d5..37c49964 100644 --- a/trial/direct_bt/test_client_server12_NoEnc.cpp +++ b/trial/direct_bt/test_client_server12_NoEnc.cpp @@ -53,10 +53,10 @@ class TestDBTClientServer12_NoEnc : public DBTClientServer1x { void test02_FullCycle_EncNone() { base_test_framework.setupTest( 20_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::DONT_CARE; + const ExpectedPairing clientExpPairing = ExpectedPairing::DONT_CARE; test8x_fullCycle("12", 1 /* protocolSessionCount */, false /* server_client_order */, serverSC, - BTSecurityLevel::NONE, serverShallHaveKeys, BTSecurityLevel::NONE, clientShallHaveKeys); + BTSecurityLevel::NONE, serverExpPairing, BTSecurityLevel::NONE, clientExpPairing); } base_test_framework.cleanupTest(); } @@ -64,10 +64,10 @@ class TestDBTClientServer12_NoEnc : public DBTClientServer1x { void test03_FullCycle_EncNone() { base_test_framework.setupTest( 30_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::DONT_CARE; + const ExpectedPairing clientExpPairing = ExpectedPairing::DONT_CARE; test8x_fullCycle("13", 2 /* protocolSessionCount */, false /* server_client_order */, serverSC, - BTSecurityLevel::NONE, serverShallHaveKeys, BTSecurityLevel::NONE, clientShallHaveKeys); + BTSecurityLevel::NONE, serverExpPairing, BTSecurityLevel::NONE, clientExpPairing); } base_test_framework.cleanupTest(); } diff --git a/trial/direct_bt/test_client_server20_SC0.cpp b/trial/direct_bt/test_client_server20_SC0.cpp index cadb0040..29e1c7c3 100644 --- a/trial/direct_bt/test_client_server20_SC0.cpp +++ b/trial/direct_bt/test_client_server20_SC0.cpp @@ -53,10 +53,10 @@ class TestDBTClientServer20_SC0 : public DBTClientServer1x { void test10_FullCycle_EncOnlyNo1() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::NEW_PAIRING; + const ExpectedPairing clientExpPairing = ExpectedPairing::NEW_PAIRING; test8x_fullCycle("20", 1 /* protocolSessionCount */, true /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } @@ -64,10 +64,10 @@ class TestDBTClientServer20_SC0 : public DBTClientServer1x { void test20_FullCycle_EncOnlyNo2() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = true; - const bool clientShallHaveKeys = true; + const ExpectedPairing serverExpPairing = ExpectedPairing::PREPAIRED; + const ExpectedPairing clientExpPairing = ExpectedPairing::PREPAIRED; test8x_fullCycle("21", 2 /* protocolSessionCount */, true /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } diff --git a/trial/direct_bt/test_client_server22_SC0.cpp b/trial/direct_bt/test_client_server22_SC0.cpp index e08ea4d4..8cddf1b0 100644 --- a/trial/direct_bt/test_client_server22_SC0.cpp +++ b/trial/direct_bt/test_client_server22_SC0.cpp @@ -53,10 +53,10 @@ class TestDBTClientServer22_SC0 : public DBTClientServer1x { void test11_FullCycle_EncOnlyNo1() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::NEW_PAIRING; + const ExpectedPairing clientExpPairing = ExpectedPairing::NEW_PAIRING; test8x_fullCycle("22", 1 /* protocolSessionCount */, false /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } @@ -64,10 +64,10 @@ class TestDBTClientServer22_SC0 : public DBTClientServer1x { void test21_FullCycle_EncOnlyNo2() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = true; - const bool clientShallHaveKeys = true; + const ExpectedPairing serverExpPairing = ExpectedPairing::PREPAIRED; + const ExpectedPairing clientExpPairing = ExpectedPairing::PREPAIRED; test8x_fullCycle("23", 2 /* protocolSessionCount */, false /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } diff --git a/trial/direct_bt/test_client_server30_SC1.cpp b/trial/direct_bt/test_client_server30_SC1.cpp index 09eb07fe..a60b7d95 100644 --- a/trial/direct_bt/test_client_server30_SC1.cpp +++ b/trial/direct_bt/test_client_server30_SC1.cpp @@ -53,10 +53,10 @@ class TestDBTClientServer30_SC1 : public DBTClientServer1x { void test10_FullCycle_EncOnlyNo1() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::NEW_PAIRING; + const ExpectedPairing clientExpPairing = ExpectedPairing::NEW_PAIRING; test8x_fullCycle("30", 1 /* protocolSessionCount */, true /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } @@ -64,10 +64,10 @@ class TestDBTClientServer30_SC1 : public DBTClientServer1x { void test20_FullCycle_EncOnlyNo2() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = true; - const bool clientShallHaveKeys = true; + const ExpectedPairing serverExpPairing = ExpectedPairing::PREPAIRED; + const ExpectedPairing clientExpPairing = ExpectedPairing::PREPAIRED; test8x_fullCycle("31", 2 /* protocolSessionCount */, true /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } diff --git a/trial/direct_bt/test_client_server32_SC1.cpp b/trial/direct_bt/test_client_server32_SC1.cpp index 30b2f2d0..6ff7221b 100644 --- a/trial/direct_bt/test_client_server32_SC1.cpp +++ b/trial/direct_bt/test_client_server32_SC1.cpp @@ -53,10 +53,10 @@ class TestDBTClientServer32_SC1 : public DBTClientServer1x { void test10_FullCycle_EncOnlyNo1() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = false; - const bool clientShallHaveKeys = false; + const ExpectedPairing serverExpPairing = ExpectedPairing::NEW_PAIRING; + const ExpectedPairing clientExpPairing = ExpectedPairing::NEW_PAIRING; test8x_fullCycle("32", 1 /* protocolSessionCount */, false /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } @@ -64,10 +64,10 @@ class TestDBTClientServer32_SC1 : public DBTClientServer1x { void test20_FullCycle_EncOnlyNo2() { base_test_framework.setupTest( 40_s ); { - const bool serverShallHaveKeys = true; - const bool clientShallHaveKeys = true; + const ExpectedPairing serverExpPairing = ExpectedPairing::PREPAIRED; + const ExpectedPairing clientExpPairing = ExpectedPairing::PREPAIRED; test8x_fullCycle("33", 2 /* protocolSessionCount */, false /* server_client_order */, serverSC, - BTSecurityLevel::ENC_ONLY, serverShallHaveKeys, BTSecurityLevel::ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel::ENC_ONLY, serverExpPairing, BTSecurityLevel::ENC_ONLY, clientExpPairing); } base_test_framework.cleanupTest(); } diff --git a/trial/direct_bt/test_provoke_client_server_i470.cpp b/trial/direct_bt/test_provoke_client_server_i470.cpp index d081e57a..e393182f 100644 --- a/trial/direct_bt/test_provoke_client_server_i470.cpp +++ b/trial/direct_bt/test_provoke_client_server_i470.cpp @@ -27,7 +27,8 @@ #include <catch2/catch_amalgamated.hpp> #include <jau/test/catch2_ext.hpp> -#include "dbt_base_client_server.hpp" +// #include "dbt_base_client_server.hpp" +#include "dbt_client_server1x.hpp" #include "dbt_server01.hpp" #include "dbt_client01.hpp" @@ -43,210 +44,30 @@ static BaseDBTClientServer& base_test_framework = BaseDBTClientServer::get(); * In other words, relying on BTAdapter to filter out: * - already discovered devices * - already connected devices + * + * Further, the server will issue a disconnect once only 300 ms after 1st MTU exchange, + * disrupting the client's getGATTServices(). */ -class TestDBTClientServer_i470 { - private: - std::mutex mtx_sync; - BTDeviceRef lastCompletedDevice = nullptr; - PairingMode lastCompletedDevicePairingMode = PairingMode::NONE; - BTSecurityLevel lastCompletedDeviceSecurityLevel = BTSecurityLevel::NONE; - EInfoReport lastCompletedDeviceEIR; - +class TestDBTClientServer_i470 : public DBTClientServer1x { public: void test_i470() { - const bool serverSC = true; - const std::string suffix = "i470"; - const int protocolSessionCount = 2; - const bool server_client_order = true; - const BTSecurityLevel secLevelServer = BTSecurityLevel::ENC_ONLY; - const BTSecurityLevel secLevelClient = BTSecurityLevel::ENC_ONLY; - base_test_framework.setupTest( 20_s ); { - - const jau::fraction_timespec t0 = jau::getMonotonicTime(); - - 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<DBTServer01> server = std::make_shared<DBTServer01>("S-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL, serverSC, secLevelServer); - server->servingProtocolSessionsLeft = 2* protocolSessionCount; - - std::shared_ptr<DBTClient01> client = std::make_shared<DBTClient01>("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 = protocolSessionCount; - client->discoveryPolicy = DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED; - - { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor - lastCompletedDevice = nullptr; - lastCompletedDevicePairingMode = PairingMode::NONE; - lastCompletedDeviceSecurityLevel = BTSecurityLevel::NONE; - lastCompletedDeviceEIR.clear(); - } - class MyAdapterStatusListener : public AdapterStatusListener { - private: - TestDBTClientServer_i470& parent; - public: - MyAdapterStatusListener(TestDBTClientServer_i470& 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"); - - BaseDBTClientServer& framework = BaseDBTClientServer::get(); - const jau::fraction_i64 timeout_value = framework.get_timeout_value(); - jau::fraction_i64 test_duration = 0_s; - bool done = false; - bool timeout = false; - bool max_connections_hit = false; - do { - { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor - done = ! ( protocolSessionCount > server->servedProtocolSessionsTotal || - protocolSessionCount > client->completedMeasurements || - nullptr == lastCompletedDevice || - lastCompletedDevice->getConnected() ); - } - max_connections_hit = ( protocolSessionCount * DBTConstants::max_connections_per_session ) <= server->disconnectCount; - test_duration = ( jau::getMonotonicTime() - t0 ).to_fraction_i64(); - timeout = 0_s < timeout_value && timeout_value <= test_duration + 500_ms; // let's timeout here before our timeout timer - if( !done && !max_connections_hit && !timeout ) { - jau::sleep_for( 88_ms ); - } - } while( !done && !max_connections_hit && !timeout ); - test_duration = ( jau::getMonotonicTime() - t0 ).to_fraction_i64(); - - fprintf_td(stderr, "\n\n"); - fprintf_td(stderr, "****** Test Stats: duration %" PRIi64 " ms, timeout[hit %d, value %s sec], max_connections hit %d\n", - test_duration.to_ms(), timeout, timeout_value.to_string(true).c_str(), max_connections_hit); - fprintf_td(stderr, " Server ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", - server->servedProtocolSessionsSuccess.load(), server->servedProtocolSessionsTotal.load(), protocolSessionCount, - server->disconnectCount.load(), ( protocolSessionCount * DBTConstants::max_connections_per_session )); - fprintf_td(stderr, " Client ProtocolSessions success %d \n", - client->completedMeasurements.load()); - fprintf_td(stderr, "\n\n"); - - REQUIRE( false == max_connections_hit ); - REQUIRE( false == timeout ); - { - const std::lock_guard<std::mutex> lock(mtx_sync); // RAII-style acquire and relinquish via destructor - REQUIRE( protocolSessionCount == server->servedProtocolSessionsTotal ); - REQUIRE( protocolSessionCount == server->servedProtocolSessionsSuccess ); - REQUIRE( protocolSessionCount == client->completedMeasurements ); - REQUIRE( nullptr != lastCompletedDevice ); - REQUIRE( EIRDataType::NONE != lastCompletedDeviceEIR.getEIRDataMask() ); - REQUIRE( false == lastCompletedDevice->getConnected() ); - REQUIRE( ( protocolSessionCount * DBTConstants::max_connections_per_session ) > server->disconnectCount ); - } - - // - // Client stop - // - DBTClientTest::stopDiscovery(client, true /* current_exp_discovering_state */, "test"+suffix+"_stopDiscovery"); - client->close("test"+suffix+"_close"); - - // - // Server stop - // - DBTServerTest::stop(server, true /* 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( PairingMode::PRE_PAIRED == lastCompletedDevicePairingMode ) { - // Using encryption: pre-paired - 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() ); - } - - // - // 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); + const bool serverSC = true; + const std::string suffix = "i470"; + const int protocolSessionCount = 2; + const bool server_client_order = true; + const BTSecurityLevel secLevelServer = BTSecurityLevel::ENC_ONLY; + const BTSecurityLevel secLevelClient = BTSecurityLevel::ENC_ONLY; + const ExpectedPairing serverExpPairing = ExpectedPairing::DONT_CARE; + const ExpectedPairing clientExpPairing = ExpectedPairing::DONT_CARE; + + // std::shared_ptr<DBTServerTest> server = std::make_shared<DBTServer01>("S-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL, serverSC, secLevelServer); + std::shared_ptr<DBTServerTest> server = std::make_shared<DBTServer00>("S-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL, serverSC, secLevelServer); + std::shared_ptr<DBTClientTest> client = std::make_shared<DBTClient01>("C-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL); + test8x_fullCycle(suffix, protocolSessionCount, server_client_order, + server, secLevelServer, serverExpPairing, + client, secLevelClient, clientExpPairing); } 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 56d69bee..91aa9029 100644 --- a/trial/java/trial/org/direct_bt/DBTClient00.java +++ b/trial/java/trial/org/direct_bt/DBTClient00.java @@ -56,48 +56,41 @@ import org.direct_bt.SMPPairingState; import org.direct_bt.ScanType; import org.jau.net.EUI48; +import trial.org.direct_bt.DBTClient01.MyAdapterStatusListener; + /** * This central BTRole::Master participant works with DBTServer00. */ public class DBTClient00 implements DBTClientTest { - /** - * Disconnect after processing. - * - * Default is `false`. - */ - boolean KEEP_CONNECTED = false; - - /** - * Remove device when disconnecting. - * - * This removes the device from all instances within adapter - * and hence all potential side-effects of the current instance. - * - * Default is `false`, since it is good to test whether such side-effects exists. - */ - boolean REMOVE_DEVICE = false; - - final boolean GATT_VERBOSE = false; - final boolean SHOW_UPDATE_EVENTS = false; - - final long timestamp_t0 = BTUtils.startupTimeMillis(); - - final String adapterShortName = "TDev2Clt"; - String adapterName = "TestDev2_Clt"; - EUI48 useAdapter = EUI48.ALL_DEVICE; - BTMode btMode = BTMode.DUAL; - BTAdapter clientAdapter = null; + private final boolean GATT_VERBOSE = false; + + private final long timestamp_t0 = BTUtils.startupTimeMillis(); + + private final String adapterShortName = "TDev2Clt"; + private final MyAdapterStatusListener myAdapterStatusListener = new MyAdapterStatusListener(); - AtomicInteger deviceReadyCount = new AtomicInteger(0); + private final byte cmd_arg = (byte)0x44; + + private String adapterName = "TestDev2_Clt"; + private EUI48 useAdapter = EUI48.ALL_DEVICE; + private BTMode btMode = BTMode.DUAL; + private BTAdapter clientAdapter = null; + + private boolean KEEP_CONNECTED = false; + + private boolean REMOVE_DEVICE = false; - AtomicInteger notificationsReceived = new AtomicInteger(0); - AtomicInteger indicationsReceived = new AtomicInteger(0); - AtomicInteger completedGATTCommands = new AtomicInteger(0); - AtomicInteger completedMeasurements = new AtomicInteger(0); - AtomicInteger measurementsLeft = new AtomicInteger(1); + private DiscoveryPolicy discoveryPolicy = DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_READY; // default value - final byte cmd_arg = (byte)0x44; + private final AtomicInteger disconnectCount = new AtomicInteger(0); + private final AtomicInteger measurementsLeft = new AtomicInteger(1); + private final AtomicInteger deviceReadyCount = new AtomicInteger(0); + private final AtomicInteger notificationsReceived = new AtomicInteger(0); + private final AtomicInteger indicationsReceived = new AtomicInteger(0); + private final AtomicInteger completedGATTCommands = new AtomicInteger(0); + private final AtomicInteger completedMeasurementsTotal = new AtomicInteger(0); + private final AtomicInteger completedMeasurementsSuccess = new AtomicInteger(0); public DBTClient00(final String adapterName, final EUI48 useAdapter, final BTMode btMode) { this.adapterName = adapterName; @@ -115,6 +108,40 @@ public class DBTClient00 implements DBTClientTest { @Override public BTAdapter getAdapter() { return clientAdapter; } + @Override + public void setProtocolSessionsLeft(final int v) { + measurementsLeft.set(v); + } + @Override + public int getProtocolSessionsLeft() { + return measurementsLeft.get(); + } + @Override + public int getProtocolSessionsDoneTotal() { + return completedMeasurementsTotal.get(); + } + @Override + public int getProtocolSessionsDoneSuccess() { + return completedMeasurementsSuccess.get(); + } + @Override + public int getDisconnectCount() { + return this.disconnectCount.get(); + } + + @Override + public void setDiscoveryPolicy(final DiscoveryPolicy v) { + discoveryPolicy = v; + } + @Override + public void setKeepConnected(final boolean v) { + KEEP_CONNECTED = v; + } + @Override + public void setRemoveDevice(final boolean v) { + REMOVE_DEVICE = v; + } + static void executeOffThread(final Runnable runobj, final String threadName, final boolean detach) { final Thread t = new Thread( runobj, threadName ); if( detach ) { @@ -269,6 +296,8 @@ public class DBTClient00 implements DBTClientTest { public void deviceDisconnected(final BTDevice device, final HCIStatusCode reason, final short handle, final long timestamp) { BTUtils.println(System.err, "****** Client DISCONNECTED: Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter()); + disconnectCount.addAndGet(1); + executeOffThread( () -> { removeDevice(device); }, "Client DBT-Remove-"+device.getAddressAndType(), true /* detach */); } @@ -558,14 +587,15 @@ public class DBTClient00 implements DBTClientTest { } } + completedMeasurementsTotal.addAndGet(1); if( success ) { - completedMeasurements.addAndGet(1); + completedMeasurementsSuccess.addAndGet(1); } if( 0 < measurementsLeft.get() ) { measurementsLeft.decrementAndGet(); } BTUtils.println(System.err, "****** Client Processing Ready Device: Success "+success+ - "; Measurements completed "+completedMeasurements.get()+ + "; Measurements completed "+completedMeasurementsSuccess.get()+ ", left "+measurementsLeft.get()+ "; Received notitifications "+notificationsReceived.get()+", indications "+indicationsReceived.get()+ "; Completed GATT commands "+completedGATTCommands.get()+ @@ -582,7 +612,6 @@ public class DBTClient00 implements DBTClientTest { } } - DiscoveryPolicy discoveryPolicy = DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_READY; // 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 diff --git a/trial/java/trial/org/direct_bt/DBTClient01.java b/trial/java/trial/org/direct_bt/DBTClient01.java new file mode 100644 index 00000000..3d53b2ff --- /dev/null +++ b/trial/java/trial/org/direct_bt/DBTClient01.java @@ -0,0 +1,679 @@ +/** + * 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. + */ + +package trial.org.direct_bt; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.direct_bt.AdapterSettings; +import org.direct_bt.AdapterStatusListener; +import org.direct_bt.BTMode; +import org.direct_bt.BTSecurityLevel; +import org.direct_bt.BTAdapter; +import org.direct_bt.BTDevice; +import org.direct_bt.BTDeviceRegistry; +import org.direct_bt.BTGattChar; +import org.direct_bt.BTGattCmd; +import org.direct_bt.BTGattDesc; +import org.direct_bt.BTGattService; +import org.direct_bt.BTSecurityRegistry; +import org.direct_bt.BTUtils; +import org.direct_bt.DiscoveryPolicy; +import org.direct_bt.EIRDataTypeSet; +import org.direct_bt.EInfoReport; +import org.direct_bt.GattCharPropertySet; +import org.direct_bt.HCIStatusCode; +import org.direct_bt.LE_Features; +import org.direct_bt.LE_PHYs; +import org.direct_bt.PairingMode; +import org.direct_bt.SMPIOCapability; +import org.direct_bt.SMPKeyBin; +import org.direct_bt.SMPPairingState; +import org.direct_bt.ScanType; +import org.jau.net.EUI48; + +/** + * This central BTRole::Master participant works with DBTServer00. + */ +public class DBTClient01 implements DBTClientTest { + private final boolean GATT_VERBOSE = false; + + private final long timestamp_t0 = BTUtils.startupTimeMillis(); + + private final String adapterShortName = "TDev2Clt"; + + private final MyAdapterStatusListener myAdapterStatusListener = new MyAdapterStatusListener(); + + private final byte cmd_arg = (byte)0x44; + + private String adapterName = "TestDev2_Clt"; + private EUI48 useAdapter = EUI48.ALL_DEVICE; + private BTMode btMode = BTMode.DUAL; + private BTAdapter clientAdapter = null; + + private boolean KEEP_CONNECTED = false; + + private boolean REMOVE_DEVICE = false; + + private DiscoveryPolicy discoveryPolicy = DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_READY; // default value + + private final AtomicInteger disconnectCount = new AtomicInteger(0); + private final AtomicInteger measurementsLeft = new AtomicInteger(1); + private final AtomicInteger deviceReadyCount = new AtomicInteger(0); + private final AtomicInteger notificationsReceived = new AtomicInteger(0); + private final AtomicInteger indicationsReceived = new AtomicInteger(0); + private final AtomicInteger completedGATTCommands = new AtomicInteger(0); + private final AtomicInteger completedMeasurementsTotal = new AtomicInteger(0); + private final AtomicInteger completedMeasurementsSuccess = new AtomicInteger(0); + + public DBTClient01(final String adapterName, final EUI48 useAdapter, final BTMode btMode) { + this.adapterName = adapterName; + this.useAdapter = useAdapter; + this.btMode = btMode; + } + + @Override + public String getName() { return adapterName; } + + @Override + public void setAdapter(final BTAdapter clientAdapter) { + this.clientAdapter = clientAdapter; + } + @Override + public BTAdapter getAdapter() { return clientAdapter; } + + @Override + public void setProtocolSessionsLeft(final int v) { + measurementsLeft.set(v); + } + @Override + public int getProtocolSessionsLeft() { + return measurementsLeft.get(); + } + @Override + public int getProtocolSessionsDoneTotal() { + return completedMeasurementsTotal.get(); + } + @Override + public int getProtocolSessionsDoneSuccess() { + return completedMeasurementsSuccess.get(); + } + @Override + public int getDisconnectCount() { + return this.disconnectCount.get(); + } + + @Override + public void setDiscoveryPolicy(final DiscoveryPolicy v) { + discoveryPolicy = v; + } + @Override + public void setKeepConnected(final boolean v) { + KEEP_CONNECTED = v; + } + @Override + public void setRemoveDevice(final boolean v) { + REMOVE_DEVICE = v; + } + + static void executeOffThread(final Runnable runobj, final String threadName, final boolean detach) { + final Thread t = new Thread( runobj, threadName ); + if( detach ) { + t.setDaemon(true); // detach thread + } + t.start(); + } + static void execute(final Runnable task, final boolean offThread) { + if( offThread ) { + final Thread t = new Thread(task); + t.setDaemon(true); + t.start(); + } else { + task.run(); + } + } + + class MyAdapterStatusListener extends AdapterStatusListener { + @Override + public void adapterSettingsChanged(final BTAdapter adapter, final AdapterSettings oldmask, + final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) { + final boolean initialSetting = oldmask.isEmpty(); + if( initialSetting ) { + BTUtils.println(System.err, "****** Client SETTINGS: "+oldmask+" -> "+newmask+", initial "+changedmask); + } else { + BTUtils.println(System.err, "****** Client SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask); + } + BTUtils.println(System.err, "Client Status Adapter:"); + BTUtils.println(System.err, adapter.toString()); + } + + @Override + public void discoveringChanged(final BTAdapter adapter, final ScanType currentMeta, final ScanType changedType, final boolean changedEnabled, final DiscoveryPolicy policy, final long timestamp) { + BTUtils.println(System.err, "****** Client DISCOVERING: meta "+currentMeta+", changed["+changedType+", enabled "+changedEnabled+", policy "+policy+"] on "+adapter); + } + + @Override + public boolean deviceFound(final BTDevice device, final long timestamp) { + if( BTDeviceRegistry.isWaitingForDevice(device.getAddressAndType().address, device.getName()) && + 0 < measurementsLeft.get() + ) + { + BTUtils.println(System.err, "****** Client FOUND__-0: Connecting "+device.toString()); + { + final long td = BTUtils.currentTimeMillis() - timestamp_t0; // adapter-init -> now + BTUtils.println(System.err, "PERF: adapter-init -> FOUND__-0 " + td + " ms"); + } + executeOffThread( () -> { connectDiscoveredDevice(device); }, + "Client DBT-Connect-"+device.getAddressAndType(), true /* detach */); + return true; + } else { + BTUtils.println(System.err, "****** Client FOUND__-1: NOP "+device.toString()); + return false; + } + } + + @Override + public void deviceUpdated(final BTDevice device, final EIRDataTypeSet updateMask, final long timestamp) { + } + + @Override + public void deviceConnected(final BTDevice device, final short handle, final long timestamp) { + BTUtils.println(System.err, "****** Client CONNECTED: "+device.toString()); + } + + @Override + public void devicePairingState(final BTDevice device, final SMPPairingState state, final PairingMode mode, final long timestamp) { + BTUtils.println(System.err, "****** Client PAIRING_STATE: state "+state+", mode "+mode+": "+device); + switch( state ) { + case NONE: + // next: deviceReady(..) + break; + case FAILED: { + final boolean res = SMPKeyBin.remove(DBTConstants.CLIENT_KEY_PATH, device); + BTUtils.println(System.err, "****** Client PAIRING_STATE: state "+state+"; Remove key file "+SMPKeyBin.getFilename(DBTConstants.CLIENT_KEY_PATH, device)+", res "+res); + // next: deviceReady() or deviceDisconnected(..) + } break; + case REQUESTED_BY_RESPONDER: + // next: FEATURE_EXCHANGE_STARTED + break; + case FEATURE_EXCHANGE_STARTED: + // next: FEATURE_EXCHANGE_COMPLETED + break; + case FEATURE_EXCHANGE_COMPLETED: + // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION + break; + case PASSKEY_EXPECTED: { + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, ""); + if( null != sec && sec.getPairingPasskey() != BTSecurityRegistry.NO_PASSKEY ) { + executeOffThread( () -> { device.setPairingPasskey( sec.getPairingPasskey() ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */); + } else { + executeOffThread( () -> { device.setPairingPasskey( 0 ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */); + // 3s disconnect: executeOffThread( () -> { device.setPairingPasskeyNegative(); }, "DBT-SetPasskeyNegative-"+device.getAddressAndType(), true /* detach */); + } + // next: KEY_DISTRIBUTION or FAILED + } break; + case NUMERIC_COMPARE_EXPECTED: { + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, ""); + if( null != sec ) { + executeOffThread( () -> { device.setPairingNumericComparison( sec.getPairingNumericComparison() ); }, "DBT-SetNumericComp-"+device.getAddressAndType(), true /* detach */); + } else { + executeOffThread( () -> { device.setPairingNumericComparison( false ); }, "DBT-SetNumericCompFalse-"+device.getAddressAndType(), true /* detach */); + } + // next: KEY_DISTRIBUTION or FAILED + } break; + case OOB_EXPECTED: + // FIXME: ABORT + break; + case KEY_DISTRIBUTION: + // next: COMPLETED or FAILED + break; + case COMPLETED: + // next: deviceReady(..) + break; + default: // nop + break; + } + } + + @Override + public void deviceReady(final BTDevice device, final long timestamp) { + { + deviceReadyCount.incrementAndGet(); + BTUtils.println(System.err, "****** Client READY-0: Processing["+deviceReadyCount.get()+"] "+device.toString()); + { + final long td = BTUtils.currentTimeMillis() - timestamp_t0; // adapter-init -> now + BTUtils.println(System.err, "PERF: adapter-init -> READY-0 " + td + " ms"); + } + + // Be nice to Test* case, allowing to reach its own listener.deviceReady() added later + executeOffThread( () -> { processReadyDevice(device); }, + "DBT-Process-"+device.getAddressAndType(), true /* detach */); + // processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution! + } + } + + @Override + public void deviceDisconnected(final BTDevice device, final HCIStatusCode reason, final short handle, final long timestamp) { + BTUtils.println(System.err, "****** Client DISCONNECTED: Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter()); + + disconnectCount.addAndGet(1); + + executeOffThread( () -> { removeDevice(device); }, "Client DBT-Remove-"+device.getAddressAndType(), true /* detach */); + } + + @Override + public String toString() { + return "Client AdapterStatusListener[user, per-adapter]"; + } + }; + + class MyGATTEventListener implements BTGattChar.Listener { + private final int i, j; + + public MyGATTEventListener(final int i_, final int j_) { i=i_; j=j_; } + + @Override + public void notificationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp) { + if( GATT_VERBOSE ) { + final long tR = BTUtils.currentTimeMillis(); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Notify: UUID %s, td %d ******\n", + i, j, charDecl.getUUID(), (tR-timestamp)); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length)); + } + notificationsReceived.incrementAndGet(); + } + + @Override + public void indicationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp, final boolean confirmationSent) { + if( GATT_VERBOSE ) { + final long tR = BTUtils.currentTimeMillis(); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n", + i, j, charDecl.getUUID(), (tR-timestamp), confirmationSent); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); + BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length)); + } + indicationsReceived.incrementAndGet(); + } + } + + private void resetLastProcessingStats() { + completedGATTCommands.set(0); + notificationsReceived.set(0); + indicationsReceived.set(0); + } + + private void connectDiscoveredDevice(final BTDevice device) { + BTUtils.println(System.err, "****** Client Connecting Device: Start " + device.toString()); + + resetLastProcessingStats(); + + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, device.getName()); + if( null != sec ) { + BTUtils.println(System.err, "****** Client Connecting Device: Found SecurityDetail "+sec.toString()+" for "+device.toString()); + } else { + BTUtils.println(System.err, "****** Client Connecting Device: No SecurityDetail for "+device.toString()); + } + final BTSecurityLevel req_sec_level = null != sec ? sec.getSecLevel() : BTSecurityLevel.UNSET; + HCIStatusCode res = device.uploadKeys(DBTConstants.CLIENT_KEY_PATH, req_sec_level, true /* verbose_ */); + BTUtils.fprintf_td(System.err, "****** Client Connecting Device: BTDevice::uploadKeys(...) result %s\n", res.toString()); + if( HCIStatusCode.SUCCESS != res ) { + if( null != sec ) { + if( sec.isSecurityAutoEnabled() ) { + final boolean r = device.setConnSecurityAuto( sec.getSecurityAutoIOCap() ); + BTUtils.println(System.err, "****** Client Connecting Device: Using SecurityDetail.SEC AUTO "+sec+" -> set OK "+r); + } else if( sec.isSecLevelOrIOCapSet() ) { + final boolean r = device.setConnSecurity(sec.getSecLevel(), sec.getIOCap()); + BTUtils.println(System.err, "****** Client Connecting Device: Using SecurityDetail.Level+IOCap "+sec+" -> set OK "+r); + } else { + final boolean r = device.setConnSecurityAuto( SMPIOCapability.KEYBOARD_ONLY ); + BTUtils.println(System.err, "****** Client Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY ("+sec+") -> set OK "+r); + } + } else { + final boolean r = device.setConnSecurityAuto( SMPIOCapability.KEYBOARD_ONLY ); + BTUtils.println(System.err, "****** Client Connecting Device: Setting SEC AUTO security detail w/ KEYBOARD_ONLY -> set OK "+r); + } + } + final EInfoReport eir = device.getEIR(); + BTUtils.println(System.err, "Client EIR-1 "+device.getEIRInd().toString()); + BTUtils.println(System.err, "Client EIR-2 "+device.getEIRScanRsp().toString()); + BTUtils.println(System.err, "Client EIR-+ "+eir.toString()); + + short conn_interval_min = (short)8; // 10ms + short conn_interval_max = (short)12; // 15ms + final short conn_latency = (short)0; + if( eir.isSet(EIRDataTypeSet.DataType.CONN_IVAL) ) { + final short[] minmax = new short[2]; + eir.getConnInterval(minmax); + conn_interval_min = minmax[0]; + conn_interval_max = minmax[1]; + } + final short supervision_timeout = BTUtils.getHCIConnSupervisorTimeout(conn_latency, (int) ( conn_interval_max * 1.25 ) /* ms */); + res = device.connectLE(le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout); + // res = device.connectDefault(); + BTUtils.println(System.err, "****** Client Connecting Device Command, res "+res+": End result "+res+" of " + device.toString()); + } + + private void processReadyDevice(final BTDevice device) { + BTUtils.println(System.err, "****** Client Processing Ready Device: Start " + device.toString()); + final long t1 = BTUtils.currentTimeMillis(); + + SMPKeyBin.createAndWrite(device, DBTConstants.CLIENT_KEY_PATH, true /* verbose */); + final long t2 = BTUtils.currentTimeMillis(); + + boolean success = false; + + { + final LE_PHYs resTx[] = { new LE_PHYs() }; + final LE_PHYs resRx[] = { new LE_PHYs() }; + final HCIStatusCode res = device.getConnectedLE_PHY(resTx, resRx); + BTUtils.fprintf_td(System.err, "****** Client Got Connected LE PHY: status %s: Tx %s, Rx %s\n", + res.toString(), resTx[0].toString(), resRx[0].toString()); + } + final long t3 = BTUtils.currentTimeMillis(); + + // + // GATT Service Processing + // + try { + final List<BTGattService> primServices = device.getGattServices(); + if( null == primServices || 0 == primServices.size() ) { + // Cheating the flow, but avoiding: goto, do-while-false and lastly unreadable intendations + // And it is an error case nonetheless ;-) + throw new RuntimeException("Processing Ready Device: getServices() failed " + device.toString()); + } + final long t5 = BTUtils.currentTimeMillis(); + { + final long td00 = device.getLastDiscoveryTimestamp() - timestamp_t0; // adapter-init to discovered + final long td01 = t1 - timestamp_t0; // adapter-init to processing-start + final long td05 = t5 - timestamp_t0; // adapter-init -> gatt-complete + final long tdc1 = t1 - device.getLastDiscoveryTimestamp(); // discovered to processing-start + final long tdc5 = t5 - device.getLastDiscoveryTimestamp(); // discovered to gatt-complete + final long td12 = t2 - t1; // SMPKeyBin + final long td23 = t3 - t2; // LE_PHY + final long td13 = t3 - t1; // SMPKeyBin + LE_PHY + final long td35 = t5 - t3; // get-gatt-services + BTUtils.println(System.err, System.lineSeparator()+System.lineSeparator()); + BTUtils.println(System.err, "PERF: GATT primary-services completed"+System.lineSeparator()+ + "PERF: adapter-init to discovered " + td00 + " ms,"+System.lineSeparator()+ + "PERF: adapter-init to processing-start " + td01 + " ms,"+System.lineSeparator()+ + "PERF: adapter-init to gatt-complete " + td05 + " ms,"+System.lineSeparator()+ + "PERF: discovered to processing-start " + tdc1 + " ms,"+System.lineSeparator()+ + "PERF: discovered to gatt-complete " + tdc5 + " ms,"+System.lineSeparator()+ + "PERF: SMPKeyBin + LE_PHY " + td13 + " ms (SMPKeyBin "+td12+"ms, LE_PHY "+td23+"ms), "+System.lineSeparator()+ + "PERF: get-gatt-services " + td35 + " ms,"+System.lineSeparator()); + } + + { + final BTGattCmd cmd = new BTGattCmd(device, "TestCmd", null /* service_uuid */, DBTConstants.CommandUUID, DBTConstants.ResponseUUID); + cmd.setVerbose(true); + final boolean cmd_resolved = cmd.isResolved(); + BTUtils.println(System.err, "Client Command test: "+cmd.toString()+", resolved "+cmd_resolved); + final byte[] cmd_data = { cmd_arg }; + final HCIStatusCode cmd_res = cmd.send(true /* prefNoAck */, cmd_data, 3000 /* timeoutMS */); + if( HCIStatusCode.SUCCESS == cmd_res ) { + final byte[] resp = cmd.getResponse(); + if( 1 == resp.length && resp[0] == cmd_arg ) { + BTUtils.fprintf_td(System.err, "Client Success: %s -> %s (echo response)\n", cmd.toString(), BTUtils.bytesHexString(resp, 0, resp.length, true /* lsb */)); + completedGATTCommands.incrementAndGet(); + } else { + BTUtils.fprintf_td(System.err, "Client Failure: %s -> %s (different response)\n", cmd.toString(), BTUtils.bytesHexString(resp, 0, resp.length, true /* lsb */)); + } + } else { + BTUtils.fprintf_td(System.err, "Client Failure: %s -> %s\n", cmd.toString(), cmd_res.toString()); + } + cmd.close(); + } + + try { + int i=0; + for(final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) { + final BTGattService primService = srvIter.next(); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d] Service UUID %s\n", i, primService.getUUID()); + BTUtils.fprintf_td(System.err, " [%02d] %s\n", i, primService.toString()); + } + int j=0; + final List<BTGattChar> serviceCharacteristics = primService.getChars(); + for(final Iterator<BTGattChar> charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) { + final BTGattChar serviceChar = charIter.next(); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic: UUID %s\n", i, j, serviceChar.getUUID()); + BTUtils.fprintf_td(System.err, " [%02d.%02d] %s\n", i, j, serviceChar.toString()); + } + final GattCharPropertySet properties = serviceChar.getProperties(); + if( properties.isSet(GattCharPropertySet.Type.Read) ) { + final byte[] value = serviceChar.readValue(); + final String svalue = BTUtils.decodeUTF8String(value, 0, value.length); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d] value: %s ('%s')\n", i, j, BTUtils.bytesHexString(value, 0, -1, true), svalue); + } + } + int k=0; + final List<BTGattDesc> charDescList = serviceChar.getDescriptors(); + for(final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) { + final BTGattDesc charDesc = descIter.next(); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d.%02d] Descriptor: UUID %s\n", i, j, k, charDesc.getUUID()); + BTUtils.fprintf_td(System.err, " [%02d.%02d.%02d] %s\n", i, j, k, charDesc.toString()); + } + } + final boolean cccdEnableResult[] = { false, false }; + if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) { + // ClientCharConfigDescriptor (CCD) is available + final boolean clAdded = null != serviceChar.addCharListener( new MyGATTEventListener(i, j) ); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n", + i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded); + BTUtils.fprintf_td(System.err, "\n"); + } + } + } + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "\n"); + } + } + } catch( final Exception ex) { + BTUtils.println(System.err, "****** Client Processing Ready Device: Exception.2 caught for " + device.toString() + ": "+ex.getMessage()); + ex.printStackTrace(); + } + + { + final long t0 = BTUtils.currentTimeMillis(); + boolean timeout = false; + do { + success = completedGATTCommands.get() >= 1 && ( notificationsReceived.get() >= 2 || indicationsReceived.get() >= 2 ); + if( !success ) { + timeout = 3000 < ( BTUtils.currentTimeMillis() - t0 ); // 3s timeout + if( !timeout ) { + try { Thread.sleep(17); } catch (final InterruptedException e) { } + } + } + } while( !success && !timeout ); + } + + { + // Tell server we have successfully completed the test. + final BTGattCmd cmd = new BTGattCmd(device, "FinalHandshake", null /* service_uuid */, DBTConstants.CommandUUID, DBTConstants.ResponseUUID); + cmd.setVerbose(true); + final boolean cmd_resolved = cmd.isResolved(); + BTUtils.println(System.err, "Client FinalCommand test: "+cmd.toString()+", resolved "+cmd_resolved); + final byte[] cmd_data = success ? DBTConstants.SuccessHandshakeCommandData : DBTConstants.FailHandshakeCommandData; + final HCIStatusCode cmd_res = cmd.send(true /* prefNoAck */, cmd_data, 3000 /* timeoutMS */); + if( HCIStatusCode.SUCCESS == cmd_res ) { + final byte[] resp = cmd.getResponse(); + if( Arrays.equals(cmd_data, resp) ) { + BTUtils.fprintf_td(System.err, "Client Success: %s -> %s (echo response)\n", cmd.toString(), BTUtils.bytesHexString(resp, 0, resp.length, true /* lsb */)); + } else { + BTUtils.fprintf_td(System.err, "Client Failure: %s -> %s (different response)\n", cmd.toString(), BTUtils.bytesHexString(resp, 0, resp.length, true /* lsb */)); + success = false; + } + } else { + BTUtils.fprintf_td(System.err, "Client Failure: %s -> %s\n", cmd.toString(), cmd_res.toString()); + success = false; + } + cmd.close(); + } + } catch (final Throwable t ) { + BTUtils.println(System.err, "****** Client Processing Ready Device: Exception.2 caught for " + device.toString() + ": "+t.getMessage()); + t.printStackTrace(); + } + + BTUtils.println(System.err, "****** Client Processing Ready Device: End-1: Success " + success + + " on " + device.toString() + "; devInProc "+BTDeviceRegistry.getProcessingDeviceCount()); + + if( DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_DISCONNECTED == discoveryPolicy ) { + device.getAdapter().removeDevicePausingDiscovery(device); + } + + BTUtils.println(System.err, "****** Client Processing Ready Device: End-2: Success " + success + + " on " + device.toString() + "; devInProc "+BTDeviceRegistry.getProcessingDeviceCount()); + device.removeAllCharListener(); + + if( !KEEP_CONNECTED ) { + if( REMOVE_DEVICE ) { + device.remove(); + } else { + device.disconnect(); + } + } + + completedMeasurementsTotal.addAndGet(1); + if( success ) { + completedMeasurementsSuccess.addAndGet(1); + } + if( 0 < measurementsLeft.get() ) { + measurementsLeft.decrementAndGet(); + } + BTUtils.println(System.err, "****** Client Processing Ready Device: Success "+success+ + "; Measurements completed "+completedMeasurementsSuccess.get()+ + ", left "+measurementsLeft.get()+ + "; Received notitifications "+notificationsReceived.get()+", indications "+indicationsReceived.get()+ + "; Completed GATT commands "+completedGATTCommands.get()+ + ": "+device.getAddressAndType()); + } + + private void removeDevice(final BTDevice device) { + BTUtils.println(System.err, "****** Client Remove Device: removing: "+device.getAddressAndType()); + + if( REMOVE_DEVICE ) { + device.remove(); + } + } + + 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 + static final boolean filter_dup = true; // default value + + @Override + public HCIStatusCode startDiscovery(final String msg) { + resetLastProcessingStats(); + + final HCIStatusCode status = clientAdapter.startDiscovery( discoveryPolicy, le_scan_active, le_scan_interval, le_scan_window, filter_policy, filter_dup ); + BTUtils.println(System.err, "****** Client Start discovery ("+msg+") result: "+status); + return status; + } + + @Override + public HCIStatusCode stopDiscovery(final String msg) { + final HCIStatusCode status = clientAdapter.stopDiscovery(); + BTUtils.println(System.err, "****** Client Stop discovery ("+msg+") result: "+status); + return status; + } + + @Override + public void close(final String msg) { + BTUtils.println(System.err, "****** Client Close: "+msg); + clientAdapter.stopDiscovery(); + clientAdapter.removeStatusListener( myAdapterStatusListener ); + } + + @Override + public boolean initAdapter(final BTAdapter adapter) { + if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) { + BTUtils.fprintf_td(System.err, "initClientAdapter: Adapter not selected: %s\n", adapter.toString()); + return false; + } + adapterName = adapterName + "-" + adapter.getAddressAndType().address.toString().replace(":", ""); + + // Initialize with defaults and power-on + if( !adapter.isInitialized() ) { + final HCIStatusCode status = adapter.initialize( btMode ); + if( HCIStatusCode.SUCCESS != status ) { + BTUtils.fprintf_td(System.err, "initClientAdapter: Adapter initialization failed: %s: %s\n", + status.toString(), adapter.toString()); + return false; + } + } else if( !adapter.setPowered( true ) ) { + BTUtils.fprintf_td(System.err, "initClientAdapter: Already initialized adapter power-on failed:: %s\n", adapter.toString()); + return false; + } + // adapter is powered-on + BTUtils.fprintf_td(System.err, "initClientAdapter.1: %s\n", adapter.toString()); + + if( adapter.setPowered(false) ) { + final HCIStatusCode status = adapter.setName(adapterName, adapterShortName); + if( HCIStatusCode.SUCCESS == status ) { + BTUtils.fprintf_td(System.err, "initClientAdapter: setLocalName OK: %s\n", adapter.toString()); + } else { + BTUtils.fprintf_td(System.err, "initClientAdapter: setLocalName failed: %s\n", adapter.toString()); + return false; + } + if( !adapter.setPowered( true ) ) { + BTUtils.fprintf_td(System.err, "initClientAdapter: setPower.2 on failed: %s\n", adapter.toString()); + return false; + } + } else { + BTUtils.fprintf_td(System.err, "initClientAdapter: setPowered.2 off failed: %s\n", adapter.toString()); + } + // adapter is powered-on + BTUtils.println(System.err, "initClientAdapter.2: "+adapter.toString()); + + { + final LE_Features le_feats = adapter.getLEFeatures(); + BTUtils.fprintf_td(System.err, "initClientAdapter: LE_Features %s\n", le_feats.toString()); + } + if( adapter.getBTMajorVersion() > 4 ) { + // BT5 specific + final LE_PHYs Tx = new LE_PHYs(LE_PHYs.PHY.LE_2M); + final LE_PHYs Rx = new LE_PHYs(LE_PHYs.PHY.LE_2M); + + final HCIStatusCode res = adapter.setDefaultLE_PHY(Tx, Rx); + BTUtils.fprintf_td(System.err, "initClientAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n", + res.toString(), Tx.toString(), Rx.toString()); + } + adapter.addStatusListener( myAdapterStatusListener ); + + return true; + } +} diff --git a/trial/java/trial/org/direct_bt/DBTClientServer1x.java b/trial/java/trial/org/direct_bt/DBTClientServer1x.java index 42507267..460e6ece 100644 --- a/trial/java/trial/org/direct_bt/DBTClientServer1x.java +++ b/trial/java/trial/org/direct_bt/DBTClientServer1x.java @@ -66,8 +66,21 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { final void test8x_fullCycle(final long timeout_value, final String suffix, final int protocolSessionCount, final boolean server_client_order, - final boolean serverSC, final BTSecurityLevel secLevelServer, final boolean serverShallHaveKeys, - final BTSecurityLevel secLevelClient, final boolean clientShallHaveKeys) + final boolean serverSC, final BTSecurityLevel secLevelServer, final ExpectedPairing serverExpPairing, + final BTSecurityLevel secLevelClient, final ExpectedPairing clientExpPairing) + { + final DBTServer00 server = new DBTServer00("S-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL, serverSC, secLevelServer); + final DBTClient00 client = new DBTClient00("C-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL); + test8x_fullCycle(timeout_value, + suffix, protocolSessionCount, server_client_order, + server, secLevelServer, serverExpPairing, + client, secLevelClient, clientExpPairing); + } + + final void test8x_fullCycle(final long timeout_value, + final String suffix, final int protocolSessionCount, final boolean server_client_order, + final DBTServerTest server, final BTSecurityLevel secLevelServer, final ExpectedPairing serverExpPairing, + final DBTClientTest client, final BTSecurityLevel secLevelClient, final ExpectedPairing clientExpPairing) { final long t0 = BTUtils.currentTimeMillis(); @@ -89,10 +102,12 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { 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.servingProtocolSessionsLeft.set(protocolSessionCount); + server.setProtocolSessionsLeft( protocolSessionCount ); - final DBTClient00 client = new DBTClient00("C-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL); + client.setProtocolSessionsLeft( protocolSessionCount ); + client.setKeepConnected( false ); // default, auto-disconnect after work is done + client.setRemoveDevice( false ); // default, test side-effects + client.setDiscoveryPolicy( DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_DISCONNECTED ); final DBTEndpoint.ChangedAdapterSetListener myChangedAdapterSetListener = DBTEndpoint.initChangedAdapterSetListener(manager, server_client_order ? Arrays.asList(server, client) : Arrays.asList(client, server)); @@ -103,10 +118,6 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { final 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.set(protocolSessionCount); - client.discoveryPolicy = DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_DISCONNECTED; synchronized( mtx_sync ) { lastCompletedDevice = null; @@ -146,12 +157,12 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { boolean max_connections_hit = false; do { synchronized( mtx_sync ) { - done = ! ( protocolSessionCount > server.servedProtocolSessionsTotal.get() || - protocolSessionCount > client.completedMeasurements.get() || + done = ! ( protocolSessionCount > server.getProtocolSessionsDoneSuccess() || + protocolSessionCount > client.getProtocolSessionsDoneSuccess() || null == lastCompletedDevice || lastCompletedDevice.getConnected() ); } - max_connections_hit = ( protocolSessionCount * DBTConstants.max_connections_per_session ) <= server.disconnectCount.get(); + max_connections_hit = ( protocolSessionCount * DBTConstants.max_connections_per_session ) <= server.getDisconnectCount(); test_duration = BTUtils.currentTimeMillis() - t0; timeout = 0 < timeout_value && timeout_value <= test_duration + 500; // let's timeout here before our timeout timer if( !done && !max_connections_hit && !timeout ) { @@ -164,23 +175,25 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { BTUtils.fprintf_td(System.err, "****** Test Stats: duration %d ms, timeout[hit %b, value %d ms], max_connections hit %b\n", test_duration, timeout, timeout_value, max_connections_hit); BTUtils.fprintf_td(System.err, " Server ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", - server.servedProtocolSessionsSuccess.get(), server.servedProtocolSessionsTotal.get(), protocolSessionCount, - server.disconnectCount.get(), ( protocolSessionCount * DBTConstants.max_connections_per_session )); - BTUtils.fprintf_td(System.err, " Client ProtocolSessions success %d \n", - client.completedMeasurements.get()); + server.getProtocolSessionsDoneSuccess(), server.getProtocolSessionsDoneTotal(), protocolSessionCount, + server.getDisconnectCount(), ( protocolSessionCount * DBTConstants.max_connections_per_session )); + BTUtils.fprintf_td(System.err, " Client ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", + client.getProtocolSessionsDoneSuccess(), client.getProtocolSessionsDoneTotal(), protocolSessionCount, + client.getDisconnectCount(), ( protocolSessionCount * DBTConstants.max_connections_per_session )); BTUtils.fprintf_td(System.err, "\n\n"); Assert.assertFalse( max_connections_hit ); Assert.assertFalse( timeout ); synchronized( mtx_sync ) { - Assert.assertEquals(protocolSessionCount, server.servedProtocolSessionsTotal.get()); - Assert.assertEquals(protocolSessionCount, server.servedProtocolSessionsSuccess.get()); - Assert.assertEquals(protocolSessionCount, client.completedMeasurements.get()); + Assert.assertTrue(protocolSessionCount <= server.getProtocolSessionsDoneTotal()); + Assert.assertEquals(protocolSessionCount, server.getProtocolSessionsDoneSuccess()); + Assert.assertTrue(protocolSessionCount <= client.getProtocolSessionsDoneTotal()); + Assert.assertEquals(protocolSessionCount, client.getProtocolSessionsDoneSuccess()); Assert.assertNotNull(lastCompletedDevice); Assert.assertNotNull(lastCompletedDeviceEIR); Assert.assertFalse(lastCompletedDevice.getConnected()); - Assert.assertTrue( ( 1 * DBTConstants.max_connections_per_session ) > server.disconnectCount.get() ); + Assert.assertTrue( ( 1 * DBTConstants.max_connections_per_session ) > server.getDisconnectCount() ); } // @@ -203,15 +216,19 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { final BTSecurityLevel clientKeysSecLevel = clientKeys.getSecLevel(); Assert.assertEquals(secLevelClient, clientKeysSecLevel); { - if( clientShallHaveKeys ) { + if( ExpectedPairing.PREPAIRED == clientExpPairing && BTSecurityLevel.NONE.value < secLevelClient.value ) { // Using encryption: pre-paired Assert.assertEquals(PairingMode.PRE_PAIRED, lastCompletedDevicePairingMode); Assert.assertEquals(BTSecurityLevel.ENC_ONLY, lastCompletedDeviceSecurityLevel); // pre-paired fixed level, no auth - } else if( BTSecurityLevel.NONE.value < secLevelClient.value ) { + } else if( ExpectedPairing.NEW_PAIRING == clientExpPairing && BTSecurityLevel.NONE.value < secLevelClient.value ) { // Using encryption: Newly paired Assert.assertNotEquals(PairingMode.PRE_PAIRED, lastCompletedDevicePairingMode); Assert.assertTrue("PairingMode client "+lastCompletedDevicePairingMode+" not > NONE", PairingMode.NONE.value < lastCompletedDevicePairingMode.value); Assert.assertTrue("SecurityLevel client "+lastCompletedDeviceSecurityLevel+" not >= "+secLevelClient, secLevelClient.value <= lastCompletedDeviceSecurityLevel.value); + } else if( ExpectedPairing.DONT_CARE == clientExpPairing && BTSecurityLevel.NONE.value < secLevelClient.value ) { + // Any encryption, any pairing + Assert.assertTrue("PairingMode client "+lastCompletedDevicePairingMode+" not > NONE", PairingMode.NONE.value < lastCompletedDevicePairingMode.value); + Assert.assertTrue("SecurityLevel client "+lastCompletedDeviceSecurityLevel+" not >= "+secLevelClient, secLevelClient.value <= lastCompletedDeviceSecurityLevel.value); } else { // No encryption: No pairing Assert.assertEquals(PairingMode.NONE, lastCompletedDevicePairingMode); diff --git a/trial/java/trial/org/direct_bt/DBTClientTest.java b/trial/java/trial/org/direct_bt/DBTClientTest.java index e862d0cb..8b5a33a0 100644 --- a/trial/java/trial/org/direct_bt/DBTClientTest.java +++ b/trial/java/trial/org/direct_bt/DBTClientTest.java @@ -25,11 +25,36 @@ package trial.org.direct_bt; import org.direct_bt.BTAdapter; import org.direct_bt.BTRole; +import org.direct_bt.DiscoveryPolicy; import org.direct_bt.HCIStatusCode; import org.junit.Assert; public interface DBTClientTest extends DBTEndpoint { + /** + * Set DiscoveryPolicy + * + * Default is {@link DiscoveryPolicy#PAUSE_CONNECTED_UNTIL_READY} + */ + void setDiscoveryPolicy(final DiscoveryPolicy v); + + /** + * Set disconnect after processing. + * + * Default is `false`. + */ + void setKeepConnected(final boolean v); + + /** + * Set remove device when disconnecting. + * + * This removes the device from all instances within adapter + * and hence all potential side-effects of the current instance. + * + * Default is `false`, since it is good to test whether such side-effects exists. + */ + void setRemoveDevice(final boolean v); + HCIStatusCode startDiscovery(String msg); HCIStatusCode stopDiscovery(String msg); diff --git a/trial/java/trial/org/direct_bt/DBTEndpoint.java b/trial/java/trial/org/direct_bt/DBTEndpoint.java index 81ef9db9..6ea07e18 100644 --- a/trial/java/trial/org/direct_bt/DBTEndpoint.java +++ b/trial/java/trial/org/direct_bt/DBTEndpoint.java @@ -25,6 +25,7 @@ package trial.org.direct_bt; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; import org.direct_bt.BTAdapter; import org.direct_bt.BTManager; @@ -58,6 +59,12 @@ public interface DBTEndpoint { void close(final String msg); + void setProtocolSessionsLeft(final int v); + int getProtocolSessionsLeft(); + int getProtocolSessionsDoneTotal(); + int getProtocolSessionsDoneSuccess(); + int getDisconnectCount(); + /** * Initialize the given adapter for this endpoint. * diff --git a/trial/java/trial/org/direct_bt/DBTServer00.java b/trial/java/trial/org/direct_bt/DBTServer00.java index 8bf7dc04..62483ee4 100644 --- a/trial/java/trial/org/direct_bt/DBTServer00.java +++ b/trial/java/trial/org/direct_bt/DBTServer00.java @@ -65,24 +65,25 @@ import org.jau.net.EUI48; */ public class DBTServer00 implements DBTServerTest { final boolean GATT_VERBOSE = false; - boolean SHOW_UPDATE_EVENTS = false; - - final String adapterShortName = "TDev1Srv"; - String adapterName = "TestDev1_Srv"; - EUI48 useAdapter = EUI48.ALL_DEVICE; - BTMode btMode = BTMode.DUAL; - boolean use_SC = true; - BTSecurityLevel adapterSecurityLevel = BTSecurityLevel.UNSET; + private final String adapterShortName = "TDev1Srv"; + + private final boolean SHOW_UPDATE_EVENTS = false; + + private String adapterName = "TestDev1_Srv"; + private EUI48 useAdapter = EUI48.ALL_DEVICE; + private BTMode btMode = BTMode.DUAL; + private boolean use_SC = true; + private BTSecurityLevel adapterSecurityLevel = BTSecurityLevel.UNSET; private final MyAdapterStatusListener myAdapterStatusListener = new MyAdapterStatusListener(); private final MyGATTServerListener gattServerListener = new MyGATTServerListener(); - BTAdapter serverAdapter = null; + private BTAdapter serverAdapter = null; private final Object sync_lock = new Object(); private volatile BTDevice connectedDevice; - public AtomicInteger disconnectCount = new AtomicInteger(0); - public AtomicInteger servedProtocolSessionsTotal = new AtomicInteger(0); - public AtomicInteger servedProtocolSessionsSuccess = new AtomicInteger(0); - public AtomicInteger servingProtocolSessionsLeft = new AtomicInteger(1); + private final AtomicInteger disconnectCount = new AtomicInteger(0); + private final AtomicInteger servedProtocolSessionsTotal = new AtomicInteger(0); + private final AtomicInteger servedProtocolSessionsSuccess = new AtomicInteger(0); + private final AtomicInteger servingProtocolSessionsLeft = new AtomicInteger(1); public DBTServer00(final String adapterName, final EUI48 useAdapter, final BTMode btMode, final boolean use_SC, final BTSecurityLevel adapterSecurityLevel) { this.adapterName = adapterName; @@ -113,6 +114,27 @@ public class DBTServer00 implements DBTServerTest { @Override public BTAdapter getAdapter() { return serverAdapter; } + @Override + public void setProtocolSessionsLeft(final int v) { + servingProtocolSessionsLeft.set(v); + } + @Override + public int getProtocolSessionsLeft() { + return servingProtocolSessionsLeft.get(); + } + @Override + public int getProtocolSessionsDoneTotal() { + return servedProtocolSessionsTotal.get(); + } + @Override + public int getProtocolSessionsDoneSuccess() { + return servedProtocolSessionsSuccess.get(); + } + @Override + public int getDisconnectCount() { + return this.disconnectCount.get(); + } + private final void setDevice(final BTDevice cd) { synchronized( sync_lock ) { connectedDevice = cd; @@ -422,7 +444,7 @@ public class DBTServer00 implements DBTServerTest { } } - void sendResponse(final byte[] data) { + private void sendResponse(final byte[] data) { final BTDevice connectedDevice_ = getDevice(); if( null != connectedDevice_ && connectedDevice_.getConnected() ) { if( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) { diff --git a/trial/java/trial/org/direct_bt/DBTServer01.java b/trial/java/trial/org/direct_bt/DBTServer01.java new file mode 100644 index 00000000..54d316be --- /dev/null +++ b/trial/java/trial/org/direct_bt/DBTServer01.java @@ -0,0 +1,842 @@ +/** + * 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. + */ + +package trial.org.direct_bt; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import org.direct_bt.AdapterSettings; +import org.direct_bt.AdapterStatusListener; +import org.direct_bt.BDAddressAndType; +import org.direct_bt.BTMode; +import org.direct_bt.BTSecurityLevel; +import org.direct_bt.BTAdapter; +import org.direct_bt.BTDevice; +import org.direct_bt.BTDeviceRegistry; +import org.direct_bt.BTSecurityRegistry; +import org.direct_bt.BTUtils; +import org.direct_bt.DBGattChar; +import org.direct_bt.DBGattDesc; +import org.direct_bt.DBGattServer; +import org.direct_bt.DBGattService; +import org.direct_bt.DBGattValue; +import org.direct_bt.DiscoveryPolicy; +import org.direct_bt.EIRDataTypeSet; +import org.direct_bt.EInfoReport; +import org.direct_bt.GAPFlags; +import org.direct_bt.GattCharPropertySet; +import org.direct_bt.HCIStatusCode; +import org.direct_bt.LE_Features; +import org.direct_bt.LE_PHYs; +import org.direct_bt.PairingMode; +import org.direct_bt.SMPIOCapability; +import org.direct_bt.SMPPairingState; +import org.direct_bt.ScanType; +import org.jau.net.EUI48; + +import trial.org.direct_bt.DBTServer00.MyAdapterStatusListener; +import trial.org.direct_bt.DBTServer00.MyGATTServerListener; + +/** + * This peripheral BTRole::Slave test participant works with DBTClient00. + */ +public class DBTServer01 implements DBTServerTest { + final boolean GATT_VERBOSE = false; + private final String adapterShortName = "TDev1Srv"; + + private final boolean SHOW_UPDATE_EVENTS = false; + + private String adapterName = "TestDev1_Srv"; + private EUI48 useAdapter = EUI48.ALL_DEVICE; + private BTMode btMode = BTMode.DUAL; + private boolean use_SC = true; + private BTSecurityLevel adapterSecurityLevel = BTSecurityLevel.UNSET; + private final MyAdapterStatusListener myAdapterStatusListener = new MyAdapterStatusListener(); + private final MyGATTServerListener gattServerListener = new MyGATTServerListener(); + private BTAdapter serverAdapter = null; + private final Object sync_lock = new Object(); + private volatile BTDevice connectedDevice; + + private final AtomicInteger disconnectCount = new AtomicInteger(0); + private final AtomicInteger servedProtocolSessionsTotal = new AtomicInteger(0); + private final AtomicInteger servedProtocolSessionsSuccess = new AtomicInteger(0); + private final AtomicInteger servingProtocolSessionsLeft = new AtomicInteger(1); + + public DBTServer01(final String adapterName, final EUI48 useAdapter, final BTMode btMode, final boolean use_SC, final BTSecurityLevel adapterSecurityLevel) { + this.adapterName = adapterName; + this.useAdapter = useAdapter; + this.btMode = btMode; + this.use_SC = use_SC; + this.adapterSecurityLevel = adapterSecurityLevel; + + dbGattServer.addListener( gattServerListener ); + } + public DBTServer01(final String adapterName, final EUI48 useAdapter, final BTSecurityLevel adapterSecurityLevel) { + this(adapterName, useAdapter, BTMode.DUAL, true /* SC */, adapterSecurityLevel); + } + public DBTServer01(final String adapterName, final BTSecurityLevel adapterSecurityLevel) { + this(adapterName, EUI48.ALL_DEVICE, BTMode.DUAL, true /* SC */, adapterSecurityLevel); + } + + @Override + public String getName() { return adapterName; } + + @Override + public BTSecurityLevel getSecurityLevel() { return adapterSecurityLevel; } + + @Override + public void setAdapter(final BTAdapter serverAdapter) { + this.serverAdapter = serverAdapter; + } + @Override + public BTAdapter getAdapter() { return serverAdapter; } + + @Override + public void setProtocolSessionsLeft(final int v) { + servingProtocolSessionsLeft.set(v); + } + @Override + public int getProtocolSessionsLeft() { + return servingProtocolSessionsLeft.get(); + } + @Override + public int getProtocolSessionsDoneTotal() { + return servedProtocolSessionsTotal.get(); + } + @Override + public int getProtocolSessionsDoneSuccess() { + return servedProtocolSessionsSuccess.get(); + } + + @Override + public int getDisconnectCount() { + return this.disconnectCount.get(); + } + + private final void setDevice(final BTDevice cd) { + synchronized( sync_lock ) { + connectedDevice = cd; + } + } + + private final BTDevice getDevice() { + synchronized( sync_lock ) { + return connectedDevice; + } + } + + private boolean matches(final BTDevice device) { + final BTDevice d = getDevice(); + return null != d ? d.equals(device) : false; + } + + static Thread executeOffThread(final Runnable runobj, final String threadName, final boolean detach) { + final Thread t = new Thread( runobj, threadName ); + t.setDaemon( detach ); + t.start(); + return t; + } + static Thread executeOffThread(final Runnable runobj, final boolean detach) { + final Thread t = new Thread( runobj ); + t.setDaemon( detach ); + t.start(); + return t; + } + + static DBGattValue make_gvalue(final String name) { + final byte[] p = name.getBytes(StandardCharsets.UTF_8); + return new DBGattValue(p, p.length); + } + static DBGattValue make_gvalue(final String name, final int capacity) { + final byte[] p = name.getBytes(StandardCharsets.UTF_8); + return new DBGattValue(p, Math.max(capacity, p.length), capacity > p.length/* variable_length */); + } + static DBGattValue make_gvalue(final short v) { + final byte[] p = { (byte)0, (byte)0 }; + p[0] = (byte)(v); + p[1] = (byte)(v >> 8); + return new DBGattValue(p, p.length); + } + static DBGattValue make_gvalue(final int capacity, final int size) { + final byte[] p = new byte[size]; + return new DBGattValue(p, capacity, true /* variable_length */); + } + + // DBGattServerRef dbGattServer = std::make_shared<DBGattServer>( + private final DBGattServer dbGattServer = new DBGattServer( + /* services: */ + Arrays.asList( // DBGattService + new DBGattService ( true /* primary */, + DBGattService.UUID16.GENERIC_ACCESS /* type_ */, + Arrays.asList( // DBGattChar + new DBGattChar( DBGattChar.UUID16.DEVICE_NAME /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue(adapterName, 128) /* value */ ), + new DBGattChar( DBGattChar.UUID16.APPEARANCE /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue((short)0) /* value */ ) + ) ), + new DBGattService ( true /* primary */, + DBGattService.UUID16.DEVICE_INFORMATION /* type_ */, + Arrays.asList( // DBGattChar + new DBGattChar( DBGattChar.UUID16.MANUFACTURER_NAME_STRING /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue("Gothel Software") /* value */ ), + new DBGattChar( DBGattChar.UUID16.MODEL_NUMBER_STRING /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue("2.4.0-pre") /* value */ ), + new DBGattChar( DBGattChar.UUID16.SERIAL_NUMBER_STRING /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue("sn:0123456789") /* value */ ), + new DBGattChar( DBGattChar.UUID16.HARDWARE_REVISION_STRING /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue("hw:0123456789") /* value */ ), + new DBGattChar( DBGattChar.UUID16.FIRMWARE_REVISION_STRING /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue("fw:0123456789") /* value */ ), + new DBGattChar( DBGattChar.UUID16.SOFTWARE_REVISION_STRING /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + new ArrayList<DBGattDesc>(/* intentionally w/o Desc */ ), + make_gvalue("sw:0123456789") /* value */ ) + ) ), + new DBGattService ( true /* primary */, + DBTConstants.DataServiceUUID /* type_ */, + Arrays.asList( // DBGattChar + new DBGattChar( DBTConstants.StaticDataUUID /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Read), + Arrays.asList( // DBGattDesc + new DBGattDesc( DBGattDesc.UUID16.USER_DESC, make_gvalue("DATA_STATIC") ) + ), + make_gvalue("Proprietary Static Data 0x00010203") /* value */ ), + new DBGattChar( DBTConstants.CommandUUID /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.WriteNoAck).set(GattCharPropertySet.Type.WriteWithAck), + Arrays.asList( // DBGattDesc + new DBGattDesc( DBGattDesc.UUID16.USER_DESC, make_gvalue("COMMAND") ) + ), + make_gvalue(128, 64) /* value */ ), + new DBGattChar( DBTConstants.ResponseUUID /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Notify).set(GattCharPropertySet.Type.Indicate), + Arrays.asList( // DBGattDesc + new DBGattDesc( DBGattDesc.UUID16.USER_DESC, make_gvalue("RESPONSE") ), + DBGattDesc.createClientCharConfig() + ), + make_gvalue((short)0) /* value */ ), + new DBGattChar( DBTConstants.PulseDataUUID /* value_type_ */, + new GattCharPropertySet(GattCharPropertySet.Type.Notify).set(GattCharPropertySet.Type.Indicate), + Arrays.asList( // DBGattDesc + new DBGattDesc( DBGattDesc.UUID16.USER_DESC, make_gvalue("DATA_PULSE") ), + DBGattDesc.createClientCharConfig() + ), + make_gvalue("Synthethic Sensor 01") /* value */ ) + ) ) + ) ); + + + class MyAdapterStatusListener extends AdapterStatusListener { + @Override + public void adapterSettingsChanged(final BTAdapter adapter, final AdapterSettings oldmask, + final AdapterSettings newmask, final AdapterSettings changedmask, final long timestamp) { + final boolean initialSetting = oldmask.isEmpty(); + if( initialSetting ) { + BTUtils.println(System.err, "****** Server SETTINGS: "+oldmask+" -> "+newmask+", initial "+changedmask); + } else { + BTUtils.println(System.err, "****** Server SETTINGS: "+oldmask+" -> "+newmask+", changed "+changedmask); + } + BTUtils.println(System.err, "Server Status Adapter:"); + BTUtils.println(System.err, adapter.toString()); + } + + @Override + public void discoveringChanged(final BTAdapter adapter, final ScanType currentMeta, final ScanType changedType, final boolean changedEnabled, final DiscoveryPolicy policy, final long timestamp) { + BTUtils.println(System.err, "****** Server DISCOVERING: meta "+currentMeta+", changed["+changedType+", enabled "+changedEnabled+", policy "+policy+"] on "+adapter); + } + + @Override + public boolean deviceFound(final BTDevice device, final long timestamp) { + BTUtils.println(System.err, "****** Server FOUND__-1: NOP "+device.toString()); + return false; + } + + @Override + public void deviceUpdated(final BTDevice device, final EIRDataTypeSet updateMask, final long timestamp) { + if( SHOW_UPDATE_EVENTS ) { + BTUtils.println(System.err, "****** Server UPDATED: "+updateMask+" of "+device); + } + } + + @Override + public void deviceConnected(final BTDevice device, final short handle, final long timestamp) { + BTUtils.println(System.err, "****** Server CONNECTED (served "+disconnectCount.get()+", left "+servingProtocolSessionsLeft.get()+"): handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter()); + final boolean available = null == getDevice(); + if( available ) { + setDevice(device); + BTDeviceRegistry.addToProcessingDevices(device.getAddressAndType(), device.getName()); + } + } + + @Override + public void devicePairingState(final BTDevice device, final SMPPairingState state, final PairingMode mode, final long timestamp) { + BTUtils.println(System.err, "****** Server PAIRING_STATE: state "+state+", mode "+mode+": "+device); + switch( state ) { + case NONE: + // next: deviceReady(..) + break; + case FAILED: { + // next: deviceReady() or deviceDisconnected(..) + } break; + case REQUESTED_BY_RESPONDER: + // next: FEATURE_EXCHANGE_STARTED + break; + case FEATURE_EXCHANGE_STARTED: + // next: FEATURE_EXCHANGE_COMPLETED + break; + case FEATURE_EXCHANGE_COMPLETED: + // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION + break; + case PASSKEY_EXPECTED: { + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, ""); + if( null != sec && sec.getPairingPasskey() != BTSecurityRegistry.NO_PASSKEY ) { + executeOffThread( () -> { device.setPairingPasskey( sec.getPairingPasskey() ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */); + } else { + executeOffThread( () -> { device.setPairingPasskey( 0 ); }, "DBT-SetPasskey-"+device.getAddressAndType(), true /* detach */); + // 3s disconnect: executeOffThread( () -> { device.setPairingPasskeyNegative(); }, "DBT-SetPasskeyNegative-"+device.getAddressAndType(), true /* detach */); + } + // next: KEY_DISTRIBUTION or FAILED + } break; + case NUMERIC_COMPARE_EXPECTED: { + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getStartOf(device.getAddressAndType().address, ""); + if( null != sec ) { + executeOffThread( () -> { device.setPairingNumericComparison( sec.getPairingNumericComparison() ); }, "DBT-SetNumericComp-"+device.getAddressAndType(), true /* detach */); + } else { + executeOffThread( () -> { device.setPairingNumericComparison( false ); }, "DBT-SetNumericCompFalse-"+device.getAddressAndType(), true /* detach */); + } + // next: KEY_DISTRIBUTION or FAILED + } break; + case OOB_EXPECTED: + // FIXME: ABORT + break; + case KEY_DISTRIBUTION: + // next: COMPLETED or FAILED + break; + case COMPLETED: + // next: deviceReady(..) + break; + default: // nop + break; + } + } + + @Override + public void deviceReady(final BTDevice device, final long timestamp) { + BTUtils.println(System.err, "****** Server READY-1: NOP " + device.toString()); + } + + @Override + public void deviceDisconnected(final BTDevice device, final HCIStatusCode reason, final short handle, final long timestamp) { + BTUtils.println(System.err, "****** Server DISCONNECTED (served "+(1+disconnectCount.get())+", left "+servingProtocolSessionsLeft.get()+"): Reason "+reason+", old handle 0x"+Integer.toHexString(handle)+": "+device+" on "+device.getAdapter()); + final boolean match = matches(device); + if( match ) { + setDevice(null); + } + executeOffThread( () -> { processDisconnectedDevice(device); }, + "Server DBT-Disconnected-"+device.getAdapter().getAddressAndType(), true /* detach */); + } + + @Override + public String toString() { + return "Server AdapterStatusListener[user, per-adapter]"; + } + }; + + class MyGATTServerListener extends DBGattServer.Listener implements AutoCloseable { + private final Thread pulseSenderThread; + private volatile boolean stopPulseSenderFlag = false; + + private volatile short handlePulseDataNotify = 0; + private volatile short handlePulseDataIndicate = 0; + private volatile short handleResponseDataNotify = 0; + private volatile short handleResponseDataIndicate = 0; + + private int usedMTU = 23; // BTGattHandler::number(BTGattHandler::Defaults::MIN_ATT_MTU); + + private void clear() { + synchronized( sync_lock ) { + handlePulseDataNotify = 0; + handlePulseDataIndicate = 0; + handleResponseDataNotify = 0; + handleResponseDataIndicate = 0; + + dbGattServer.resetGattClientCharConfig(DBTConstants.DataServiceUUID, DBTConstants.PulseDataUUID); + dbGattServer.resetGattClientCharConfig(DBTConstants.DataServiceUUID, DBTConstants.ResponseUUID); + } + } + + private boolean shallStopPulseSender() { + synchronized( sync_lock ) { + return stopPulseSenderFlag; + } + } + private void pulseSender() { + { + final BTDevice connectedDevice_ = getDevice(); + final String connectedDeviceStr = null != connectedDevice_ ? connectedDevice_.toString() : "n/a"; + BTUtils.fprintf_td(System.err, "****** Server GATT::PULSE Start %s\n", connectedDeviceStr); + } + while( !shallStopPulseSender() ) { + final BTDevice connectedDevice_ = getDevice(); + if( null != connectedDevice_ && connectedDevice_.getConnected() ) { + if( 0 != handlePulseDataNotify || 0 != handlePulseDataIndicate ) { + final String data = String.format("Dynamic Data Example. Elapsed Milliseconds: %,9d", BTUtils.elapsedTimeMillis()); + final byte[] v = data.getBytes(StandardCharsets.UTF_8); + if( 0 != handlePulseDataNotify ) { + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "****** Server GATT::sendNotification: PULSE to %s\n", connectedDevice_.toString()); + } + connectedDevice_.sendNotification(handlePulseDataNotify, v); + } + if( 0 != handlePulseDataIndicate ) { + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "****** Server GATT::sendIndication: PULSE to %s\n", connectedDevice_.toString()); + } + connectedDevice_.sendIndication(handlePulseDataIndicate, v); + } + } + } + if( !shallStopPulseSender() ) { + try { + Thread.sleep(100); // 100ms + } catch (final InterruptedException e) { } + } + } + { + final BTDevice connectedDevice_ = getDevice(); + final String connectedDeviceStr = null != connectedDevice_ ? connectedDevice_.toString() : "n/a"; + BTUtils.fprintf_td(System.err, "****** Server GATT::PULSE End %s\n", connectedDeviceStr); + } + } + + private void sendResponse(final byte[] data) { + final BTDevice connectedDevice_ = getDevice(); + if( null != connectedDevice_ && connectedDevice_.getConnected() ) { + if( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) { + if( 0 != handleResponseDataNotify ) { + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "****** Server GATT::sendNotification: %s to %s\n", + BTUtils.bytesHexString(data, 0, data.length, true /* lsb */), connectedDevice_.toString()); + } + connectedDevice_.sendNotification(handleResponseDataNotify, data); + } + if( 0 != handleResponseDataIndicate ) { + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "****** Server GATT::sendIndication: %s to %s\n", + BTUtils.bytesHexString(data, 0, data.length, true /* lsb */), connectedDevice_.toString()); + } + connectedDevice_.sendIndication(handleResponseDataIndicate, data); + } + } + } + } + + boolean onceDisconnect = true; + + private void disconnectDevice() { + try { + Thread.sleep(300); + } catch (final InterruptedException e) { } + + final BTDevice connectedDevice_ = getDevice(); + BTUtils.fprintf_td(System.err, "****** Server GATT::disconnectDevice(sessions [%d ok / %d total], left %d): client %s\n", + servedProtocolSessionsSuccess.get(), servedProtocolSessionsTotal.get(), servingProtocolSessionsLeft.get(), + connectedDevice_.toString()); + connectedDevice_.disconnect(); + } + + public MyGATTServerListener() { + pulseSenderThread = executeOffThread( () -> { pulseSender(); }, "GattServer-PulseSender", false /* detach */); + } + + @Override + public void close() { + synchronized( sync_lock ) { + clear(); + stopPulseSenderFlag = true; + } + try { + pulseSenderThread.join(1000); + } catch (final InterruptedException e) { } + super.close(); + } + + @Override + public void connected(final BTDevice device, final int initialMTU) { + final boolean match = matches(device); + BTUtils.fprintf_td(System.err, "****** Server GATT::connected(match %b): initMTU %d, %s\n", + match, initialMTU, device.toString()); + if( match ) { + synchronized( sync_lock ) { + usedMTU = initialMTU; + } + } + } + + @Override + public void disconnected(final BTDevice device) { + final boolean match = matches(device); + BTUtils.fprintf_td(System.err, "****** Server GATT::disconnected(match %b): %s\n", match, device.toString()); + if( match ) { + clear(); + } + } + + @Override + public void mtuChanged(final BTDevice device, final int mtu) { + final boolean match = matches(device); + final int usedMTU_old = usedMTU; + if( match ) { + synchronized( sync_lock ) { + usedMTU = mtu; + } + } + BTUtils.fprintf_td(System.err, "****** Server GATT::mtuChanged(match %b, served %d, left %d): %d -> %d, %s\n", + match, servedProtocolSessionsTotal.get(), servingProtocolSessionsLeft.get(), + match ? usedMTU_old : 0, mtu, device.toString()); + if( onceDisconnect ) { + onceDisconnect = false; + executeOffThread( () -> { disconnectDevice(); }, "GattServer-DisconnectDevice", true /* detach */); + } + } + + @Override + public boolean readCharValue(final BTDevice device, final DBGattService s, final DBGattChar c) { + final boolean match = matches(device); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "****** Server GATT::readCharValue(match %b): to %s, from\n %s\n %s\n", + match, device.toString(), s.toString(), c.toString()); + } + return match; + } + + @Override + public boolean readDescValue(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d) { + final boolean match = matches(device); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "****** Server GATT::readDescValue(match %b): to %s, from\n %s\n %s\n %s\n", + match, device.toString(), s.toString(), c.toString(), d.toString()); + } + return match; + } + + @Override + public boolean writeCharValue(final BTDevice device, final DBGattService s, final DBGattChar c, final byte[] value, final int value_offset) { + final boolean match = matches(device); + if( GATT_VERBOSE ) { + final String value_s = BTUtils.bytesHexString(value, 0, value.length, true /* lsbFirst */)+ + " '"+BTUtils.decodeUTF8String(value, 0, value.length)+"'"; + BTUtils.fprintf_td(System.err, "****** Server GATT::writeCharValue(match %b): %s @ %d from %s, to\n %s\n %s\n", + match, value_s, value_offset, + device.toString(), s.toString(), c.toString()); + } + return match; + } + + @Override + public void writeCharValueDone(final BTDevice device, final DBGattService s, final DBGattChar c) { + final boolean match = matches(device); + final DBGattValue value = c.getValue(); + final byte[] data = value.data(); + boolean isFinalHandshake = false; + boolean isFinalHandshakeSuccess = false; + + if( match && + c.getValueType().equals( DBTConstants.CommandUUID ) && + ( 0 != handleResponseDataNotify || 0 != handleResponseDataIndicate ) ) + { + isFinalHandshakeSuccess = Arrays.equals(DBTConstants.SuccessHandshakeCommandData, data); + isFinalHandshake = isFinalHandshakeSuccess || + Arrays.equals(DBTConstants.FailHandshakeCommandData, data); + + if( isFinalHandshake ) { + if( isFinalHandshakeSuccess ) { + servedProtocolSessionsSuccess.addAndGet(1); + } + servedProtocolSessionsTotal.addAndGet(1); // we assume this to be server, i.e. connected, SMP done and GATT action .. + if( servingProtocolSessionsLeft.get() > 0 ) { + servingProtocolSessionsLeft.decrementAndGet(); + } + } + executeOffThread( () -> { sendResponse( data ); }, true /* detach */); + } + if( GATT_VERBOSE || isFinalHandshake ) { + BTUtils.fprintf_td(System.err, "****** Server GATT::writeCharValueDone(match %b, finalCmd %b, sessions [%d ok / %d total], left %d): From %s, to\n %s\n %s\n Char-Value: %s\n", + match, isFinalHandshake, servedProtocolSessionsSuccess.get(), servedProtocolSessionsTotal.get(), servingProtocolSessionsLeft.get(), + device.toString(), s.toString(), c.toString(), value.toString()); + } + } + + @Override + public boolean writeDescValue(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d, + final byte[] value, final int value_offset) + { + final boolean match = matches(device); + if( GATT_VERBOSE ) { + final String value_s = BTUtils.bytesHexString(value, 0, value.length, true /* lsbFirst */)+ + " '"+BTUtils.decodeUTF8String(value, 0, value.length)+"'"; + BTUtils.fprintf_td(System.err, "****** Server GATT::writeDescValue(match %b): %s @ %d from %s\n %s\n %s\n %s\n", + match, value_s, value_offset, + device.toString(), s.toString(), c.toString(), d.toString()); + } + return match; + } + + @Override + public void writeDescValueDone(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d) { + if( GATT_VERBOSE ) { + final boolean match = matches(device); + final DBGattValue value = d.getValue(); + BTUtils.fprintf_td(System.err, "****** Server GATT::writeDescValueDone(match %b): From %s\n %s\n %s\n %s\n Desc-Value: %s\n", + match, device.toString(), s.toString(), c.toString(), d.toString(), value.toString()); + } + } + + @Override + public void clientCharConfigChanged(final BTDevice device, final DBGattService s, final DBGattChar c, final DBGattDesc d, + final boolean notificationEnabled, final boolean indicationEnabled) + { + final boolean match = matches(device); + if( GATT_VERBOSE ) { + final DBGattValue value = d.getValue(); + BTUtils.fprintf_td(System.err, "****** Server GATT::clientCharConfigChanged(match %b): notify %b, indicate %b from %s\n %s\n %s\n %s\n Desc-Value: %s\n", + match, notificationEnabled, indicationEnabled, + device.toString(), s.toString(), c.toString(), d.toString(), value.toString()); + } + if( match ) { + final String value_type = c.getValueType(); + final short value_handle = c.getValueHandle(); + if( value_type.equals( DBTConstants.PulseDataUUID ) ) { + synchronized( sync_lock ) { + handlePulseDataNotify = notificationEnabled ? value_handle : 0; + handlePulseDataIndicate = indicationEnabled ? value_handle : 0; + } + } else if( value_type.equals( DBTConstants.ResponseUUID ) ) { + synchronized( sync_lock ) { + handleResponseDataNotify = notificationEnabled ? value_handle : 0; + handleResponseDataIndicate = indicationEnabled ? value_handle : 0; + } + } + } + } + } + static final short adv_interval_min=(short)160; // x0.625 = 100ms + static final short adv_interval_max=(short)480; // x0.625 = 300ms + static final byte adv_type=(byte)0; // AD_PDU_Type::ADV_IND; + static final byte adv_chan_map=(byte)0x07; + static final byte filter_policy=(byte)0x00; + + @Override + public HCIStatusCode stop(final String msg) { + BTUtils.println(System.err, "****** Server Stop.0: "+msg); + final HCIStatusCode res = stopAdvertising(msg); + final BTDevice connectedDevice_ = getDevice(); + if( null != connectedDevice_ ) { + setDevice(null); + connectedDevice_.disconnect(); + } + gattServerListener.clear(); + BTUtils.println(System.err, "****** Server Stop.X: "+msg); + return res; + } + + @Override + public void close(final String msg) { + BTUtils.println(System.err, "****** Server Close.0: "+msg); + stop(msg); + gattServerListener.close(); + // dbGattServer.close(); // keep alive + serverAdapter.removeStatusListener( myAdapterStatusListener ); + BTUtils.println(System.err, "****** Server Close.X: "+msg); + } + + private HCIStatusCode stopAdvertising(final String msg) { + final HCIStatusCode status = serverAdapter.stopAdvertising(); + BTUtils.println(System.err, "****** Server Stop advertising ("+msg+") result: "+status+": "+serverAdapter.toString()); + return status; + } + + @Override + public HCIStatusCode startAdvertising(final String msg) { + final EInfoReport eir = new EInfoReport(); + final EIRDataTypeSet adv_mask = new EIRDataTypeSet(); + final EIRDataTypeSet scanrsp_mask = new EIRDataTypeSet(); + + adv_mask.set(EIRDataTypeSet.DataType.FLAGS); + adv_mask.set(EIRDataTypeSet.DataType.SERVICE_UUID); + + scanrsp_mask.set(EIRDataTypeSet.DataType.NAME); + scanrsp_mask.set(EIRDataTypeSet.DataType.CONN_IVAL); + + eir.addFlag(GAPFlags.Bit.LE_Gen_Disc); + eir.addFlag(GAPFlags.Bit.BREDR_UNSUP); + + eir.addService(DBTConstants.DataServiceUUID); + eir.setServicesComplete(false); + + eir.setName(serverAdapter.getName()); + eir.setConnInterval((short)8, (short)12); // 10ms - 15ms + + final DBGattChar gattDevNameChar = dbGattServer.findGattChar(DBGattService.UUID16.GENERIC_ACCESS, DBGattChar.UUID16.DEVICE_NAME); + if( null != gattDevNameChar ) { + final byte[] aname_bytes = serverAdapter.getName().getBytes(StandardCharsets.UTF_8); + gattDevNameChar.setValue(aname_bytes, 0, aname_bytes.length, 0); + } + + BTUtils.println(System.err, "****** Server Start advertising ("+msg+"): EIR "+eir.toString()); + BTUtils.println(System.err, "****** Server Start advertising ("+msg+"): adv "+adv_mask.toString()+", scanrsp "+scanrsp_mask.toString()); + + final HCIStatusCode status = serverAdapter.startAdvertising(dbGattServer, eir, adv_mask, scanrsp_mask, + adv_interval_min, adv_interval_max, + adv_type, adv_chan_map, filter_policy); + BTUtils.println(System.err, "****** Server Start advertising ("+msg+") result: "+status+": "+serverAdapter.toString()); + if( GATT_VERBOSE ) { + BTUtils.println(System.err, dbGattServer.toFullString()); + } + return status; + } + + private void processDisconnectedDevice(final BTDevice device) { + BTUtils.println(System.err, "****** Server Disconnected Device (count "+(1+disconnectCount.get())+ + ", served "+servedProtocolSessionsTotal.get()+", left "+servingProtocolSessionsLeft.get()+"): Start "+device.toString()); + + // already unpaired + stopAdvertising("device-disconnected"); + device.remove(); + BTDeviceRegistry.removeFromProcessingDevices(device.getAddressAndType()); + + disconnectCount.addAndGet(1); + + try { + Thread.sleep(100); // wait a little (FIXME: Fast restart of advertising error) + } catch (final InterruptedException e) { } + + if( servingProtocolSessionsLeft.get() > 0 ) { + startAdvertising("device-disconnected"); + } + + BTUtils.println(System.err, "****** Server Disonnected Device: End "+device.toString()); + } + + @Override + public boolean initAdapter(final BTAdapter adapter) { + if( !useAdapter.equals(EUI48.ALL_DEVICE) && !useAdapter.equals(adapter.getAddressAndType().address) ) { + BTUtils.fprintf_td(System.err, "initServerAdapter: Adapter not selected: %s\n", adapter.toString()); + return false; + } + adapterName = adapterName + "-" + adapter.getAddressAndType().address.toString().replace(":", ""); + + if( !adapter.isInitialized() ) { + // Initialize with defaults and power-on + final HCIStatusCode status = adapter.initialize( btMode ); + if( HCIStatusCode.SUCCESS != status ) { + BTUtils.fprintf_td(System.err, "initServerAdapter: initialization failed: %s: %s\n", + status.toString(), adapter.toString()); + return false; + } + } else if( !adapter.setPowered( true ) ) { + BTUtils.fprintf_td(System.err, "initServerAdapter: setPower.1 on failed: %s\n", adapter.toString()); + return false; + } + // adapter is powered-on + BTUtils.println(System.err, "initServerAdapter.1: "+adapter.toString()); + + if( adapter.setPowered(false) ) { + HCIStatusCode status = adapter.setName(adapterName, adapterShortName); + if( HCIStatusCode.SUCCESS == status ) { + BTUtils.fprintf_td(System.err, "initServerAdapter: setLocalName OK: %s\n", adapter.toString()); + } else { + BTUtils.fprintf_td(System.err, "initServerAdapter: setLocalName failed: %s\n", adapter.toString()); + return false; + } + + status = adapter.setSecureConnections( use_SC ); + if( HCIStatusCode.SUCCESS == status ) { + BTUtils.fprintf_td(System.err, "initServerAdapter: setSecureConnections OK: %s\n", adapter.toString()); + } else { + BTUtils.fprintf_td(System.err, "initServerAdapter: setSecureConnections failed: %s\n", adapter.toString()); + return false; + } + + final short conn_min_interval = 8; // 10ms + final short conn_max_interval = 40; // 50ms + final short conn_latency = 0; + final short supervision_timeout = 50; // 500ms + status = adapter.setDefaultConnParam(conn_min_interval, conn_max_interval, conn_latency, supervision_timeout); + if( HCIStatusCode.SUCCESS == status ) { + BTUtils.fprintf_td(System.err, "initServerAdapter: setDefaultConnParam OK: %s\n", adapter.toString()); + } else { + BTUtils.fprintf_td(System.err, "initServerAdapter: setDefaultConnParam failed: %s\n", adapter.toString()); + return false; + } + + if( !adapter.setPowered( true ) ) { + BTUtils.fprintf_td(System.err, "initServerAdapter: setPower.2 on failed: %s\n", adapter.toString()); + return false; + } + } else { + BTUtils.fprintf_td(System.err, "initServerAdapter: setPowered.2 off failed: %s\n", adapter.toString()); + } + // adapter is powered-on + BTUtils.println(System.err, "initServerAdapter.2: "+adapter.toString()); + + { + final LE_Features le_feats = adapter.getLEFeatures(); + BTUtils.fprintf_td(System.err, "initServerAdapter: LE_Features %s\n", le_feats.toString()); + } + if( adapter.getBTMajorVersion() > 4 ) { + // BT5 specific + final LE_PHYs Tx = new LE_PHYs( LE_PHYs.PHY.LE_2M ); + final LE_PHYs Rx = new LE_PHYs( LE_PHYs.PHY.LE_2M ); + final HCIStatusCode res = adapter.setDefaultLE_PHY(Tx, Rx); + BTUtils.fprintf_td(System.err, "initServerAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n", + res.toString(), Tx.toString(), Rx.toString()); + } + adapter.setSMPKeyPath(DBTConstants.SERVER_KEY_PATH); + + adapter.addStatusListener( myAdapterStatusListener ); + + adapter.setServerConnSecurity(adapterSecurityLevel, SMPIOCapability.UNSET); + + return true; + } +} diff --git a/trial/java/trial/org/direct_bt/ExpectedPairing.java b/trial/java/trial/org/direct_bt/ExpectedPairing.java new file mode 100644 index 00000000..1a67533a --- /dev/null +++ b/trial/java/trial/org/direct_bt/ExpectedPairing.java @@ -0,0 +1,31 @@ +/** + * 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. + */ +package trial.org.direct_bt; + +public enum ExpectedPairing { + DONT_CARE, + NEW_PAIRING, + PREPAIRED +}; + diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer00.java b/trial/java/trial/org/direct_bt/TestDBTClientServer00.java index 37a0024b..77dc2250 100644 --- a/trial/java/trial/org/direct_bt/TestDBTClientServer00.java +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer00.java @@ -108,7 +108,7 @@ public class TestDBTClientServer00 extends BaseDBTClientServer { final String serverName = "TestDBTCS00-S-T10"; final DBTServer00 server = new DBTServer00(serverName, EUI48.ALL_DEVICE, BTMode.DUAL, true /* SC */, BTSecurityLevel.NONE); - server.servingProtocolSessionsLeft.set(1); + server.setProtocolSessionsLeft(1); final DBTEndpoint.ChangedAdapterSetListener myChangedAdapterSetListener = DBTEndpoint.initChangedAdapterSetListener(manager, Arrays.asList(server)); diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer10_NoEnc.java b/trial/java/trial/org/direct_bt/TestDBTClientServer10_NoEnc.java index b1c53a64..d18c2478 100644 --- a/trial/java/trial/org/direct_bt/TestDBTClientServer10_NoEnc.java +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer10_NoEnc.java @@ -46,18 +46,18 @@ public class TestDBTClientServer10_NoEnc extends DBTClientServer1x { @Test(timeout = 20000) public final void test00_FullCycle_EncNone() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.DONT_CARE; + final ExpectedPairing clientExpPairing = ExpectedPairing.DONT_CARE; test8x_fullCycle(20000, "10", 1, true /* server_client_order */, serverSC, - BTSecurityLevel.NONE, serverShallHaveKeys, BTSecurityLevel.NONE, clientShallHaveKeys); + BTSecurityLevel.NONE, serverExpPairing, BTSecurityLevel.NONE, clientExpPairing); } @Test(timeout = 30000) public final void test01_FullCycle_EncNone() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.DONT_CARE; + final ExpectedPairing clientExpPairing = ExpectedPairing.DONT_CARE; test8x_fullCycle(30000, "11", 2, true /* server_client_order */, serverSC, - BTSecurityLevel.NONE, serverShallHaveKeys, BTSecurityLevel.NONE, clientShallHaveKeys); + BTSecurityLevel.NONE, serverExpPairing, BTSecurityLevel.NONE, clientExpPairing); } public static void main(final String args[]) { diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer12_NoEnc.java b/trial/java/trial/org/direct_bt/TestDBTClientServer12_NoEnc.java index 7d1aac65..477b9281 100644 --- a/trial/java/trial/org/direct_bt/TestDBTClientServer12_NoEnc.java +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer12_NoEnc.java @@ -46,18 +46,18 @@ public class TestDBTClientServer12_NoEnc extends DBTClientServer1x { @Test(timeout = 20000) public final void test02_FullCycle_EncNone() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.DONT_CARE; + final ExpectedPairing clientExpPairing = ExpectedPairing.DONT_CARE; test8x_fullCycle(20000, "12", 1, false /* server_client_order */, serverSC, - BTSecurityLevel.NONE, serverShallHaveKeys, BTSecurityLevel.NONE, clientShallHaveKeys); + BTSecurityLevel.NONE, serverExpPairing, BTSecurityLevel.NONE, clientExpPairing); } @Test(timeout = 30000) public final void test03_FullCycle_EncNone() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.DONT_CARE; + final ExpectedPairing clientExpPairing = ExpectedPairing.DONT_CARE; test8x_fullCycle(30000, "13", 2, false /* server_client_order */, serverSC, - BTSecurityLevel.NONE, serverShallHaveKeys, BTSecurityLevel.NONE, clientShallHaveKeys); + BTSecurityLevel.NONE, serverExpPairing, BTSecurityLevel.NONE, clientExpPairing); } public static void main(final String args[]) { diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer20_SC0.java b/trial/java/trial/org/direct_bt/TestDBTClientServer20_SC0.java index 3659a53e..e4b06b76 100644 --- a/trial/java/trial/org/direct_bt/TestDBTClientServer20_SC0.java +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer20_SC0.java @@ -46,18 +46,18 @@ public class TestDBTClientServer20_SC0 extends DBTClientServer1x { @Test(timeout = 40000) public final void test10_FullCycle_EncOnlyNo1() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.NEW_PAIRING; + final ExpectedPairing clientExpPairing = ExpectedPairing.NEW_PAIRING; test8x_fullCycle(40000, "20", 1, true /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } @Test(timeout = 40000) public final void test20_FullCycle_EncOnlyNo2() { - final boolean serverShallHaveKeys = true; - final boolean clientShallHaveKeys = true; + final ExpectedPairing serverExpPairing = ExpectedPairing.PREPAIRED; + final ExpectedPairing clientExpPairing = ExpectedPairing.PREPAIRED; test8x_fullCycle(40000, "21", 2, true /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } public static void main(final String args[]) { diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer22_SC0.java b/trial/java/trial/org/direct_bt/TestDBTClientServer22_SC0.java index 49a361a8..10fa3663 100644 --- a/trial/java/trial/org/direct_bt/TestDBTClientServer22_SC0.java +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer22_SC0.java @@ -46,18 +46,18 @@ public class TestDBTClientServer22_SC0 extends DBTClientServer1x { @Test(timeout = 40000) public final void test11_FullCycle_EncOnlyNo1() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.NEW_PAIRING; + final ExpectedPairing clientExpPairing = ExpectedPairing.NEW_PAIRING; test8x_fullCycle(40000, "22", 1, false /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } @Test(timeout = 40000) public final void test21_FullCycle_EncOnlyNo2() { - final boolean serverShallHaveKeys = true; - final boolean clientShallHaveKeys = true; + final ExpectedPairing serverExpPairing = ExpectedPairing.PREPAIRED; + final ExpectedPairing clientExpPairing = ExpectedPairing.PREPAIRED; test8x_fullCycle(40000, "23", 2, false /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } public static void main(final String args[]) { diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer30_SC1.java b/trial/java/trial/org/direct_bt/TestDBTClientServer30_SC1.java index 55e05b04..708c3a43 100644 --- a/trial/java/trial/org/direct_bt/TestDBTClientServer30_SC1.java +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer30_SC1.java @@ -46,18 +46,18 @@ public class TestDBTClientServer30_SC1 extends DBTClientServer1x { @Test(timeout = 40000) public final void test10_FullCycle_EncOnlyNo1() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.NEW_PAIRING; + final ExpectedPairing clientExpPairing = ExpectedPairing.NEW_PAIRING; test8x_fullCycle(40000, "30", 1, true /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } @Test(timeout = 40000) public final void test20_FullCycle_EncOnlyNo2() { - final boolean serverShallHaveKeys = true; - final boolean clientShallHaveKeys = true; + final ExpectedPairing serverExpPairing = ExpectedPairing.PREPAIRED; + final ExpectedPairing clientExpPairing = ExpectedPairing.PREPAIRED; test8x_fullCycle(40000, "31", 2, true /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } public static void main(final String args[]) { diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer32_SC1.java b/trial/java/trial/org/direct_bt/TestDBTClientServer32_SC1.java index 522afd22..b6afa327 100644 --- a/trial/java/trial/org/direct_bt/TestDBTClientServer32_SC1.java +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer32_SC1.java @@ -46,18 +46,18 @@ public class TestDBTClientServer32_SC1 extends DBTClientServer1x { @Test(timeout = 40000) public final void test11_FullCycle_EncOnlyNo1() { - final boolean serverShallHaveKeys = false; - final boolean clientShallHaveKeys = false; + final ExpectedPairing serverExpPairing = ExpectedPairing.NEW_PAIRING; + final ExpectedPairing clientExpPairing = ExpectedPairing.NEW_PAIRING; test8x_fullCycle(40000, "32", 1, false /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } @Test(timeout = 40000) public final void test21_FullCycle_EncOnlyNo2() { - final boolean serverShallHaveKeys = true; - final boolean clientShallHaveKeys = true; + final ExpectedPairing serverExpPairing = ExpectedPairing.PREPAIRED; + final ExpectedPairing clientExpPairing = ExpectedPairing.PREPAIRED; test8x_fullCycle(40000, "33", 2, false /* server_client_order */, serverSC, - BTSecurityLevel.ENC_ONLY, serverShallHaveKeys, BTSecurityLevel.ENC_ONLY, clientShallHaveKeys); + BTSecurityLevel.ENC_ONLY, serverExpPairing, BTSecurityLevel.ENC_ONLY, clientExpPairing); } public static void main(final String args[]) { diff --git a/trial/java/trial/org/direct_bt/TestDBTProvokeClientServer_i470.java b/trial/java/trial/org/direct_bt/TestDBTProvokeClientServer_i470.java new file mode 100644 index 00000000..cb96610c --- /dev/null +++ b/trial/java/trial/org/direct_bt/TestDBTProvokeClientServer_i470.java @@ -0,0 +1,69 @@ +/** + * 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. + */ + +package trial.org.direct_bt; + +import org.direct_bt.BTMode; +import org.direct_bt.BTSecurityLevel; +import org.jau.net.EUI48; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * Testing w/o client filtering processing device and hence not blocking deviceFound. + * + * In other words, relying on BTAdapter to filter out: + * - already discovered devices + * - already connected devices + * + * Further, the server will issue a disconnect once only 300 ms after 1st MTU exchange, + * disrupting the client's getGATTServices(). + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestDBTProvokeClientServer_i470 extends DBTClientServer1x { + + @Test(timeout = 20000) + public final void test_i470() + { + final boolean serverSC = true; + final String suffix = "i470"; + final int protocolSessionCount = 2; + final boolean server_client_order = true; + final ExpectedPairing serverExpPairing = ExpectedPairing.DONT_CARE; + final ExpectedPairing clientExpPairing = ExpectedPairing.DONT_CARE; + + // final DBTServerTest server = new DBTServer01("S-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL, serverSC, BTSecurityLevel.ENC_ONLY); + final DBTServerTest server = new DBTServer00("S-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL, serverSC, BTSecurityLevel.ENC_ONLY); + final DBTClientTest client = new DBTClient01("C-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL); + + test8x_fullCycle(20000, suffix, protocolSessionCount, server_client_order, + server, BTSecurityLevel.ENC_ONLY, serverExpPairing, + client, BTSecurityLevel.ENC_ONLY, clientExpPairing); + } + + public static void main(final String args[]) { + org.junit.runner.JUnitCore.main(TestDBTProvokeClientServer_i470.class.getName()); + } +} |