diff options
author | Sven Gothel <[email protected]> | 2020-10-25 02:57:31 +0100 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2020-10-25 02:57:31 +0100 |
commit | e878104ae9950d7cbc44841e6c5c5f0b9c131304 (patch) | |
tree | 4113a6af50db87dcbc32a4e5dd58a5b5e1a253bd /src | |
parent | 305a92e8f295298bc4dd3cb12f4b32b68d7bf0ae (diff) |
Support Adapter removal and add @ runtime: Handle INDEX_ADDED and INDEX_REMOVED Mgmt events
C++ DBTManager detects INDEX_ADDED and INDEX_REMOVED Mgmt events
@INDEX_ADDED reception in the event reader, a new processAdapterAdded() thread is spawned
initializing the adapter as usual and maintaining adding its AdapterInfo artifact.
Then all user INDEX_ADDED callbacks are called from this thread,
after the new AdapterInfo entry has been added.
@INDEX_REMOVED here the matching AdapterInfo entry is simply removed
DBTAdapter naturally also listens to INDEX_REMOVED,
simply closing its instance and hence rendering it isValid() = false.
+++
On close(), Java's DBTAdapter will remove itself
from the DBTManager's adapter list.
This removal will happen automatically,
if INDEX_REMOVED is received - see below.
On the Java side, DBTManager listens to:
@INDEX_REMOVED here the matching adapter instance is simply removed
@INDEX_ADDED and @NEW_SETTINGS(POWERED) checks whether the matching adapter instance
exists and creates a new one added to the adapters list if necessary.
@NEW_SETTINGS(POWERED) has been added here as well, since a user could
chose to close the adapter if POWERED off and hence removing itself
from the DBTManager adapter list.
Hence NEW_SETTINGS(POWERED) will add a new adapter instance, if none exists.
+++
Due to this new concurrent use-case of the adapter list,
the list is now a CopyOnWriteArrayList instance supporting lock-free fast reads.
Mutations of the list occur rarely, hopefully ;-).
On the C++, the AdapterInfo is a jau::cow_vector and the callbacks are
store in a jau::cow_vector as well (MgmtAdapterEventCallbackList).
Hence both share the same properties w/ the Java side: Fast lock-free reads.
Diffstat (limited to 'src')
-rw-r--r-- | src/direct_bt/DBTAdapter.cpp | 38 | ||||
-rw-r--r-- | src/direct_bt/DBTManager.cpp | 95 | ||||
-rw-r--r-- | src/direct_bt/MgmtTypes.cpp | 7 |
3 files changed, 97 insertions, 43 deletions
diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp index 776a508c..19791bdb 100644 --- a/src/direct_bt/DBTAdapter.cpp +++ b/src/direct_bt/DBTAdapter.cpp @@ -131,29 +131,28 @@ std::shared_ptr<DBTDevice> DBTAdapter::findConnectedDevice (EUI48 const & mac, c // ************************************************* bool DBTAdapter::validateDevInfo() noexcept { + bool ok = false; currentMetaScanType = ScanType::NONE; keep_le_scan_alive = false; if( 0 > dev_id ) { ERR_PRINT("DBTAdapter::validateDevInfo: Invalid negative dev_id %d", dev_id); - return false; + goto errout0; } if( !mgmt.isOpen() ) { ERR_PRINT("DBTAdapter::validateDevInfo: Adapter[%d]: Manager not open", dev_id); - return false; + goto errout0; } if( !hci.isOpen() ) { ERR_PRINT("DBTAdapter::validateDevInfo: Adapter[%d]: HCIHandler closed", dev_id); - return false; + goto errout0; } adapterInfo = mgmt.getAdapterInfo(dev_id); if( nullptr == adapterInfo ) { // fill in a dummy AdapterInfo for the sake of de-referencing throughout this adapter instance - adapterInfo = std::shared_ptr<AdapterInfo>( new AdapterInfo(dev_id, EUI48_ANY_DEVICE, 0, 0, - AdapterSetting::NONE, AdapterSetting::NONE, 0, "invalid", "invalid")); - ERR_PRINT("DBTAdapter::validateDevInfo: Adapter[%d]: Not existent: %s", dev_id, adapterInfo->toString().c_str()); - return false; + ERR_PRINT("DBTAdapter::validateDevInfo: Adapter[%d]: Not existent", dev_id); + goto errout0; } old_settings = adapterInfo->getCurrentSettingMask(); @@ -178,7 +177,10 @@ bool DBTAdapter::validateDevInfo() noexcept { } else { WORDY_PRINT("DBTAdapter::validateDevInfo: Adapter[%d]: Not POWERED: %s", dev_id, adapterInfo->toString().c_str()); } - bool ok = true; + ok = true; + ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::INDEX_ADDED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvAdapterAddedMgmt)); + ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::INDEX_REMOVED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvAdapterRemovedMgmt)); + ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DISCOVERING, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceDiscoveringMgmt)) && ok; ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_SETTINGS, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvNewSettingsMgmt)) && ok; ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::LOCAL_NAME_CHANGED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvLocalNameChangedMgmt)) && ok; @@ -201,6 +203,11 @@ bool DBTAdapter::validateDevInfo() noexcept { return false; // dtor local HCIHandler w/ closing } return true; + +errout0: + adapterInfo = std::shared_ptr<AdapterInfo>( new AdapterInfo(dev_id, EUI48_ANY_DEVICE, 0, 0, + AdapterSetting::NONE, AdapterSetting::NONE, 0, "invalid", "invalid")); + return false; } DBTAdapter::DBTAdapter() noexcept @@ -265,6 +272,7 @@ void DBTAdapter::close() noexcept { const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor sharedDevices.clear(); } + valid = false; DBG_PRINT("DBTAdapter::close: XXX"); } @@ -1121,3 +1129,17 @@ bool DBTAdapter::mgmtEvDeviceFoundHCI(std::shared_ptr<MgmtEvent> e) noexcept { return true; } + +bool DBTAdapter::mgmtEvAdapterAddedMgmt(std::shared_ptr<MgmtEvent> e) noexcept { + jau::PLAIN_PRINT("DBTAdapter:mgmt:AdapterAdded: %s on %s", e->toString().c_str(), toString(false).c_str()); + return true; +} + +bool DBTAdapter::mgmtEvAdapterRemovedMgmt(std::shared_ptr<MgmtEvent> e) noexcept { + jau::PLAIN_PRINT("DBTAdapter:mgmt:AdapterRemoved: %s on %s", e->toString().c_str(), toString(false).c_str()); + // Adapter has been powered off, close connections and cleanup off-thread. + std::thread bg(&DBTAdapter::close, this); // @suppress("Invalid arguments") + bg.detach(); + return true; +} + diff --git a/src/direct_bt/DBTManager.cpp b/src/direct_bt/DBTManager.cpp index fa5513f1..5cb7c789 100644 --- a/src/direct_bt/DBTManager.cpp +++ b/src/direct_bt/DBTManager.cpp @@ -115,6 +115,10 @@ void DBTManager::mgmtReaderThreadImpl() noexcept { WARN_PRINT("DBTManager-IO RECV Drop (%u oldest elements of %u capacity, ring full)", dropCount, mgmtEventRing.capacity()); } mgmtEventRing.putBlocking( event ); + } else if( MgmtEvent::Opcode::INDEX_ADDED == opc ) { + COND_PRINT(env.DEBUG_EVENT, "DBTManager-IO RECV (ADD) %s", event->toString().c_str()); + std::thread adapterAddedThread(&DBTManager::processAdapterAdded, this, event); // @suppress("Invalid arguments") + adapterAddedThread.detach(); } else { // issue a callback COND_PRINT(env.DEBUG_EVENT, "DBTManager-IO RECV (CB) %s", event->toString().c_str()); @@ -391,10 +395,6 @@ DBTManager::DBTManager(const BTMode _defaultBTMode) noexcept } next1: - // Register to add/remove adapter optionally: - // MgmtEvent::INDEX_ADDED, MgmtConst::INDEX_NONE; - // MgmtEvent::INDEX_REMOVED, MgmtConst::INDEX_NONE; - // Mandatory { MgmtCommand req0(MgmtOpcode::READ_INDEX_LIST, MgmtConstU16::MGMT_INDEX_NONE); @@ -432,27 +432,25 @@ next1: // Not required: CTOR: adapterInfos.set_store(std::move(snapshot)); } } + + addMgmtEventCallback(-1, MgmtEvent::Opcode::INDEX_REMOVED, jau::bindMemberFunc(this, &DBTManager::mgmtEvAdapterRemovedCB)); addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_SETTINGS, jau::bindMemberFunc(this, &DBTManager::mgmtEvNewSettingsCB)); - if( ok ) { - if( env.DEBUG_EVENT ) { - addMgmtEventCallback(-1, MgmtEvent::Opcode::CLASS_OF_DEV_CHANGED, jau::bindMemberFunc(this, &DBTManager::mgmtEvClassOfDeviceChangedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DISCOVERING, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceDiscoveringCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_FOUND, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceFoundCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceDisconnectedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_CONNECTED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceConnectedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::CONNECT_FAILED, jau::bindMemberFunc(this, &DBTManager::mgmtEvConnectFailedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_BLOCKED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceBlockedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_UNBLOCKED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceUnblockedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_UNPAIRED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceUnpairedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_CONN_PARAM, jau::bindMemberFunc(this, &DBTManager::mgmtEvNewConnectionParamCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_WHITELIST_ADDED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceWhitelistAddedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_WHITELIST_REMOVED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceWhilelistRemovedCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::PIN_CODE_REQUEST, jau::bindMemberFunc(this, &DBTManager::mgmtEvPinCodeRequestCB)); - addMgmtEventCallback(-1, MgmtEvent::Opcode::USER_PASSKEY_REQUEST, jau::bindMemberFunc(this, &DBTManager::mgmtEvUserPasskeyRequestCB)); - } - PERF_TS_TD("DBTManager::open.ok"); - return; + if( env.DEBUG_EVENT ) { + addMgmtEventCallback(-1, MgmtEvent::Opcode::CLASS_OF_DEV_CHANGED, jau::bindMemberFunc(this, &DBTManager::mgmtEvClassOfDeviceChangedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DISCOVERING, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceDiscoveringCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_FOUND, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceFoundCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceDisconnectedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_CONNECTED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceConnectedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::CONNECT_FAILED, jau::bindMemberFunc(this, &DBTManager::mgmtEvConnectFailedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_BLOCKED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceBlockedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_UNBLOCKED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceUnblockedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_UNPAIRED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceUnpairedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_CONN_PARAM, jau::bindMemberFunc(this, &DBTManager::mgmtEvNewConnectionParamCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_WHITELIST_ADDED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceWhitelistAddedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_WHITELIST_REMOVED, jau::bindMemberFunc(this, &DBTManager::mgmtEvDeviceWhilelistRemovedCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::PIN_CODE_REQUEST, jau::bindMemberFunc(this, &DBTManager::mgmtEvPinCodeRequestCB)); + addMgmtEventCallback(-1, MgmtEvent::Opcode::USER_PASSKEY_REQUEST, jau::bindMemberFunc(this, &DBTManager::mgmtEvUserPasskeyRequestCB)); } PERF_TS_TD("DBTManager::ctor.ok"); DBG_PRINT("DBTManager::ctor: OK"); @@ -569,6 +567,39 @@ std::shared_ptr<AdapterInfo> DBTManager::getAdapterInfo(const uint16_t dev_id) c return *it; } } +bool DBTManager::addAdapterInfo(std::shared_ptr<AdapterInfo> ai) noexcept { + const std::lock_guard<std::recursive_mutex> lock(adapterInfos.get_write_mutex()); + std::shared_ptr<std::vector<std::shared_ptr<AdapterInfo>>> snapshot = adapterInfos.get_snapshot(); + + auto begin = snapshot->begin(); + auto it = std::find_if(begin, snapshot->end(), [&](std::shared_ptr<AdapterInfo> const& p) -> bool { + return p->dev_id == ai->dev_id; + }); + if ( it != std::end(*snapshot) ) { + // already existing + return false; + } + snapshot->push_back(ai); + adapterInfos.set_store(std::move(snapshot)); + return true; +} +std::shared_ptr<AdapterInfo> DBTManager::removeAdapterInfo(const uint16_t dev_id) noexcept { + const std::lock_guard<std::recursive_mutex> lock(adapterInfos.get_write_mutex()); + std::shared_ptr<std::vector<std::shared_ptr<AdapterInfo>>> snapshot = adapterInfos.get_snapshot(); + + for(auto it = snapshot->begin(); it != snapshot->end(); ) { + std::shared_ptr<AdapterInfo> & ai = *it; + if( ai->dev_id == dev_id ) { + std::shared_ptr<AdapterInfo> res = ai; + it = snapshot->erase(it); + adapterInfos.set_store(std::move(snapshot)); + return res; + } else { + ++it; + } + } + return nullptr; +} BTMode DBTManager::getCurrentBTMode(uint16_t dev_id) const noexcept { std::shared_ptr<AdapterInfo> ai = getAdapterInfo(dev_id); @@ -857,25 +888,21 @@ void DBTManager::clearAllMgmtEventCallbacks() noexcept { } } -void DBTManager::processAdapterAdded(const uint16_t dev_id) noexcept { +void DBTManager::processAdapterAdded(std::shared_ptr<MgmtEvent> e) noexcept { + const uint16_t dev_id = e->getDevID(); std::shared_ptr<AdapterInfo> ai = initAdapter(dev_id, defaultBTMode); if( nullptr != ai ) { const bool added = addAdapterInfo(ai); - jau::PLAIN_PRINT("DBTManager::Adapter[%d] Added %d: %s", dev_id, added, ai->toString().c_str()); + DBG_PRINT("DBTManager::Adapter[%d] Added %d: %s", dev_id, added, ai->toString().c_str()); + sendMgmtEvent(e); } else { - jau::PLAIN_PRINT("DBTManager::Adapter[%d] Added 0: Init failed", dev_id); + DBG_PRINT("DBTManager::Adapter[%d] Added 0: Init failed", dev_id); } } -bool DBTManager::mgmtEvAdapterAddedCB(std::shared_ptr<MgmtEvent> e) noexcept { - jau::PLAIN_PRINT("DBTManager:mgmt:AdapterAdded: %s", e->toString().c_str()); - std::thread adapterAddedThread(&DBTManager::processAdapterAdded, this, e->getDevID()); // @suppress("Invalid arguments") - adapterAddedThread.detach(); - return true; -} bool DBTManager::mgmtEvAdapterRemovedCB(std::shared_ptr<MgmtEvent> e) noexcept { - jau::PLAIN_PRINT("DBTManager:mgmt:AdapterRemoved: Start %s", e->toString().c_str()); + DBG_PRINT("DBTManager:mgmt:AdapterRemoved: Start %s", e->toString().c_str()); std::shared_ptr<AdapterInfo> ai = removeAdapterInfo(e->getDevID()); - jau::PLAIN_PRINT("DBTManager:mgmt:AdapterRemoved: End: Removed %s", (nullptr != ai ? ai->toString().c_str() : "none")); + DBG_PRINT("DBTManager:mgmt:AdapterRemoved: End: Removed %s", (nullptr != ai ? ai->toString().c_str() : "none")); return true; } bool DBTManager::mgmtEvNewSettingsCB(std::shared_ptr<MgmtEvent> e) noexcept { diff --git a/src/direct_bt/MgmtTypes.cpp b/src/direct_bt/MgmtTypes.cpp index cf37e1d6..52bdf3b0 100644 --- a/src/direct_bt/MgmtTypes.cpp +++ b/src/direct_bt/MgmtTypes.cpp @@ -267,7 +267,12 @@ std::shared_ptr<MgmtEvent> MgmtEvent::getSpecialized(const uint8_t * buffer, jau case MgmtEvent::Opcode::DEVICE_UNPAIRED: res = new MgmtEvtDeviceUnpaired(buffer, buffer_size); break; case MgmtEvent::Opcode::LOCAL_NAME_CHANGED: - res = new MgmtEvtLocalNameChanged(buffer, buffer_size); break; + res = new MgmtEvtLocalNameChanged(buffer, buffer_size); + break; + case MgmtEvent::Opcode::INDEX_ADDED: + [[fallthrough]]; + case MgmtEvent::Opcode::INDEX_REMOVED: + [[fallthrough]]; default: res = new MgmtEvent(buffer, buffer_size, 0); break; } |