diff options
-rw-r--r-- | trial/direct_bt/dbt_client01.hpp | 7 | ||||
-rw-r--r-- | trial/direct_bt/dbt_client_server1x.hpp | 42 | ||||
-rw-r--r-- | trial/direct_bt/dbt_server01.hpp | 6 | ||||
-rw-r--r-- | trial/direct_bt/test_client_server40_reset.cpp | 84 | ||||
-rw-r--r-- | trial/java/trial/org/direct_bt/DBTClient01.java | 7 | ||||
-rw-r--r-- | trial/java/trial/org/direct_bt/DBTClientServer1x.java | 42 | ||||
-rw-r--r-- | trial/java/trial/org/direct_bt/DBTServer01.java | 7 | ||||
-rw-r--r-- | trial/java/trial/org/direct_bt/TestDBTClientServer40_Reset.java | 84 |
8 files changed, 275 insertions, 4 deletions
diff --git a/trial/direct_bt/dbt_client01.hpp b/trial/direct_bt/dbt_client01.hpp index 80f76ed9..a5083329 100644 --- a/trial/direct_bt/dbt_client01.hpp +++ b/trial/direct_bt/dbt_client01.hpp @@ -83,6 +83,13 @@ class DBTClient01 : public DBTClientTest { } else { fprintf_td(stderr, "****** Client SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(), to_string(newmask).c_str(), to_string(changedmask).c_str()); + + const bool justPoweredOn = isAdapterSettingBitSet(changedmask, AdapterSetting::POWERED) && + isAdapterSettingBitSet(newmask, AdapterSetting::POWERED); + if( justPoweredOn && DiscoveryPolicy::AUTO_OFF != parent.discoveryPolicy && *parent.clientAdapter == a ) { + std::thread dc(&DBTClient01::startDiscovery, &parent, "powered_on"); // @suppress("Invalid arguments") + dc.detach(); + } } fprintf_td(stderr, "Client Status BTAdapter:\n"); fprintf_td(stderr, "%s\n", a.toString().c_str()); diff --git a/trial/direct_bt/dbt_client_server1x.hpp b/trial/direct_bt/dbt_client_server1x.hpp index 3274b30f..4d6b4523 100644 --- a/trial/direct_bt/dbt_client_server1x.hpp +++ b/trial/direct_bt/dbt_client_server1x.hpp @@ -57,8 +57,17 @@ class DBTClientServer1x { PairingMode lastCompletedDevicePairingMode = PairingMode::NONE; BTSecurityLevel lastCompletedDeviceSecurityLevel = BTSecurityLevel::NONE; EInfoReport lastCompletedDeviceEIR; + int client_power_down_count = 0; + int client_power_up_count = 0; + bool client_reset_at_ready = false; + bool server_reset_at_ready = false; + bool client_reset_test = false; + bool server_reset_test = false; public: + void set_client_reset_at_ready(const bool v) noexcept { client_reset_at_ready = v; client_reset_test = v; } + void set_server_reset_at_ready(const bool v) noexcept { server_reset_at_ready = v; server_reset_test = v; } + void test8x_fullCycle(const std::string& suffix, const int protocolSessionCount, const bool server_client_order, const bool serverSC, const BTSecurityLevel secLevelServer, const ExpectedPairing serverExpPairing, const BTSecurityLevel secLevelClient, const ExpectedPairing clientExpPairing) @@ -127,6 +136,21 @@ class DBTClientServer1x { public: MyAdapterStatusListener(DBTClientServer1x& p) : parent(p) {} + void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, + const AdapterSetting changedmask, const uint64_t timestamp) override { + const bool initialSetting = AdapterSetting::NONE == oldmask; + if( !initialSetting && isAdapterSettingBitSet(changedmask, AdapterSetting::POWERED) ) { + const std::lock_guard<std::mutex> lock(parent.mtx_sync); // RAII-style acquire and relinquish via destructor + if( isAdapterSettingBitSet(newmask, AdapterSetting::POWERED) ) { + ++parent.client_power_up_count; + } else { + ++parent.client_power_down_count; + } + } + (void)timestamp; + (void)a; + } + 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 @@ -135,6 +159,12 @@ class DBTClientServer1x { parent.lastCompletedDeviceSecurityLevel = device->getConnSecurityLevel(); parent.lastCompletedDeviceEIR = *device->getEIR(); fprintf_td(stderr, "XXXXXX Client Ready: %s\n", device->toString(true).c_str()); + if( parent.client_reset_at_ready ) { + parent.client_reset_at_ready = false; + fprintf_td(stderr, "XXXXXX Client Reset.0: %s\n", device->toString(true).c_str()); + const HCIStatusCode rr = device->getAdapter().reset(); + fprintf_td(stderr, "XXXXXX Client Reset.X: %s: %s\n", direct_bt::to_string(rr).c_str(), device->toString(true).c_str()); + } } std::string toString() const noexcept override { return "DBTClientServer1x::Client"; } @@ -184,9 +214,10 @@ class DBTClientServer1x { fprintf_td(stderr, " Server ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", server->getProtocolSessionsDoneSuccess(), server->getProtocolSessionsDoneTotal(), protocolSessionCount, server->getDisconnectCount(), ( protocolSessionCount * max_connections_per_session )); - fprintf_td(stderr, " Client ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", + fprintf_td(stderr, " Client ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max, power[down %d, up %d]\n", client->getProtocolSessionsDoneSuccess(), client->getProtocolSessionsDoneTotal(), protocolSessionCount, - client->getDisconnectCount(), ( protocolSessionCount * max_connections_per_session )); + client->getDisconnectCount(), ( protocolSessionCount * max_connections_per_session ), + client_power_down_count, client_power_up_count); fprintf_td(stderr, "\n\n"); if( expSuccess ) { @@ -202,6 +233,13 @@ class DBTClientServer1x { REQUIRE( EIRDataType::NONE != lastCompletedDeviceEIR.getEIRDataMask() ); REQUIRE( false == lastCompletedDevice->getConnected() ); REQUIRE( ( protocolSessionCount * max_connections_per_session ) > server->getDisconnectCount() ); + if( client_reset_test ) { + REQUIRE( 1 == client_power_down_count ); + REQUIRE( 1 == client_power_up_count ); + } else { + REQUIRE( 0 == client_power_down_count ); + REQUIRE( 0 == client_power_up_count ); + } } } diff --git a/trial/direct_bt/dbt_server01.hpp b/trial/direct_bt/dbt_server01.hpp index ebe108dd..11a3d027 100644 --- a/trial/direct_bt/dbt_server01.hpp +++ b/trial/direct_bt/dbt_server01.hpp @@ -191,6 +191,12 @@ class DBTServer01 : public DBTServerTest { } else { fprintf_td(stderr, "****** Server SETTINGS_CHANGED: %s -> %s, changed %s\n", to_string(oldmask).c_str(), to_string(newmask).c_str(), to_string(changedmask).c_str()); + + const bool justPoweredOn = isAdapterSettingBitSet(changedmask, AdapterSetting::POWERED) && + isAdapterSettingBitSet(newmask, AdapterSetting::POWERED); + if( justPoweredOn && *parent.serverAdapter == a ) { + parent.startAdvertising("powered_on"); + } } fprintf_td(stderr, "Server Status BTAdapter:\n"); fprintf_td(stderr, "%s\n", a.toString().c_str()); diff --git a/trial/direct_bt/test_client_server40_reset.cpp b/trial/direct_bt/test_client_server40_reset.cpp new file mode 100644 index 00000000..6746ae5a --- /dev/null +++ b/trial/direct_bt/test_client_server40_reset.cpp @@ -0,0 +1,84 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2022 Gothel Software e.K. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <jau/test/catch2_ext.hpp> + +#include "dbt_client_server1x.hpp" + +using namespace direct_bt; + +/** + * Testing a full Bluetooth server and client lifecycle of operations including adapter reset, requiring two BT adapter: + * - trigger client adapter reset + * - operating w/o encryption + * - start server advertising + * - start client discovery and connect to server when discovered + * - client/server processing of connection when ready + * - client disconnect + * - server stop advertising + * - security-level: NONE, ENC_ONLY freshly-paired and ENC_ONLY pre-paired + * - reuse server-adapter for client-mode discovery (just toggle on/off) + */ +class TestDBTClientServer40_Reset : public DBTClientServer1x { + private: + static constexpr const bool serverSC = true; + + public: + void test40_ClientReset01() { + base_test_framework.setupTest( 20_s ); + { + const std::string suffix = "40"; + const int protocolSessionCount = 1; + const int max_connections_per_session = DBTConstants::max_connections_per_session; + const bool expSuccess = true; + const bool server_client_order = true; + const BTSecurityLevel secLevelServer = BTSecurityLevel::NONE; + const BTSecurityLevel secLevelClient = BTSecurityLevel::NONE; + 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<DBTClientTest> client = std::make_shared<DBTClient01>("C-"+suffix, EUI48::ALL_DEVICE, BTMode::DUAL); + + server->setProtocolSessionsLeft( protocolSessionCount ); + + client->setProtocolSessionsLeft( protocolSessionCount ); + client->setDisconnectDevice( true ); // default, auto-disconnect after work is done + client->setRemoveDevice( false ); // default, test side-effects + client->setDiscoveryPolicy( DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED ); + + set_client_reset_at_ready(true); + + test8x_fullCycle(suffix, + max_connections_per_session, expSuccess, + server_client_order, + server, secLevelServer, serverExpPairing, + client, secLevelClient, clientExpPairing); + } + base_test_framework.cleanupTest(); + } + +}; + +METHOD_AS_TEST_CASE( TestDBTClientServer40_Reset::test40_ClientReset01, "ClientServer 40 Reset Trial", "[trial][serverclient][reset]"); diff --git a/trial/java/trial/org/direct_bt/DBTClient01.java b/trial/java/trial/org/direct_bt/DBTClient01.java index 473c5fa3..a37df5c7 100644 --- a/trial/java/trial/org/direct_bt/DBTClient01.java +++ b/trial/java/trial/org/direct_bt/DBTClient01.java @@ -178,6 +178,13 @@ public class DBTClient01 implements DBTClientTest { PrintUtil.println(System.err, "****** Client SETTINGS_INITIAL: "+oldmask+" -> "+newmask+", changed "+changedmask); } else { PrintUtil.println(System.err, "****** Client SETTINGS_CHANGED: "+oldmask+" -> "+newmask+", changed "+changedmask); + + final boolean justPoweredOn = changedmask.isSet(AdapterSettings.SettingType.POWERED) && + newmask.isSet(AdapterSettings.SettingType.POWERED); + if( justPoweredOn && DiscoveryPolicy.AUTO_OFF != discoveryPolicy && clientAdapter.equals(adapter) ) { + executeOffThread( () -> { startDiscovery("powered_on"); }, + "Client DBT-StartDiscovery-"+adapter.getAddressAndType(), true /* detach */); + } } PrintUtil.println(System.err, "Client Status Adapter:"); PrintUtil.println(System.err, adapter.toString()); diff --git a/trial/java/trial/org/direct_bt/DBTClientServer1x.java b/trial/java/trial/org/direct_bt/DBTClientServer1x.java index 3dfa48b2..c7845f39 100644 --- a/trial/java/trial/org/direct_bt/DBTClientServer1x.java +++ b/trial/java/trial/org/direct_bt/DBTClientServer1x.java @@ -69,6 +69,15 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { PairingMode lastCompletedDevicePairingMode = PairingMode.NONE; BTSecurityLevel lastCompletedDeviceSecurityLevel = BTSecurityLevel.NONE; EInfoReport lastCompletedDeviceEIR = null; + int client_power_down_count = 0; + int client_power_up_count = 0; + boolean client_reset_at_ready = false; + boolean server_reset_at_ready = false; + boolean client_reset_test = false; + boolean server_reset_test = false; + + final void set_client_reset_at_ready(final boolean v) { client_reset_at_ready = v; client_reset_test = v; } + final void set_server_reset_at_ready(final boolean v) { server_reset_at_ready = v; server_reset_test = v; } final void test8x_fullCycle(final long timeout_value, final String suffix, final int protocolSessionCount, final boolean server_client_order, @@ -137,6 +146,21 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { } final AdapterStatusListener clientAdapterStatusListener = new 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 && changedmask.isSet(AdapterSettings.SettingType.POWERED) ) { + synchronized( mtx_sync ) { + if( newmask.isSet(AdapterSettings.SettingType.POWERED) ) { + ++client_power_up_count; + } else { + ++client_power_down_count; + } + } + } + } + + @Override public void deviceReady(final BTDevice device, final long timestamp) { synchronized( mtx_sync ) { lastCompletedDevice = device; @@ -144,6 +168,12 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { lastCompletedDeviceSecurityLevel = device.getConnSecurityLevel(); lastCompletedDeviceEIR = device.getEIR().clone(); PrintUtil.println(System.err, "XXXXXX Client Ready: "+device); + if( client_reset_at_ready ) { + client_reset_at_ready = false; + PrintUtil.fprintf_td(System.err, "XXXXXX Client Reset.0: %s\n", device.toString()); + final HCIStatusCode rr = device.getAdapter().reset(); + PrintUtil.fprintf_td(System.err, "XXXXXX Client Reset.X: %s: %s\n", rr.toString(), device.toString()); + } } } }; @@ -187,9 +217,10 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { PrintUtil.fprintf_td(System.err, " Server ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", server.getProtocolSessionsDoneSuccess(), server.getProtocolSessionsDoneTotal(), protocolSessionCount, server.getDisconnectCount(), ( protocolSessionCount * max_connections_per_session )); - PrintUtil.fprintf_td(System.err, " Client ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max\n", + PrintUtil.fprintf_td(System.err, " Client ProtocolSessions[success %d/%d total, requested %d], disconnects %d of %d max, power[down %d, up %d]\n", client.getProtocolSessionsDoneSuccess(), client.getProtocolSessionsDoneTotal(), protocolSessionCount, - client.getDisconnectCount(), ( protocolSessionCount * max_connections_per_session )); + client.getDisconnectCount(), ( protocolSessionCount * max_connections_per_session ), + client_power_down_count, client_power_up_count); PrintUtil.fprintf_td(System.err, "\n\n"); if( expSuccess ) { @@ -205,6 +236,13 @@ public abstract class DBTClientServer1x extends BaseDBTClientServer { Assert.assertNotNull(lastCompletedDeviceEIR); Assert.assertFalse(lastCompletedDevice.getConnected()); Assert.assertTrue( ( 1 * max_connections_per_session ) > server.getDisconnectCount() ); + if( client_reset_test ) { + Assert.assertEquals( 1, client_power_down_count ); + Assert.assertEquals( 1, client_power_up_count ); + } else { + Assert.assertEquals( 0, client_power_down_count ); + Assert.assertEquals( 0, client_power_up_count ); + } } } diff --git a/trial/java/trial/org/direct_bt/DBTServer01.java b/trial/java/trial/org/direct_bt/DBTServer01.java index b17c6465..e3cbf591 100644 --- a/trial/java/trial/org/direct_bt/DBTServer01.java +++ b/trial/java/trial/org/direct_bt/DBTServer01.java @@ -275,6 +275,13 @@ public class DBTServer01 implements DBTServerTest { PrintUtil.println(System.err, "****** Server SETTINGS_INITIAL: "+oldmask+" -> "+newmask+", changed "+changedmask); } else { PrintUtil.println(System.err, "****** Server SETTINGS_CHANGED: "+oldmask+" -> "+newmask+", changed "+changedmask); + + final boolean justPoweredOn = changedmask.isSet(AdapterSettings.SettingType.POWERED) && + newmask.isSet(AdapterSettings.SettingType.POWERED); + if( justPoweredOn && serverAdapter.equals(adapter) ) { + executeOffThread( () -> { startAdvertising("powered_on"); }, + "Server DBT-StartAdvertising-"+adapter.getAddressAndType(), true /* detach */); + } } PrintUtil.println(System.err, "Server Status Adapter:"); PrintUtil.println(System.err, adapter.toString()); diff --git a/trial/java/trial/org/direct_bt/TestDBTClientServer40_Reset.java b/trial/java/trial/org/direct_bt/TestDBTClientServer40_Reset.java new file mode 100644 index 00000000..f0beefe1 --- /dev/null +++ b/trial/java/trial/org/direct_bt/TestDBTClientServer40_Reset.java @@ -0,0 +1,84 @@ +/** + * 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.direct_bt.DiscoveryPolicy; +import org.jau.net.EUI48; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.runners.MethodSorters; + +/** + * Testing a full Bluetooth server and client lifecycle of operations including adapter reset, requiring two BT adapter: + * - trigger client adapter reset + * - operating w/o encryption + * - start server advertising + * - start client discovery and connect to server when discovered + * - client/server processing of connection when ready + * - client disconnect + * - server stop advertising + * - security-level: NONE, ENC_ONLY freshly-paired and ENC_ONLY pre-paired + * - reuse server-adapter for client-mode discovery (just toggle on/off) + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class TestDBTClientServer40_Reset extends DBTClientServer1x { + static final boolean serverSC = true; + + @Test(timeout = 20000) + public final void test40_ClientReset01() { + final String suffix = "40"; + final int protocolSessionCount = 1; + final int max_connections_per_session = DBTConstants.max_connections_per_session; + final boolean expSuccess = true; + final boolean server_client_order = true; + final BTSecurityLevel secLevelServer = BTSecurityLevel.NONE; + final BTSecurityLevel secLevelClient = BTSecurityLevel.NONE; + 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, secLevelServer); + final DBTClientTest client = new DBTClient01("C-"+suffix, EUI48.ALL_DEVICE, BTMode.DUAL); + + server.setProtocolSessionsLeft( protocolSessionCount ); + + client.setProtocolSessionsLeft( protocolSessionCount ); + client.setDisconnectDeviceed( true ); // default, auto-disconnect after work is done + client.setRemoveDevice( false ); // default, test side-effects + client.setDiscoveryPolicy( DiscoveryPolicy.PAUSE_CONNECTED_UNTIL_DISCONNECTED ); + + set_client_reset_at_ready(true); + + test8x_fullCycle(20000, suffix, + max_connections_per_session, expSuccess, server_client_order, + server, secLevelServer, serverExpPairing, + client, secLevelClient, clientExpPairing); + } + + public static void main(final String args[]) { + org.junit.runner.JUnitCore.main(TestDBTClientServer40_Reset.class.getName()); + } +} |