diff options
author | Sven Gothel <[email protected]> | 2021-07-31 02:25:03 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-07-31 02:25:03 +0200 |
commit | 5a63c8ccc1a8edf307be49b1cdb6c786898bd2b2 (patch) | |
tree | f543d0201853c4aa13ce66a1e44c48f6882aefdb | |
parent | 0e2d05a2b7542c4c2865f736ddc7a1272c730f9d (diff) |
dbt_scanner10/DBTScanner10: Extract BTDeviceRegistry and BTSecurityRegistry to lib, support full EUI84Sub and name-sub pattern matching
Same naming-schema and functionality C++/Java
Due to using EUI48Sub and name-sub pattern matching,
we naturally had to drop the hash-set fast-lookup approach.
However, looking up device names via pattern-matching is essential
to handle device groups, i.e.
- specific security settings (level, passkey, ..)
- actually accepting them to connect (waitForDevices)
-rw-r--r-- | api/direct_bt/BTDeviceRegistry.hpp | 61 | ||||
-rw-r--r-- | api/direct_bt/BTSecurityRegistry.hpp | 103 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 296 | ||||
-rw-r--r-- | examples/java/DBTScanner10.java | 183 | ||||
-rw-r--r-- | java/org/direct_bt/BTDeviceRegistry.java | 165 | ||||
-rw-r--r-- | java/org/direct_bt/BTSecurityRegistry.java | 141 | ||||
-rwxr-xr-x | scripts/run-dbt_scanner10.sh | 14 | ||||
-rw-r--r-- | src/direct_bt/BTDeviceRegistry.cpp | 174 | ||||
-rw-r--r-- | src/direct_bt/BTSecurityRegistry.cpp | 98 | ||||
-rw-r--r-- | src/direct_bt/CMakeLists.txt | 2 |
10 files changed, 883 insertions, 354 deletions
diff --git a/api/direct_bt/BTDeviceRegistry.hpp b/api/direct_bt/BTDeviceRegistry.hpp new file mode 100644 index 00000000..a09c5248 --- /dev/null +++ b/api/direct_bt/BTDeviceRegistry.hpp @@ -0,0 +1,61 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * 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. + */ + +#ifndef DBT_DEV_ACCOUNTING_HPP_ +#define DBT_DEV_ACCOUNTING_HPP_ + +#include <string> +#include <cstdio> + +#include <direct_bt/DirectBT.hpp> + +namespace direct_bt { + + /** + * Application toolkit providing BT device registration of processed and awaited devices. + * The latter on a pattern matching basis, i.e. EUI48Sub or name-sub. + */ + namespace BTDeviceRegistry { + void addToWaitForDevices(const std::string& addrOrNameSub); + bool isWaitingForDevice(const BDAddressAndType &mac, const std::string &name); + bool isWaitingForAnyDevice(); + size_t getWaitForDevicesCount(); + void printWaitForDevices(FILE *out, const std::string &msg); + + void addToDevicesProcessed(const BDAddressAndType &a, const std::string& n); + bool isDeviceProcessed(const BDAddressAndType & a); + size_t getDeviceProcessedCount(); + bool allDevicesProcessed(); + void printDevicesProcessed(FILE *out, const std::string &msg); + + void addToDevicesProcessing(const BDAddressAndType &a, const std::string& n); + bool removeFromDevicesProcessing(const BDAddressAndType &a); + bool isDeviceProcessing(const BDAddressAndType & a); + size_t getDeviceProcessingCount(); + } + +} // namespace direct_bt + +#endif /* DBT_DEV_ACCOUNTING_HPP_ */ diff --git a/api/direct_bt/BTSecurityRegistry.hpp b/api/direct_bt/BTSecurityRegistry.hpp new file mode 100644 index 00000000..ace42cd1 --- /dev/null +++ b/api/direct_bt/BTSecurityRegistry.hpp @@ -0,0 +1,103 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * 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. + */ + +#ifndef DBT_SEC_SETTINGS_HPP_ +#define DBT_SEC_SETTINGS_HPP_ + +#include <string> +#include <cstdio> + +#include <direct_bt/DirectBT.hpp> + +namespace direct_bt { + + /** + * Application toolkit providing BT security setup and its device association + * on a pattern matching basis, i.e. EUI48Sub or name-sub. + */ + namespace BTSecurityRegistry { + + struct Entry { + static constexpr int NO_PASSKEY = -1; + + EUI48Sub addrSub; + std::string nameSub; + + BTSecurityLevel sec_level { BTSecurityLevel::UNSET }; + SMPIOCapability io_cap { SMPIOCapability::UNSET }; + SMPIOCapability io_cap_auto { SMPIOCapability::UNSET }; + int passkey = NO_PASSKEY; + + Entry(const EUI48Sub& addrSub_) + : addrSub(addrSub_), nameSub() {} + + Entry(const std::string& nameSub_) + : addrSub(EUI48Sub::ALL_DEVICE), nameSub(nameSub_) {} + + bool matches(const EUI48Sub& addressSub) const noexcept { + return addrSub.length > 0 && addressSub.contains(addrSub); + } + bool matches(const EUI48& address) const noexcept { + return addrSub.length > 0 && address.contains(addrSub); + } + bool matches(const std::string& name) const noexcept { + return nameSub.length() > 0 && name.find(nameSub) != std::string::npos; + } + + constexpr bool isSecLevelOrIOCapSet() const noexcept { + return SMPIOCapability::UNSET != io_cap || BTSecurityLevel::UNSET != sec_level; + } + constexpr const BTSecurityLevel& getSecLevel() const noexcept { return sec_level; } + constexpr const SMPIOCapability& getIOCap() const noexcept { return io_cap; } + + constexpr bool isSecurityAutoEnabled() const noexcept { + return SMPIOCapability::UNSET != io_cap_auto; + } + constexpr const SMPIOCapability& getSecurityAutoIOCap() const noexcept { return io_cap_auto; } + + constexpr int getPairingPasskey() const noexcept { return passkey; } + + constexpr bool getPairingNumericComparison() const noexcept { return true; } + + std::string toString() const noexcept { + const std::string id = addrSub == EUI48Sub::ALL_DEVICE ? "'"+nameSub+"'" : addrSub.toString(); + return "BTSecurityDetail["+id+", lvl "+ + to_string(sec_level)+ + ", io "+to_string(io_cap)+ + ", auto-io "+to_string(io_cap_auto)+ + ", passkey "+std::to_string(passkey)+"]"; + } + }; + Entry* get(const EUI48& addr); + Entry* get(const EUI48Sub& addrSub); + Entry* get(const std::string& nameSub); + Entry* getOrCreate(const std::string& addrOrNameSub); + std::string allToString(); + + } // namespace BTSecurityRegistry + +} // namespace direct_bt + +#endif /* DBT_SEC_SETTINGS_HPP_ */ diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp index 9db08309..3ffb602b 100644 --- a/examples/direct_bt_scanner10/dbt_scanner10.cpp +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -44,6 +44,9 @@ #include <direct_bt/DirectBT.hpp> +#include <direct_bt/BTDeviceRegistry.hpp> +#include <direct_bt/BTSecurityRegistry.hpp> + extern "C" { #include <unistd.h> } @@ -67,22 +70,32 @@ using namespace jau; * * * Read device C0:26:DA:01:DA:B1 (using default auto-sec w/ keyboard iocap) * ~~~ - * ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 + * ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 * ~~~ * * * Read device C0:26:DA:01:DA:B1 (enforcing no security) * ~~~ - * ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 -seclevel C0:26:DA:01:DA:B1 1 1 + * ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 -seclevel C0:26:DA:01:DA:B1 1 + * ~~~ + * + * * Read any device containing C0:26:DA (enforcing no security) + * ~~~ + * ../scripts/run-dbt_scanner10.sh -dev C0:26:DA -seclevel C0:26:DA 1 + * ~~~ + * + * * Read any device containing name `TAIDOC` (enforcing no security) + * ~~~ + * ../scripts/run-dbt_scanner10.sh -dev 'TAIDOC' -seclevel 'TAIDOC' 1 * ~~~ * * * Read device C0:26:DA:01:DA:B1, basic debug flags enabled (using default auto-sec w/ keyboard iocap) * ~~~ - * ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 -dbt_debug true + * ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 -dbt_debug true * ~~~ * * * Read device C0:26:DA:01:DA:B1, all debug flags enabled (using default auto-sec w/ keyboard iocap) * ~~~ - * ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 -dbt_debug adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event + * ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 -dbt_debug adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event * ~~~ * * ## Special Actions @@ -93,7 +106,6 @@ using namespace jau; * ~~~ */ -const static int NO_PASSKEY = -1; const static std::string KEY_PATH = "keys"; static uint64_t timestamp_t0; @@ -116,8 +128,6 @@ static int charValue = 0; static bool SHOW_UPDATE_EVENTS = false; static bool QUIET = false; -static jau::darray<BDAddressAndType> waitForDevices; - static void connectDiscoveredDevice(std::shared_ptr<BTDevice> device); static void processReadyDevice(std::shared_ptr<BTDevice> device); @@ -126,153 +136,6 @@ static void removeDevice(std::shared_ptr<BTDevice> device); static void resetAdapter(BTAdapter *a, int mode); static bool startDiscovery(BTAdapter *a, std::string msg); - -static std::unordered_set<BDAddressAndType> devicesInProcessing; -static std::recursive_mutex mtx_devicesProcessing; - -static std::recursive_mutex mtx_devicesProcessed; -static std::unordered_set<BDAddressAndType> devicesProcessed; - -bool matches(jau::darray<BDAddressAndType> &cont, const BDAddressAndType &mac) { - return cont.end() != jau::find_if(cont.begin(), cont.end(), [&](const BDAddressAndType & it)->bool { - return it.matches(mac); - }); -} - -void printList(const std::string &msg, jau::darray<BDAddressAndType> &cont) { - fprintf_td(stderr, "%s ", msg.c_str()); - jau::for_each(cont.begin(), cont.end(), - [](const BDAddressAndType &mac) { fprintf_td(stderr, "%s, ", mac.toString().c_str()); }); - fprintf_td(stderr, "\n"); -} - -void printList(const std::string &msg, std::unordered_set<BDAddressAndType> &cont) { - fprintf_td(stderr, "%s ", msg.c_str()); - jau::for_each(cont.begin(), cont.end(), - [](const BDAddressAndType &mac) { fprintf_td(stderr, "%s, ", mac.toString().c_str()); }); - fprintf_td(stderr, "\n"); -} - -static void addToDevicesProcessed(const BDAddressAndType &a) { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor - devicesProcessed.insert(devicesProcessed.end(), a); -} -static bool isDeviceProcessed(const BDAddressAndType & a) { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor - return devicesProcessed.end() != devicesProcessed.find(a); -} -static size_t getDeviceProcessedCount() { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor - return devicesProcessed.size(); -} -static bool allDevicesProcessed(jau::darray<BDAddressAndType> &cont) { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor - for (auto it = cont.begin(); it != cont.end(); ++it) { - if( devicesProcessed.end() == devicesProcessed.find(*it) ) { - return false; - } - } - return true; -} -static void printDevicesProcessed(const std::string &msg) { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor - printList(msg, devicesProcessed); -} - -static void addToDevicesProcessing(const BDAddressAndType &a) { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor - devicesInProcessing.insert(devicesInProcessing.end(), a); -} -static bool removeFromDevicesProcessing(const BDAddressAndType &a) { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor - for (auto it = devicesInProcessing.begin(); it != devicesInProcessing.end(); ++it) { - if ( a == *it ) { - devicesInProcessing.erase(it); - return true; - } - } - return false; -} -static bool isDeviceProcessing(const BDAddressAndType & a) { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor - return devicesInProcessing.end() != devicesInProcessing.find(a); -} -static size_t getDeviceProcessingCount() { - const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor - return devicesInProcessing.size(); -} - -struct MyBTSecurityDetail { - BDAddressAndType addrAndType; - - BTSecurityLevel sec_level { BTSecurityLevel::UNSET }; - SMPIOCapability io_cap { SMPIOCapability::UNSET }; - SMPIOCapability io_cap_auto { SMPIOCapability::UNSET }; - int passkey = NO_PASSKEY; - - MyBTSecurityDetail(const BDAddressAndType& addrAndType_) - : addrAndType(addrAndType_) {} - - constexpr bool isSecLevelOrIOCapSet() const noexcept { - return SMPIOCapability::UNSET != io_cap || BTSecurityLevel::UNSET != sec_level; - } - constexpr const BTSecurityLevel& getSecLevel() const noexcept { return sec_level; } - constexpr const SMPIOCapability& getIOCap() const noexcept { return io_cap; } - - constexpr bool isSecurityAutoEnabled() const noexcept { - return SMPIOCapability::UNSET != io_cap_auto; - } - constexpr const SMPIOCapability& getSecurityAutoIOCap() const noexcept { return io_cap_auto; } - - constexpr int getPairingPasskey() const noexcept { return passkey; } - - constexpr bool getPairingNumericComparison() const noexcept { return true; } - - std::string toString() const noexcept { - return "MyBTSecurityDetail["+addrAndType.toString()+", lvl "+ - to_string(sec_level)+ - ", io "+to_string(io_cap)+ - ", auto-io "+to_string(io_cap_auto)+ - ", passkey "+std::to_string(passkey)+"]"; - } - - private: - static std::unordered_map<BDAddressAndType, MyBTSecurityDetail> devicesSecDetail; - - public: - - static MyBTSecurityDetail* get(const BDAddressAndType& addrAndType) { - auto s = devicesSecDetail.find(addrAndType); - if( s != devicesSecDetail.end() ) { - return &(s->second); - } else { - return nullptr; - } - } - static MyBTSecurityDetail* getOrCreate(const BDAddressAndType& addrAndType) { - MyBTSecurityDetail* sec_detail = get(addrAndType); - if( nullptr != sec_detail ) { - return sec_detail; - } else { - auto i = devicesSecDetail.insert( devicesSecDetail.end(), { addrAndType, MyBTSecurityDetail(addrAndType) } ); - return &(i->second); - } - } - static std::string allToString() { - std::string res; - int i=0; - for(auto iter = devicesSecDetail.begin(); iter != devicesSecDetail.end(); ++iter, ++i) { - const MyBTSecurityDetail& sec = iter->second; - if( 0 < i ) { - res += ", "; - } - res += sec.toString(); - } - return res; - } -}; -std::unordered_map<BDAddressAndType, MyBTSecurityDetail> MyBTSecurityDetail::devicesSecDetail; - class MyAdapterStatusListener : public AdapterStatusListener { void adapterSettingsChanged(BTAdapter &a, const AdapterSetting oldmask, const AdapterSetting newmask, @@ -313,10 +176,10 @@ class MyAdapterStatusListener : public AdapterStatusListener { fprintf_td(stderr, "****** FOUND__-2: Skip non 'public LE' and non 'random static public LE' %s\n", device->toString(true).c_str()); return false; } - if( !isDeviceProcessing( device->getAddressAndType() ) && - ( waitForDevices.empty() || - ( matches(waitForDevices, device->getAddressAndType()) && - ( 0 < MULTI_MEASUREMENTS || !isDeviceProcessed(device->getAddressAndType()) ) + if( !BTDeviceRegistry::isDeviceProcessing( device->getAddressAndType() ) && + ( !BTDeviceRegistry::isWaitingForAnyDevice() || + ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType(), device->getName()) && + ( 0 < MULTI_MEASUREMENTS || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) ) ) ) @@ -372,8 +235,8 @@ class MyAdapterStatusListener : public AdapterStatusListener { // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION break; case SMPPairingState::PASSKEY_EXPECTED: { - const MyBTSecurityDetail* sec = MyBTSecurityDetail::get(device->getAddressAndType()); - if( nullptr != sec && sec->getPairingPasskey() != NO_PASSKEY ) { + const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::get(device->getAddressAndType().address); + if( nullptr != sec && sec->getPairingPasskey() != BTSecurityRegistry::Entry::NO_PASSKEY ) { std::thread dc(&BTDevice::setPairingPasskey, device, static_cast<uint32_t>( sec->getPairingPasskey() )); dc.detach(); } else { @@ -384,7 +247,7 @@ class MyAdapterStatusListener : public AdapterStatusListener { // next: KEY_DISTRIBUTION or FAILED } break; case SMPPairingState::NUMERIC_COMPARE_EXPECTED: { - const MyBTSecurityDetail* sec = MyBTSecurityDetail::get(device->getAddressAndType()); + const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::get(device->getAddressAndType().address); if( nullptr != sec ) { std::thread dc(&BTDevice::setPairingNumericComparison, device, sec->getPairingNumericComparison()); dc.detach(); @@ -410,17 +273,17 @@ class MyAdapterStatusListener : public AdapterStatusListener { void deviceReady(std::shared_ptr<BTDevice> device, const uint64_t timestamp) override { (void)timestamp; - if( !isDeviceProcessing( device->getAddressAndType() ) && - ( waitForDevices.empty() || - ( matches(waitForDevices, device->getAddressAndType()) && - ( 0 < MULTI_MEASUREMENTS || !isDeviceProcessed(device->getAddressAndType()) ) + if( !BTDeviceRegistry::isDeviceProcessing( device->getAddressAndType() ) && + ( !BTDeviceRegistry::isWaitingForAnyDevice() || + ( BTDeviceRegistry::isWaitingForDevice(device->getAddressAndType(), device->getName()) && + ( 0 < MULTI_MEASUREMENTS || !BTDeviceRegistry::isDeviceProcessed(device->getAddressAndType()) ) ) ) ) { deviceReadyCount++; fprintf_td(stderr, "****** READY-0: Processing[%d] %s\n", deviceReadyCount.load(), device->toString(true).c_str()); - addToDevicesProcessing(device->getAddressAndType()); + BTDeviceRegistry::addToDevicesProcessing(device->getAddressAndType(), device->getName()); processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution! } else { fprintf_td(stderr, "****** READY-1: NOP %s\n", device->toString(true).c_str()); @@ -437,7 +300,7 @@ class MyAdapterStatusListener : public AdapterStatusListener { std::thread dc(::removeDevice, device); // @suppress("Invalid arguments") dc.detach(); } else { - removeFromDevicesProcessing(device->getAddressAndType()); + BTDeviceRegistry::removeFromDevicesProcessing(device->getAddressAndType()); } if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount % RESET_ADAPTER_EACH_CONN ) { std::thread dc(::resetAdapter, &device->getAdapter(), 1); // @suppress("Invalid arguments") @@ -523,7 +386,7 @@ static void connectDiscoveredDevice(std::shared_ptr<BTDevice> device) { fprintf_td(stderr, "****** Connecting Device: stopDiscovery result %s\n", to_string(r).c_str()); } - const MyBTSecurityDetail* sec = MyBTSecurityDetail::get(device->getAddressAndType()); + const BTSecurityRegistry::Entry* sec = BTSecurityRegistry::get(device->getAddressAndType().address); const BTSecurityLevel req_sec_level = nullptr != sec ? sec->getSecLevel() : BTSecurityLevel::UNSET; HCIStatusCode res = SMPKeyBin::readAndApply(KEY_PATH, *device, req_sec_level, true /* verbose */); fprintf_td(stderr, "****** Connecting Device: SMPKeyBin::readAndApply(..) result %s\n", to_string(res).c_str()); @@ -553,7 +416,7 @@ static void connectDiscoveredDevice(std::shared_ptr<BTDevice> device) { } fprintf_td(stderr, "****** Connecting Device: End result %s of %s\n", to_string(res).c_str(), device->toString().c_str()); - if( !USE_WHITELIST && 0 == getDeviceProcessingCount() && HCIStatusCode::SUCCESS != res ) { + if( !USE_WHITELIST && 0 == BTDeviceRegistry::getDeviceProcessingCount() && HCIStatusCode::SUCCESS != res ) { startDiscovery(&device->getAdapter(), "post-connect"); } } @@ -685,11 +548,11 @@ static void processReadyDevice(std::shared_ptr<BTDevice> device) { exit: fprintf_td(stderr, "****** Processing Ready Device: End-1: Success %d on %s; devInProc %zu\n", - success, device->toString().c_str(), getDeviceProcessingCount()); + success, device->toString().c_str(), BTDeviceRegistry::getDeviceProcessingCount()); - removeFromDevicesProcessing(device->getAddressAndType()); + BTDeviceRegistry::removeFromDevicesProcessing(device->getAddressAndType()); - if( !USE_WHITELIST && 0 == getDeviceProcessingCount() ) { + if( !USE_WHITELIST && 0 == BTDeviceRegistry::getDeviceProcessingCount() ) { startDiscovery(&device->getAdapter(), "post-processing-1"); } @@ -707,10 +570,10 @@ exit: } fprintf_td(stderr, "****** Processing Ready Device: End-2: Success %d on %s; devInProc %zu\n", - success, device->toString().c_str(), getDeviceProcessingCount()); + success, device->toString().c_str(), BTDeviceRegistry::getDeviceProcessingCount()); if( success ) { - addToDevicesProcessed(device->getAddressAndType()); + BTDeviceRegistry::addToDevicesProcessed(device->getAddressAndType(), device->getName()); } device->removeAllCharListener(); @@ -724,7 +587,7 @@ exit: if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount % RESET_ADAPTER_EACH_CONN ) { resetAdapter(&device->getAdapter(), 2); - } else if( !USE_WHITELIST && 0 == getDeviceProcessingCount() ) { + } else if( !USE_WHITELIST && 0 == BTDeviceRegistry::getDeviceProcessingCount() ) { startDiscovery(&device->getAdapter(), "post-processing-2"); } } @@ -739,11 +602,11 @@ static void removeDevice(std::shared_ptr<BTDevice> device) { fprintf_td(stderr, "****** Remove Device: removing: %s\n", device->getAddressAndType().toString().c_str()); device->getAdapter().stopDiscovery(); - removeFromDevicesProcessing(device->getAddressAndType()); + BTDeviceRegistry::removeFromDevicesProcessing(device->getAddressAndType()); device->remove(); - if( !USE_WHITELIST && 0 == getDeviceProcessingCount() ) { + if( !USE_WHITELIST && 0 == BTDeviceRegistry::getDeviceProcessingCount() ) { startDiscovery(&device->getAdapter(), "post-remove-device"); } } @@ -754,7 +617,7 @@ static void resetAdapter(BTAdapter *a, int mode) { fprintf_td(stderr, "****** Reset Adapter: reset[%d] end: %s, %s\n", mode, to_string(res).c_str(), a->toString().c_str()); } -static bool le_scan_active = false; // default value +static bool le_scan_active = true; // default value static const uint16_t le_scan_interval = 24; // default value static const uint16_t le_scan_window = 24; // default value static const uint8_t filter_policy = 0; // default value @@ -774,15 +637,7 @@ static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) { // Flush discovered devices after registering our status listener. // This avoids discovered devices before we have registered! - if( 0 == waitForDevices.size() ) { - // we accept all devices, so flush all discovered devices - adapter->removeDiscoveredDevices(); - } else { - // only flush discovered devices we intend to listen to - jau::for_each(waitForDevices.begin(), waitForDevices.end(), [&adapter](const BDAddressAndType &mac) { - adapter->removeDiscoveredDevice(mac); - }); - } + adapter->removeDiscoveredDevices(); if( USE_WHITELIST ) { for (auto it = WHITELIST.begin(); it != WHITELIST.end(); ++it) { @@ -820,13 +675,13 @@ void test() { while( !done ) { if( 0 == MULTI_MEASUREMENTS || - ( -1 == MULTI_MEASUREMENTS && !waitForDevices.empty() && allDevicesProcessed(waitForDevices) ) + ( -1 == MULTI_MEASUREMENTS && BTDeviceRegistry::isWaitingForAnyDevice() && BTDeviceRegistry::allDevicesProcessed() ) ) { fprintf_td(stderr, "****** EOL Test MULTI_MEASUREMENTS left %d, processed %zu/%zu\n", - MULTI_MEASUREMENTS.load(), getDeviceProcessedCount(), (size_t)waitForDevices.size()); - printList("****** WaitForDevice ", waitForDevices); - printDevicesProcessed("****** DevicesProcessed "); + MULTI_MEASUREMENTS.load(), BTDeviceRegistry::getDeviceProcessedCount(), BTDeviceRegistry::getWaitForDevicesCount()); + BTDeviceRegistry::printWaitForDevices(stderr, "****** WaitForDevice "); + BTDeviceRegistry::printDevicesProcessed(stderr, "****** DevicesProcessed "); done = true; } else { std::this_thread::sleep_for(std::chrono::milliseconds(2000)); @@ -885,44 +740,35 @@ int main(int argc, char *argv[]) SHOW_UPDATE_EVENTS = true; } else if( !strcmp("-quiet", argv[i]) ) { QUIET = true; - } else if( !strcmp("-scanActive", argv[i]) ) { - le_scan_active = true; - } else if( !strcmp("-mac", argv[i]) && argc > (i+1) ) { - std::string macstr = std::string(argv[++i]); - BDAddressAndType mac(EUI48(macstr), BDAddressType::BDADDR_UNDEFINED); - waitForDevices.push_back( mac ); + } else if( !strcmp("-scanPassive", argv[i]) ) { + le_scan_active = false; + } else if( !strcmp("-dev", argv[i]) && argc > (i+1) ) { + std::string addrOrNameSub = std::string(argv[++i]); + BTDeviceRegistry::addToWaitForDevices( addrOrNameSub ); } else if( !strcmp("-wl", argv[i]) && argc > (i+1) ) { std::string macstr = std::string(argv[++i]); BDAddressAndType wle(EUI48(macstr), BDAddressType::BDADDR_LE_PUBLIC); fprintf(stderr, "Whitelist + %s\n", wle.toString().c_str()); WHITELIST.push_back( wle ); USE_WHITELIST = true; - } else if( !strcmp("-passkey", argv[i]) && argc > (i+3) ) { - const char* mac = argv[++i]; - const uint8_t atype = (uint8_t) ( atoi(argv[++i]) & 0xff ); - const BDAddressAndType macAndType(EUI48(mac), to_BDAddressType(atype)); - MyBTSecurityDetail* sec = MyBTSecurityDetail::getOrCreate(macAndType); + } else if( !strcmp("-passkey", argv[i]) && argc > (i+2) ) { + const std::string addrOrNameSub(argv[++i]); + BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getOrCreate(addrOrNameSub); sec->passkey = atoi(argv[++i]); fprintf(stderr, "Set passkey in %s\n", sec->toString().c_str()); - } else if( !strcmp("-seclevel", argv[i]) && argc > (i+3) ) { - const char* mac = argv[++i]; - const uint8_t atype = (uint8_t) ( atoi(argv[++i]) & 0xff ); - const BDAddressAndType macAndType(EUI48(mac), to_BDAddressType(atype)); - MyBTSecurityDetail* sec = MyBTSecurityDetail::getOrCreate(macAndType); + } else if( !strcmp("-seclevel", argv[i]) && argc > (i+2) ) { + const std::string addrOrNameSub(argv[++i]); + BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getOrCreate(addrOrNameSub); sec->sec_level = to_BTSecurityLevel(atoi(argv[++i])); fprintf(stderr, "Set sec_level in %s\n", sec->toString().c_str()); - } else if( !strcmp("-iocap", argv[i]) && argc > (i+3) ) { - const char* mac = argv[++i]; - const uint8_t atype = (uint8_t) ( atoi(argv[++i]) & 0xff ); - const BDAddressAndType macAndType(EUI48(mac), to_BDAddressType(atype)); - MyBTSecurityDetail* sec = MyBTSecurityDetail::getOrCreate(macAndType); + } else if( !strcmp("-iocap", argv[i]) && argc > (i+2) ) { + const std::string addrOrNameSub(argv[++i]); + BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getOrCreate(addrOrNameSub); sec->io_cap = to_SMPIOCapability(atoi(argv[++i])); fprintf(stderr, "Set io_cap in %s\n", sec->toString().c_str()); - } else if( !strcmp("-secauto", argv[i]) && argc > (i+3) ) { - const char* mac = argv[++i]; - const uint8_t atype = (uint8_t) ( atoi(argv[++i]) & 0xff ); - const BDAddressAndType macAndType(EUI48(mac), to_BDAddressType(atype)); - MyBTSecurityDetail* sec = MyBTSecurityDetail::getOrCreate(macAndType); + } else if( !strcmp("-secauto", argv[i]) && argc > (i+2) ) { + const std::string addrOrNameSub(argv[++i]); + BTSecurityRegistry::Entry* sec = BTSecurityRegistry::getOrCreate(addrOrNameSub); sec->io_cap_auto = to_SMPIOCapability(atoi(argv[++i])); fprintf(stderr, "Set SEC AUTO security io_cap in %s\n", sec->toString().c_str()); } else if( !strcmp("-charid", argv[i]) && argc > (i+1) ) { @@ -947,13 +793,13 @@ int main(int argc, char *argv[]) fprintf(stderr, "Run with '[-btmode LE|BREDR|DUAL] " "[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] " - "[-scanActive]" + "[-scanPassive]" "[-resetEachCon connectionCount] " - "(-mac <device_address>)* (-wl <device_address>)* " - "[-seclevel <device_address> <(int)address_type> <int>] " - "[-iocap <device_address> <(int)address_type> <int>] " - "[-secauto <device_address> <(int)address_type> <int>] " - "[-passkey <device_address> <(int)address_type> <digits>] " + "(-dev <device_[address|name]_sub>)* (-wl <device_address>)* " + "(-seclevel <device_[address|name]_sub> <int_sec_level>)* " + "(-iocap <device_[address|name]_sub> <int_iocap>)* " + "(-secauto <device_[address|name]_sub> <int_iocap>)* " + "(-passkey <device_[address|name]_sub> <digits>)* " "[-unpairPre] [-unpairPost] " "[-charid <uuid>] [-charval <byte-val>] " "[-dbt_verbose true|false] " @@ -977,8 +823,8 @@ int main(int argc, char *argv[]) fprintf(stderr, "characteristic-id: %s\n", charIdentifier.c_str()); fprintf(stderr, "characteristic-value: %d\n", charValue); - fprintf(stderr, "security-details: %s\n", MyBTSecurityDetail::allToString().c_str()); - printList( "waitForDevice: ", waitForDevices); + fprintf(stderr, "security-details: %s\n", BTSecurityRegistry::allToString().c_str()); + BTDeviceRegistry::printWaitForDevices(stderr, "waitForDevice: "); if( waitForEnter ) { diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java index 9839e3f6..6daf1ee4 100644 --- a/examples/java/DBTScanner10.java +++ b/examples/java/DBTScanner10.java @@ -26,10 +26,6 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; @@ -44,6 +40,7 @@ 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.BTException; import org.direct_bt.BTFactory; import org.direct_bt.BTGattChar; @@ -51,6 +48,7 @@ import org.direct_bt.BTGattDesc; import org.direct_bt.BTGattService; import org.direct_bt.BTManager; import org.direct_bt.BTNotification; +import org.direct_bt.BTSecurityRegistry; import org.direct_bt.BTType; import org.direct_bt.BTUtils; import org.direct_bt.EIRDataTypeSet; @@ -61,7 +59,6 @@ import org.direct_bt.HCIWhitelistConnectType; import org.direct_bt.PairingMode; import org.direct_bt.SMPIOCapability; import org.direct_bt.SMPKeyBin; -import org.direct_bt.SMPKeyMask; import org.direct_bt.SMPPairingState; import org.direct_bt.ScanType; @@ -74,13 +71,13 @@ import jau.direct_bt.DBTManager; * <p> * This example represents the recommended utilization of Direct-BT. * </p> + * <p> + * See `dbt_scanner10.cpp` for invocation examples, since both apps are fully compatible. + * </p> */ public class DBTScanner10 { - static final int NO_PASSKEY = -1; static final String KEY_PATH = "keys"; - final List<BDAddressAndType> waitForDevices = new ArrayList<BDAddressAndType>(); - long timestamp_t0; int RESET_ADAPTER_EACH_CONN = 0; @@ -137,53 +134,6 @@ public class DBTScanner10 { } } - static public class MyBTSecurityDetail { - public final BDAddressAndType addrAndType; - - BTSecurityLevel sec_level = BTSecurityLevel.UNSET; - SMPIOCapability io_cap = SMPIOCapability.UNSET; - SMPIOCapability io_cap_auto = SMPIOCapability.UNSET; - int passkey = NO_PASSKEY; - - public MyBTSecurityDetail(final BDAddressAndType addrAndType) { this.addrAndType = addrAndType; } - - public boolean isSecLevelOrIOCapSet() { - return SMPIOCapability.UNSET != io_cap || BTSecurityLevel.UNSET != sec_level; - } - public BTSecurityLevel getSecLevel() { return sec_level; } - public SMPIOCapability getIOCap() { return io_cap; } - - public boolean isSecurityAutoEnabled() { return SMPIOCapability.UNSET != io_cap_auto; } - public SMPIOCapability getSecurityAutoIOCap() { return io_cap_auto; } - - public int getPairingPasskey() { return passkey; } - - public boolean getPairingNumericComparison() { return true; } - - @Override - public String toString() { - return "BTSecurityDetail["+addrAndType+", lvl "+sec_level+", io "+io_cap+", auto-io "+io_cap_auto+", passkey "+passkey+"]"; - } - - static private HashMap<BDAddressAndType, MyBTSecurityDetail> devicesSecDetail = new HashMap<BDAddressAndType, MyBTSecurityDetail>(); - - static public MyBTSecurityDetail get(final BDAddressAndType addrAndType) { - return devicesSecDetail.get(addrAndType); - } - static public MyBTSecurityDetail getOrCreate(final BDAddressAndType addrAndType) { - MyBTSecurityDetail sec_detail = devicesSecDetail.get(addrAndType); - if( null == sec_detail ) { - sec_detail = new MyBTSecurityDetail(addrAndType); - devicesSecDetail.put(addrAndType, sec_detail); - } - return sec_detail; - } - static public String allToString() { return Arrays.toString( devicesSecDetail.values().toArray() ); } - } - - Collection<BDAddressAndType> devicesInProcessing = Collections.synchronizedCollection(new HashSet<>()); - Collection<BDAddressAndType> devicesProcessed = Collections.synchronizedCollection(new HashSet<>()); - final AdapterStatusListener statusListener = new AdapterStatusListener() { @Override public void adapterSettingsChanged(final BTAdapter adapter, final AdapterSettings oldmask, @@ -221,10 +171,10 @@ public class DBTScanner10 { BTUtils.println(System.err, "****** FOUND__-2: Skip non 'public LE' and non 'random static public LE' "+device.toString()); return false; } - if( !devicesInProcessing.contains( device.getAddressAndType() ) && - ( waitForDevices.isEmpty() || - ( matches(waitForDevices, device.getAddressAndType() ) && - ( 0 < MULTI_MEASUREMENTS.get() || !devicesProcessed.contains( device.getAddressAndType() ) ) + if( !BTDeviceRegistry.isDeviceProcessing( device.getAddressAndType() ) && + ( !BTDeviceRegistry.isWaitingForAnyDevice() || + ( BTDeviceRegistry.isWaitingForDevice(device.getAddressAndType(), device.getName()) && + ( 0 < MULTI_MEASUREMENTS.get() || !BTDeviceRegistry.isDeviceProcessed(device.getAddressAndType()) ) ) ) ) @@ -277,8 +227,8 @@ public class DBTScanner10 { // next: PASSKEY_EXPECTED... or KEY_DISTRIBUTION break; case PASSKEY_EXPECTED: { - final MyBTSecurityDetail sec = MyBTSecurityDetail.get(device.getAddressAndType()); - if( null != sec && sec.getPairingPasskey() != NO_PASSKEY ) { + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.get(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 */); @@ -287,7 +237,7 @@ public class DBTScanner10 { // next: KEY_DISTRIBUTION or FAILED } break; case NUMERIC_COMPARE_EXPECTED: { - final MyBTSecurityDetail sec = MyBTSecurityDetail.get(device.getAddressAndType()); + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.get(device.getAddressAndType().address); if( null != sec ) { executeOffThread( () -> { device.setPairingNumericComparison( sec.getPairingNumericComparison() ); }, "DBT-SetNumericComp-"+device.getAddressAndType(), true /* detach */); } else { @@ -311,10 +261,10 @@ public class DBTScanner10 { @Override public void deviceReady(final BTDevice device, final long timestamp) { - if( !devicesInProcessing.contains( device.getAddressAndType() ) && - ( waitForDevices.isEmpty() || - ( matches(waitForDevices, device.getAddressAndType() ) && - ( 0 < MULTI_MEASUREMENTS.get() || !devicesProcessed.contains( device.getAddressAndType() ) ) + if( !BTDeviceRegistry.isDeviceProcessing( device.getAddressAndType() ) && + ( !BTDeviceRegistry.isWaitingForAnyDevice() || + ( BTDeviceRegistry.isWaitingForDevice(device.getAddressAndType(), device.getName()) && + ( 0 < MULTI_MEASUREMENTS.get() || !BTDeviceRegistry.isDeviceProcessed(device.getAddressAndType()) ) ) ) ) @@ -325,7 +275,7 @@ public class DBTScanner10 { final long td = BTUtils.currentTimeMillis() - timestamp_t0; // adapter-init -> now BTUtils.println(System.err, "PERF: adapter-init -> READY-0 " + td + " ms"); } - devicesInProcessing.add(device.getAddressAndType()); + BTDeviceRegistry.addToDevicesProcessing(device.getAddressAndType(), device.getName()); processReadyDevice(device); // AdapterStatusListener::deviceReady() explicitly allows prolonged and complex code execution! } else { BTUtils.println(System.err, "****** READY-1: NOP " + device.toString()); @@ -339,7 +289,7 @@ public class DBTScanner10 { if( REMOVE_DEVICE ) { executeOffThread( () -> { removeDevice(device); }, "DBT-Remove-"+device.getAddressAndType(), true /* detach */); } else { - devicesInProcessing.remove(device.getAddressAndType()); + BTDeviceRegistry.removeFromDevicesProcessing(device.getAddressAndType()); } if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) { executeOffThread( () -> { resetAdapter(device.getAdapter(), 1); }, @@ -384,7 +334,7 @@ public class DBTScanner10 { BTUtils.println(System.err, "****** Connecting Device: stopDiscovery result "+r); } - final MyBTSecurityDetail sec = MyBTSecurityDetail.get(device.getAddressAndType()); + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.get(device.getAddressAndType().address); final BTSecurityLevel req_sec_level = null != sec ? sec.getSecLevel() : BTSecurityLevel.UNSET; HCIStatusCode res = SMPKeyBin.readAndApply(KEY_PATH, device, req_sec_level, true /* verbose */); BTUtils.fprintf_td(System.err, "****** Connecting Device: SMPKeyBin::readAndApply(..) result %s\n", res.toString()); @@ -414,7 +364,7 @@ public class DBTScanner10 { } BTUtils.println(System.err, "****** Connecting Device Command, res "+res+": End result "+res+" of " + device.toString()); - if( !USE_WHITELIST && 0 == devicesInProcessing.size() && HCIStatusCode.SUCCESS != res ) { + if( !USE_WHITELIST && 0 == BTDeviceRegistry.getDeviceProcessingCount() && HCIStatusCode.SUCCESS != res ) { startDiscovery(device.getAdapter(), "post-connect"); } } @@ -596,11 +546,11 @@ public class DBTScanner10 { } BTUtils.println(System.err, "****** Processing Ready Device: End-1: Success " + success + - " on " + device.toString() + "; devInProc "+devicesInProcessing.size()); + " on " + device.toString() + "; devInProc "+BTDeviceRegistry.getDeviceProcessingCount()); - devicesInProcessing.remove(device.getAddressAndType()); + BTDeviceRegistry.removeFromDevicesProcessing( device.getAddressAndType() ); - if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { + if( !USE_WHITELIST && 0 == BTDeviceRegistry.getDeviceProcessingCount() ) { startDiscovery(device.getAdapter(), "post-processing-1"); } @@ -618,9 +568,9 @@ public class DBTScanner10 { } BTUtils.println(System.err, "****** Processing Ready Device: End-2: Success " + success + - " on " + device.toString() + "; devInProc "+devicesInProcessing.size()); + " on " + device.toString() + "; devInProc "+BTDeviceRegistry.getDeviceProcessingCount()); if( success ) { - devicesProcessed.add(device.getAddressAndType()); + BTDeviceRegistry.addToDevicesProcessed(device.getAddressAndType(), device.getName()); } device.removeAllCharListener(); @@ -635,7 +585,7 @@ public class DBTScanner10 { if( 0 < RESET_ADAPTER_EACH_CONN && 0 == deviceReadyCount.get() % RESET_ADAPTER_EACH_CONN ) { resetAdapter(device.getAdapter(), 2); - } else if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { + } else if( !USE_WHITELIST && 0 == BTDeviceRegistry.getDeviceProcessingCount() ) { startDiscovery(device.getAdapter(), "post-processing-2"); } } @@ -650,11 +600,11 @@ public class DBTScanner10 { BTUtils.println(System.err, "****** Remove Device: removing: "+device.getAddressAndType()); device.getAdapter().stopDiscovery(); - devicesInProcessing.remove(device.getAddressAndType()); + BTDeviceRegistry.removeFromDevicesProcessing(device.getAddressAndType()); device.remove(); - if( !USE_WHITELIST && 0 == devicesInProcessing.size() ) { + if( !USE_WHITELIST && 0 == BTDeviceRegistry.getDeviceProcessingCount() ) { startDiscovery(device.getAdapter(), "post-remove-device"); } } @@ -665,7 +615,7 @@ public class DBTScanner10 { BTUtils.println(System.err, "****** Reset Adapter: reset["+mode+"] end: "+res+", "+adapter.toString()); } - static boolean le_scan_active = false; // default value + static boolean le_scan_active = true; // default value static final short le_scan_interval = (short)24; // default value static final short le_scan_window = (short)24; // default value static final byte filter_policy = (byte)0; // default value @@ -693,15 +643,7 @@ public class DBTScanner10 { // Flush discovered devices after registering our status listener. // This avoids discovered devices before we have registered! - if( 0 == waitForDevices.size() ) { - // we accept all devices, so flush all discovered devices - adapter.removeDiscoveredDevices(); - } else { - // only flush discovered devices we intend to listen to - for( final Iterator<BDAddressAndType> iter=waitForDevices.iterator(); iter.hasNext(); ) { - adapter.removeDiscoveredDevice( iter.next() ); - } - } + adapter.removeDiscoveredDevices(); if( USE_WHITELIST ) { for(final Iterator<BDAddressAndType> wliter = whitelist.iterator(); wliter.hasNext(); ) { @@ -743,13 +685,13 @@ public class DBTScanner10 { while( !done ) { if( 0 == MULTI_MEASUREMENTS.get() || - ( -1 == MULTI_MEASUREMENTS.get() && !waitForDevices.isEmpty() && devicesProcessed.containsAll(waitForDevices) ) + ( -1 == MULTI_MEASUREMENTS.get() && BTDeviceRegistry.isWaitingForAnyDevice() && BTDeviceRegistry.allDevicesProcessed() ) ) { BTUtils.println(System.err, "****** EOL Test MULTI_MEASUREMENTS left "+MULTI_MEASUREMENTS.get()+ - ", processed "+devicesProcessed.size()+"/"+waitForDevices.size()); - BTUtils.println(System.err, "****** WaitForDevices "+Arrays.toString(waitForDevices.toArray())); - BTUtils.println(System.err, "****** DevicesProcessed "+Arrays.toString(devicesProcessed.toArray())); + ", processed "+BTDeviceRegistry.getDeviceProcessedCount()+"/"+BTDeviceRegistry.getWaitForDevicesCount()); + BTDeviceRegistry.printWaitForDevices(System.err, "****** WaitForDevices "); + BTDeviceRegistry.printDevicesProcessed(System.err, "****** DevicesProcessed "); done = true; } else { try { @@ -836,46 +778,37 @@ public class DBTScanner10 { test.SHOW_UPDATE_EVENTS = true; } else if( arg.equals("-quiet") ) { test.QUIET = true; - } else if( arg.equals("-scanActive") ) { - le_scan_active = true; + } else if( arg.equals("-scanPassive") ) { + le_scan_active = false; } else if( arg.equals("-shutdown") && args.length > (i+1) ) { test.shutdownTest = Integer.valueOf(args[++i]).intValue(); - } else if( arg.equals("-mac") && args.length > (i+1) ) { - final BDAddressAndType a = new BDAddressAndType(new EUI48(args[++i]), BDAddressType.BDADDR_UNDEFINED); - test.waitForDevices.add(a); + } else if( arg.equals("-dev") && args.length > (i+1) ) { + BTDeviceRegistry.addToWaitForDevices( args[++i] ); } else if( arg.equals("-wl") && args.length > (i+1) ) { final BDAddressAndType wle = new BDAddressAndType(new EUI48(args[++i]), BDAddressType.BDADDR_LE_PUBLIC); BTUtils.println(System.err, "Whitelist + "+wle); test.whitelist.add(wle); test.USE_WHITELIST = true; - } else if( arg.equals("-passkey") && args.length > (i+3) ) { - final String mac = args[++i]; - final byte atype = (byte) ( Integer.valueOf(args[++i]).intValue() & 0xff ); - final BDAddressAndType macAndType = new BDAddressAndType(new EUI48(mac), BDAddressType.get(atype)); - final MyBTSecurityDetail sec = MyBTSecurityDetail.getOrCreate(macAndType); + } else if( arg.equals("-passkey") && args.length > (i+2) ) { + final String addrOrNameSub = args[++i]; + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub); sec.passkey = Integer.valueOf(args[++i]).intValue(); System.err.println("Set passkey in "+sec); - } else if( arg.equals("-seclevel") && args.length > (i+3) ) { - final String mac = args[++i]; - final byte atype = (byte) ( Integer.valueOf(args[++i]).intValue() & 0xff ); - final BDAddressAndType macAndType = new BDAddressAndType(new EUI48(mac), BDAddressType.get(atype)); - final MyBTSecurityDetail sec = MyBTSecurityDetail.getOrCreate(macAndType); + } else if( arg.equals("-seclevel") && args.length > (i+2) ) { + final String addrOrNameSub = args[++i]; + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub); final int sec_level_i = Integer.valueOf(args[++i]).intValue(); sec.sec_level = BTSecurityLevel.get( (byte)( sec_level_i & 0xff ) ); System.err.println("Set sec_level "+sec_level_i+" in "+sec); - } else if( arg.equals("-iocap") && args.length > (i+3) ) { - final String mac = args[++i]; - final byte atype = (byte) ( Integer.valueOf(args[++i]).intValue() & 0xff ); - final BDAddressAndType macAndType = new BDAddressAndType(new EUI48(mac), BDAddressType.get(atype)); - final MyBTSecurityDetail sec = MyBTSecurityDetail.getOrCreate(macAndType); + } else if( arg.equals("-iocap") && args.length > (i+2) ) { + final String addrOrNameSub = args[++i]; + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub); final int io_cap_i = Integer.valueOf(args[++i]).intValue(); sec.io_cap = SMPIOCapability.get( (byte)( io_cap_i & 0xff ) ); System.err.println("Set io_cap "+io_cap_i+" in "+sec); - } else if( arg.equals("-secauto") && args.length > (i+3) ) { - final String mac = args[++i]; - final byte atype = (byte) ( Integer.valueOf(args[++i]).intValue() & 0xff ); - final BDAddressAndType macAndType = new BDAddressAndType(new EUI48(mac), BDAddressType.get(atype)); - final MyBTSecurityDetail sec = MyBTSecurityDetail.getOrCreate(macAndType); + } else if( arg.equals("-secauto") && args.length > (i+2) ) { + final String addrOrNameSub = args[++i]; + final BTSecurityRegistry.Entry sec = BTSecurityRegistry.getOrCreate(addrOrNameSub); final int io_cap_i = Integer.valueOf(args[++i]).intValue(); sec.io_cap_auto = SMPIOCapability.get( (byte)( io_cap_i & 0xff ) ); System.err.println("Set SEC AUTO security io_cap "+io_cap_i+" in "+sec); @@ -900,13 +833,13 @@ public class DBTScanner10 { BTUtils.println(System.err, "Run with '[-btmode LE|BREDR|DUAL] "+ "[-bluetoothManager <BluetoothManager-Implementation-Class-Name>] "+ "[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] "+ - "[-scanActive]"+ + "[-scanPassive]"+ "[-resetEachCon connectionCount] "+ - "(-mac <device_address>)* (-wl <device_address>)* "+ - "[-seclevel <device_address> <(int)address_type> <int>] "+ - "[-iocap <device_address> <(int)address_type> <int>] "+ - "[-secauto <device_address> <(int)address_type> <int>] "+ - "[-passkey <device_address> <(int)address_type> <digits>] " + + "(-dev <device_[address|name]_sub>)* (-wl <device_address>)* "+ + "(-seclevel <device_[address|name]_sub> <int_sec_level>)* "+ + "(-iocap <device_[address|name]_sub> <int_iocap>)* "+ + "(-secauto <device_[address|name]_sub> <int_iocap>)* "+ + "(-passkey <device_[address|name]_sub> <digits>)* "+ "[-charid <uuid>] [-charval <byte-val>] "+ "[-verbose] [-debug] "+ "[-dbt_verbose true|false] "+ @@ -929,9 +862,9 @@ public class DBTScanner10 { BTUtils.println(System.err, "characteristic-id: "+test.charIdentifier); BTUtils.println(System.err, "characteristic-value: "+test.charValue); - BTUtils.println(System.err, "security-details: "+MyBTSecurityDetail.allToString() ); + BTUtils.println(System.err, "security-details: "+BTSecurityRegistry.allToString() ); - BTUtils.println(System.err, "waitForDevice: "+Arrays.toString(test.waitForDevices.toArray())); + BTDeviceRegistry.printWaitForDevices(System.err, "waitForDevices: "); if( waitForEnter ) { BTUtils.println(System.err, "Press ENTER to continue\n"); diff --git a/java/org/direct_bt/BTDeviceRegistry.java b/java/org/direct_bt/BTDeviceRegistry.java new file mode 100644 index 00000000..c9779b4f --- /dev/null +++ b/java/org/direct_bt/BTDeviceRegistry.java @@ -0,0 +1,165 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * 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 org.direct_bt; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +/** + * Application toolkit providing BT device registration of processed and awaited devices. + * The latter on a pattern matching basis, i.e. EUI48Sub or name-sub. + */ +public class BTDeviceRegistry { + private static class DeviceQuery { + public final EUI48Sub addressSub; + public final String nameSub; + public DeviceQuery(final EUI48Sub as, final String ns) { + addressSub = as; + nameSub = ns; + } + @Override + public String toString() { + if( addressSub.length>0 ) { + return addressSub.toString(); + } else { + return "'"+nameSub+"'"; + } + } + }; + private static List<DeviceQuery> waitForDevices = new ArrayList<DeviceQuery>(); + + private static class DeviceID { + public final BDAddressAndType addressAndType; + public final String name; + public DeviceID(final BDAddressAndType a, final String n) { + addressAndType = a; + name = n; + } + @Override + public String toString() { + return "["+addressAndType+", "+name+"]"; + } + }; + private static Collection<DeviceID> devicesInProcessing = Collections.synchronizedCollection(new ArrayList<>()); + private static Collection<DeviceID> devicesProcessed = Collections.synchronizedCollection(new ArrayList<>()); + + public static void addToWaitForDevices(final String addrOrNameSub) { + final EUI48Sub addr1 = new EUI48Sub(); + final StringBuilder errmsg = new StringBuilder(); + if( EUI48Sub.scanEUI48Sub(addrOrNameSub, addr1, errmsg) ) { + waitForDevices.add( new DeviceQuery( addr1, "" ) ); + } else { + addr1.clear(); + waitForDevices.add( new DeviceQuery( addr1, addrOrNameSub ) ); + } + } + public static boolean isWaitingForDevice(final BDAddressAndType mac, final String name) { + for(final Iterator<DeviceQuery> it=waitForDevices.iterator(); it.hasNext(); ) { + final DeviceQuery q = it.next(); + if( ( q.addressSub.length>0 && mac.address.contains(q.addressSub) ) || + ( q.nameSub.length()>0 && name.indexOf(q.nameSub) >= 0 ) ) { + return true; + } + } + return false; + } + public static boolean isWaitingForAnyDevice() { + return waitForDevices.size()>0; + } + public static int getWaitForDevicesCount() { + return waitForDevices.size(); + } + public static void printWaitForDevices(final PrintStream out, final String msg) { + BTUtils.println(out, msg+" "+Arrays.toString(waitForDevices.toArray())); + } + + public static void addToDevicesProcessed(final BDAddressAndType a, final String n) { + devicesProcessed.add( new DeviceID(a, n) ); + } + public static boolean isDeviceProcessed(final BDAddressAndType a) { + for(final Iterator<DeviceID> it=devicesProcessed.iterator(); it.hasNext(); ) { + final DeviceID id = it.next(); + if( id.addressAndType.equals(a) ) { + return true; + } + } + return false; + } + public static int getDeviceProcessedCount() { + return devicesProcessed.size(); + } + public static boolean allDevicesProcessed() { + for(final Iterator<DeviceQuery> it1=waitForDevices.iterator(); it1.hasNext(); ) { + final DeviceQuery q = it1.next(); + final Iterator<DeviceID> it2=devicesProcessed.iterator(); + while( it2.hasNext() ) { + final DeviceID id = it2.next(); + if( ( q.addressSub.length>0 && id.addressAndType.address.contains(q.addressSub) ) || + ( q.nameSub.length()>0 && id.name.indexOf(q.nameSub) >= 0 ) ) { + break; + } + } + if( !it2.hasNext() ) { + return false; + } + } + return true; + } + public static void printDevicesProcessed(final PrintStream out, final String msg) { + BTUtils.println(out, msg+" "+Arrays.toString(devicesProcessed.toArray())); + } + + public static void addToDevicesProcessing(final BDAddressAndType a, final String n) { + devicesInProcessing.add( new DeviceID(a, n) ); + } + public static boolean removeFromDevicesProcessing(final BDAddressAndType a) { + for(final Iterator<DeviceID> it=devicesInProcessing.iterator(); it.hasNext(); ) { + final DeviceID id = it.next(); + if ( id.addressAndType.equals(a) ) { + it.remove(); + return true; + } + } + return false; + } + public static boolean isDeviceProcessing(final BDAddressAndType a) { + for(final Iterator<DeviceID> it=devicesInProcessing.iterator(); it.hasNext(); ) { + final DeviceID id = it.next(); + if ( id.addressAndType.equals(a) ) { + return true; + } + } + return false; + } + public static int getDeviceProcessingCount() { + return devicesInProcessing.size(); + } +} diff --git a/java/org/direct_bt/BTSecurityRegistry.java b/java/org/direct_bt/BTSecurityRegistry.java new file mode 100644 index 00000000..adbf16e5 --- /dev/null +++ b/java/org/direct_bt/BTSecurityRegistry.java @@ -0,0 +1,141 @@ +/** + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * 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 org.direct_bt; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +/** + * Application toolkit providing BT security setup and its device association + * on a pattern matching basis, i.e. EUI48Sub or name-sub. + */ +public class BTSecurityRegistry { + public static final int NO_PASSKEY = -1; + + public static class Entry { + public EUI48Sub addrSub; + public String nameSub; + + public BTSecurityLevel sec_level = BTSecurityLevel.UNSET; + public SMPIOCapability io_cap = SMPIOCapability.UNSET; + public SMPIOCapability io_cap_auto = SMPIOCapability.UNSET; + public int passkey = NO_PASSKEY; + + public Entry(final EUI48Sub addrSub) { + this.addrSub = addrSub; + this.nameSub = ""; + } + public Entry(final String nameSub) { + this.addrSub = EUI48Sub.ALL_DEVICE; + this.nameSub = nameSub; + } + + public boolean matches(final EUI48Sub addressSub) { + return addrSub.length > 0 && addressSub.contains(addrSub); + } + public boolean matches(final EUI48 address) { + return addrSub.length > 0 && address.contains(addrSub); + } + public boolean matches(final String name) { + return nameSub.length() > 0 && name.indexOf(nameSub) >= 0; + } + + public boolean isSecLevelOrIOCapSet() { + return SMPIOCapability.UNSET != io_cap || BTSecurityLevel.UNSET != sec_level; + } + public BTSecurityLevel getSecLevel() { return sec_level; } + public SMPIOCapability getIOCap() { return io_cap; } + + public boolean isSecurityAutoEnabled() { return SMPIOCapability.UNSET != io_cap_auto; } + public SMPIOCapability getSecurityAutoIOCap() { return io_cap_auto; } + + public int getPairingPasskey() { return passkey; } + + public boolean getPairingNumericComparison() { return true; } + + @Override + public String toString() { + final String id = EUI48Sub.ALL_DEVICE.equals(addrSub) ? "'"+nameSub+"'" : addrSub.toString(); + return "BTSecurityDetail["+id+", lvl "+sec_level+", io "+io_cap+", auto-io "+io_cap_auto+", passkey "+passkey+"]"; + } + } + + static private List<BTSecurityRegistry.Entry> devicesSecDetails = new ArrayList<BTSecurityRegistry.Entry>(); + + public static BTSecurityRegistry.Entry get(final EUI48 addr) { + for(final Iterator<BTSecurityRegistry.Entry> i=devicesSecDetails.iterator(); i.hasNext(); ) { + final BTSecurityRegistry.Entry sd = i.next(); + if( sd.matches(addr) ) { + return sd; + } + } + return null; + } + public static BTSecurityRegistry.Entry get(final EUI48Sub addrSub) { + for(final Iterator<BTSecurityRegistry.Entry> i=devicesSecDetails.iterator(); i.hasNext(); ) { + final BTSecurityRegistry.Entry sd = i.next(); + if( sd.matches(addrSub) ) { + return sd; + } + } + return null; + } + public static BTSecurityRegistry.Entry get(final String nameSub) { + for(final Iterator<BTSecurityRegistry.Entry> i=devicesSecDetails.iterator(); i.hasNext(); ) { + final BTSecurityRegistry.Entry sd = i.next(); + if( sd.matches(nameSub) ) { + return sd; + } + } + return null; + } + + public static BTSecurityRegistry.Entry getOrCreate(final String addrOrNameSub) { + final EUI48Sub addr1 = new EUI48Sub(); + final StringBuilder errmsg = new StringBuilder(); + BTSecurityRegistry.Entry sec = null; + if( EUI48Sub.scanEUI48Sub(addrOrNameSub, addr1, errmsg) ) { + sec = get(addr1); + if( null == sec ) { + sec = new BTSecurityRegistry.Entry( addr1 ); + devicesSecDetails.add(sec); + } + } else { + sec = get(addrOrNameSub); + if( null == sec ) { + sec = new BTSecurityRegistry.Entry( addrOrNameSub ); + devicesSecDetails.add(sec); + } + } + return sec; + } + + public static String allToString() { + return Arrays.toString( devicesSecDetails.toArray() ); + } +} diff --git a/scripts/run-dbt_scanner10.sh b/scripts/run-dbt_scanner10.sh index e88e46f1..cb7f57b7 100755 --- a/scripts/run-dbt_scanner10.sh +++ b/scripts/run-dbt_scanner10.sh @@ -13,16 +13,22 @@ # ../scripts/run-dbt_scanner10.sh # # Read device C0:26:DA:01:DA:B1 (using default auto-sec w/ keyboard iocap) -# ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 +# ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 # # Read device C0:26:DA:01:DA:B1 (enforcing no security) -# ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 -seclevel C0:26:DA:01:DA:B1 1 1 +# ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 -seclevel C0:26:DA:01:DA:B1 1 +# +# Read any device containing C0:26:DA (enforcing no security) +# ../scripts/run-dbt_scanner10.sh -dev C0:26:DA -seclevel C0:26:DA 1 +# +# Read any device containing name 'TAIDOC' (enforcing no security) +# ../scripts/run-dbt_scanner10.sh -dev 'TAIDOC' -seclevel 'TAIDOC' 1 # # Read device C0:26:DA:01:DA:B1, basic debug flags enabled (using default auto-sec w/ keyboard iocap) -# ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 -dbt_debug true +# ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 -dbt_debug true # # Read device C0:26:DA:01:DA:B1, all debug flags enabled (using default auto-sec w/ keyboard iocap) -# ../scripts/run-dbt_scanner10.sh -mac C0:26:DA:01:DA:B1 -dbt_debug adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event +# ../scripts/run-dbt_scanner10.sh -dev C0:26:DA:01:DA:B1 -dbt_debug adapter.event,gatt.data,hci.event,hci.scan_ad_eir,mgmt.event # # To do a BT adapter removal/add via software, assuming the device is '1-4' (Bus 1.Port 4): # echo '1-4' > /sys/bus/usb/drivers/usb/unbind diff --git a/src/direct_bt/BTDeviceRegistry.cpp b/src/direct_bt/BTDeviceRegistry.cpp new file mode 100644 index 00000000..3d0262a1 --- /dev/null +++ b/src/direct_bt/BTDeviceRegistry.cpp @@ -0,0 +1,174 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * 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 "BTDeviceRegistry.hpp" + +#include <jau/cpp_lang_util.hpp> +#include <jau/basic_algos.hpp> +#include <jau/darray.hpp> + +using namespace direct_bt; + +namespace direct_bt::BTDeviceRegistry { + struct DeviceQuery { + EUI48Sub addressSub; + std::string nameSub; + DeviceQuery(const EUI48Sub& as, const std::string& ns) : addressSub(as), nameSub(ns) {} + DeviceQuery() : addressSub(), nameSub() {} + std::string toString() const { + if( addressSub.length>0 ) { + return addressSub.toString(); + } else { + return "'"+nameSub+"'"; + } + } + }; + static jau::darray<DeviceQuery> waitForDevices; + + struct DeviceID { + BDAddressAndType addressAndType; + std::string name; + DeviceID(const BDAddressAndType& a, const std::string& n) : addressAndType(a), name(n) {} + DeviceID() : addressAndType(), name() {} + std::string toString() const { + return "["+addressAndType.toString()+", '"+name+"']"; + } + }; + static jau::darray<DeviceID> devicesInProcessing; + static std::recursive_mutex mtx_devicesProcessing; + + static jau::darray<DeviceID> devicesProcessed; + static std::recursive_mutex mtx_devicesProcessed; + + void addToWaitForDevices(const std::string& addrOrNameSub) { + EUI48Sub addr1; + std::string errmsg; + if( EUI48Sub::scanEUI48Sub(addrOrNameSub, addr1, errmsg) ) { + waitForDevices.emplace_back( addr1, "" ); + } else { + addr1.clear(); + waitForDevices.emplace_back( addr1, addrOrNameSub ); + } + } + bool isWaitingForDevice(const BDAddressAndType &mac, const std::string &name) { + return waitForDevices.cend() != jau::find_if(waitForDevices.cbegin(), waitForDevices.cend(), [&](const DeviceQuery & it)->bool { + return ( it.addressSub.length>0 && mac.address.contains(it.addressSub) ) || + ( it.nameSub.length()>0 && name.find(it.nameSub) != std::string::npos ); + }); + } + bool isWaitingForAnyDevice() { + return !waitForDevices.empty(); + } + size_t getWaitForDevicesCount() { + return waitForDevices.size(); + } + static void printList(FILE *out, const std::string &msg, jau::darray<DeviceQuery> &cont) { + jau::fprintf_td(out, "%s ", msg.c_str()); + jau::for_each(cont.cbegin(), cont.cend(), [out](const DeviceQuery& q) { + fprintf(out, "%s, ", q.toString().c_str()); + }); + fprintf(out, "\n"); + } + void printWaitForDevices(FILE *out, const std::string &msg) { + printList(out, msg, waitForDevices); + } + + void addToDevicesProcessed(const BDAddressAndType &a, const std::string& n) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor + devicesProcessed.emplace_back(a, n); + } + bool isDeviceProcessed(const BDAddressAndType & a) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor + for (auto it = devicesProcessed.cbegin(); it != devicesProcessed.cend(); ++it) { + if( it->addressAndType == a ) { + return true; + } + } + return false; + } + size_t getDeviceProcessedCount() { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor + return devicesProcessed.size(); + } + bool allDevicesProcessed() { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor + for (auto it1 = waitForDevices.cbegin(); it1 != waitForDevices.cend(); ++it1) { + const DeviceQuery& q = *it1; + auto it2 = devicesProcessed.cbegin(); + while ( it2 != devicesProcessed.cend() ) { + const DeviceID& id = *it2; + if( ( q.addressSub.length>0 && id.addressAndType.address.contains(q.addressSub) ) || + ( q.nameSub.length()>0 && id.name.find(q.nameSub) != std::string::npos ) ) { + break; + } + ++it2; + } + if( it2 == devicesProcessed.cend() ) { + return false; + } + } + return true; + } + static void printList(FILE *out, const std::string &msg, jau::darray<DeviceID> &cont) { + jau::fprintf_td(out, "%s ", msg.c_str()); + jau::for_each(cont.cbegin(), cont.cend(), [out](const DeviceID &id) { + fprintf(out, "%s, ", id.toString().c_str()); + }); + fprintf(out, "\n"); + } + void printDevicesProcessed(FILE *out, const std::string &msg) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessed); // RAII-style acquire and relinquish via destructor + printList(out, msg, devicesProcessed); + } + + void addToDevicesProcessing(const BDAddressAndType &a, const std::string& n) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor + devicesInProcessing.emplace_back(a, n); + } + bool removeFromDevicesProcessing(const BDAddressAndType &a) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor + for (auto it = devicesInProcessing.begin(); it != devicesInProcessing.end(); ++it) { + if ( it->addressAndType == a ) { + devicesInProcessing.erase(it); + return true; + } + } + return false; + } + bool isDeviceProcessing(const BDAddressAndType & a) { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor + for (auto it = devicesInProcessing.cbegin(); it != devicesInProcessing.cend(); ++it) { + if( it->addressAndType == a ) { + return true; + } + } + return false; + } + size_t getDeviceProcessingCount() { + const std::lock_guard<std::recursive_mutex> lock(mtx_devicesProcessing); // RAII-style acquire and relinquish via destructor + return devicesInProcessing.size(); + } + +} // namespace direct_bt::BTDeviceRegistry diff --git a/src/direct_bt/BTSecurityRegistry.cpp b/src/direct_bt/BTSecurityRegistry.cpp new file mode 100644 index 00000000..fae1756f --- /dev/null +++ b/src/direct_bt/BTSecurityRegistry.cpp @@ -0,0 +1,98 @@ +/* + * Author: Sven Gothel <[email protected]> + * Copyright (c) 2020 Gothel Software e.K. + * Copyright (c) 2020 ZAFENA AB + * + * 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 <BTSecurityRegistry.hpp> + +#include <jau/darray.hpp> + +using namespace direct_bt; + +namespace direct_bt::BTSecurityRegistry { + + static jau::darray<Entry> devicesSecDetails; + + Entry* get(const EUI48& addr) { + auto first = devicesSecDetails.begin(); + auto last = devicesSecDetails.end(); + for (; first != last; ++first) { + if ( first->matches(addr) ) { + return &(*first); + } + } + return nullptr; + } + Entry* get(const EUI48Sub& addrSub) { + auto first = devicesSecDetails.begin(); + auto last = devicesSecDetails.end(); + for (; first != last; ++first) { + if ( first->matches(addrSub) ) { + return &(*first); + } + } + return nullptr; + } + Entry* get(const std::string& nameSub) { + auto first = devicesSecDetails.begin(); + auto last = devicesSecDetails.end(); + for (; first != last; ++first) { + if ( first->matches(nameSub) ) { + return &(*first); + } + } + return nullptr; + } + Entry* getOrCreate(const std::string& addrOrNameSub) { + EUI48Sub addr1; + std::string errmsg; + Entry* sec = nullptr; + if( EUI48Sub::scanEUI48Sub(addrOrNameSub, addr1, errmsg) ) { + sec = get(addr1); + if( nullptr == sec ) { + Entry& r = devicesSecDetails.emplace_back( addr1 ); + sec = &r; + } + } else { + sec = get(addrOrNameSub); + if( nullptr == sec ) { + Entry& r = devicesSecDetails.emplace_back( addrOrNameSub ); + sec = &r; + } + } + return sec; + } + std::string allToString() { + std::string res; + int i=0; + for(auto iter = devicesSecDetails.cbegin(); iter != devicesSecDetails.cend(); ++iter, ++i) { + const Entry& sec = *iter; + if( 0 < i ) { + res += ", "; + } + res += sec.toString(); + } + return res; + } + +} // namespace direct_bt::BTSecurityRegistry diff --git a/src/direct_bt/CMakeLists.txt b/src/direct_bt/CMakeLists.txt index dea50003..8fba441a 100644 --- a/src/direct_bt/CMakeLists.txt +++ b/src/direct_bt/CMakeLists.txt @@ -19,11 +19,13 @@ set (direct_bt_LIB_SRCS ${PROJECT_SOURCE_DIR}/src/direct_bt/ATTPDUTypes.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTAdapter.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTDevice.cpp + ${PROJECT_SOURCE_DIR}/src/direct_bt/BTDeviceRegistry.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTGattDesc.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTGattChar.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTGattService.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTGattHandler.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTManager.cpp + ${PROJECT_SOURCE_DIR}/src/direct_bt/BTSecurityRegistry.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTTypes0.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/BTTypes1.cpp ${PROJECT_SOURCE_DIR}/src/direct_bt/GATTNumbers.cpp |