summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-10-25 02:57:31 +0100
committerSven Gothel <[email protected]>2020-10-25 02:57:31 +0100
commite878104ae9950d7cbc44841e6c5c5f0b9c131304 (patch)
tree4113a6af50db87dcbc32a4e5dd58a5b5e1a253bd /src
parent305a92e8f295298bc4dd3cb12f4b32b68d7bf0ae (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.cpp38
-rw-r--r--src/direct_bt/DBTManager.cpp95
-rw-r--r--src/direct_bt/MgmtTypes.cpp7
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;
}