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 | |
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.
-rw-r--r-- | api/direct_bt/DBTAdapter.hpp | 4 | ||||
-rw-r--r-- | api/direct_bt/DBTManager.hpp | 2 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 85 | ||||
-rw-r--r-- | examples/java/DBTScanner10.java | 65 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTAdapter.java | 3 | ||||
-rw-r--r-- | java/direct_bt/tinyb/DBTManager.java | 48 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTManager.cxx | 101 | ||||
-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 |
10 files changed, 350 insertions, 98 deletions
diff --git a/api/direct_bt/DBTAdapter.hpp b/api/direct_bt/DBTAdapter.hpp index 0a487b65..532f9e2d 100644 --- a/api/direct_bt/DBTAdapter.hpp +++ b/api/direct_bt/DBTAdapter.hpp @@ -248,8 +248,10 @@ namespace direct_bt { void removeSharedDevice(const DBTDevice & device) noexcept; std::shared_ptr<DBTDevice> findSharedDevice (EUI48 const & mac, const BDAddressType macType) noexcept; - bool mgmtEvDeviceDiscoveringMgmt(std::shared_ptr<MgmtEvent> e) noexcept; + bool mgmtEvAdapterAddedMgmt(std::shared_ptr<MgmtEvent> e) noexcept; + bool mgmtEvAdapterRemovedMgmt(std::shared_ptr<MgmtEvent> e) noexcept; bool mgmtEvNewSettingsMgmt(std::shared_ptr<MgmtEvent> e) noexcept; + bool mgmtEvDeviceDiscoveringMgmt(std::shared_ptr<MgmtEvent> e) noexcept; bool mgmtEvLocalNameChangedMgmt(std::shared_ptr<MgmtEvent> e) noexcept; bool mgmtEvDeviceFoundHCI(std::shared_ptr<MgmtEvent> e) noexcept; bool mgmtEvDeviceDisconnectedMgmt(std::shared_ptr<MgmtEvent> e) noexcept; diff --git a/api/direct_bt/DBTManager.hpp b/api/direct_bt/DBTManager.hpp index 99dd6c70..c1b351d2 100644 --- a/api/direct_bt/DBTManager.hpp +++ b/api/direct_bt/DBTManager.hpp @@ -208,6 +208,8 @@ namespace direct_bt { std::shared_ptr<AdapterInfo> initAdapter(const uint16_t dev_id, const BTMode btMode) noexcept; void shutdownAdapter(const uint16_t dev_id) noexcept; + void processAdapterAdded(std::shared_ptr<MgmtEvent> e) noexcept; + bool mgmtEvAdapterRemovedCB(std::shared_ptr<MgmtEvent> e) noexcept; bool mgmtEvNewSettingsCB(std::shared_ptr<MgmtEvent> e) noexcept; bool mgmtEvClassOfDeviceChangedCB(std::shared_ptr<MgmtEvent> e) noexcept; bool mgmtEvDeviceDiscoveringCB(std::shared_ptr<MgmtEvent> e) noexcept; diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp index b89c498b..9a9d162b 100644 --- a/examples/direct_bt_scanner10/dbt_scanner10.cpp +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -506,40 +506,54 @@ static bool startDiscovery(DBTAdapter *a, std::string msg) { return HCIStatusCode::SUCCESS == status; } -void test(int dev_id) { - bool done = false; - - timestamp_t0 = getCurrentMilliseconds(); - - DBTAdapter adapter(dev_id); // given dev_id >= 0 or default adapter (1st powered) - if( !adapter.hasDevId() ) { - fprintf(stderr, "Default adapter not available.\n"); - exit(1); +static std::shared_ptr<DBTAdapter> createAdapter(const int dev_id0) { + // pre-validate dev_id availability + int dev_id; + DBTManager & mngr = DBTManager::get(BTMode::LE); + if( 0 > dev_id0 ) { + dev_id = mngr.getDefaultAdapterDevID(); + } else if( nullptr != mngr.getAdapterInfo(dev_id0) ) { + dev_id = dev_id0; + } else { + dev_id = -1; + } + if( 0 > dev_id ) { + fprintf(stderr, "Adapter not available (1): Request %d, deduced %d\n", dev_id0, dev_id); + return nullptr; } - if( !adapter.isValid() ) { - fprintf(stderr, "Adapter invalid.\n"); - exit(1); + + std::shared_ptr<DBTAdapter> adapter(new DBTAdapter(dev_id)); // given dev_id >= 0 or default adapter (1st powered) + if( !adapter->hasDevId() ) { + fprintf(stderr, "Adapter not available (2): %d\n", dev_id); // should have been covered above + return nullptr; } - if( !adapter.isEnabled() ) { - fprintf(stderr, "Adapter not enabled: device %s, address %s: %s\n", - adapter.getName().c_str(), adapter.getAddressString().c_str(), adapter.toString().c_str()); - exit(1); + if( !adapter->isPowered() ) { // should have been covered above + fprintf(stderr, "Adapter not powered (2): %s\n", adapter->toString().c_str()); + return nullptr; } - fprintf(stderr, "Using adapter: device %s, address %s: %s\n", - adapter.getName().c_str(), adapter.getAddressString().c_str(), adapter.toString().c_str()); + fprintf(stderr, "Using adapter: %s\n", adapter->toString().c_str()); - adapter.addStatusListener(std::shared_ptr<AdapterStatusListener>(new MyAdapterStatusListener())); + adapter->addStatusListener(std::shared_ptr<AdapterStatusListener>(new MyAdapterStatusListener())); if( USE_WHITELIST ) { for (auto it = WHITELIST.begin(); it != WHITELIST.end(); ++it) { - bool res = adapter.addDeviceToWhitelist(*it, BDAddressType::BDADDR_LE_PUBLIC, HCIWhitelistConnectType::HCI_AUTO_CONN_ALWAYS); + bool res = adapter->addDeviceToWhitelist(*it, BDAddressType::BDADDR_LE_PUBLIC, HCIWhitelistConnectType::HCI_AUTO_CONN_ALWAYS); fprintf(stderr, "Added to WHITELIST: res %d, address %s\n", res, it->toString().c_str()); } } else { - if( !startDiscovery(&adapter, "kick-off") ) { - done = true; + if( !startDiscovery(adapter.get(), "kick-off") ) { + return nullptr; } } + return adapter; +} + +void test(int dev_id) { + bool done = false; + + timestamp_t0 = getCurrentMilliseconds(); + + std::shared_ptr<DBTAdapter> adapter = createAdapter(dev_id); while( !done ) { if( 0 == MULTI_MEASUREMENTS || @@ -552,14 +566,29 @@ void test(int dev_id) { printDevicesProcessed("****** DevicesProcessed "); done = true; } else { - std::this_thread::sleep_for(std::chrono::milliseconds(3000)); + // validate existing adapter + if( nullptr != adapter ) { + if( !adapter->isValid() /* || !adapter->isPowered() */ ) { + // In case of removed adapter, not just powered-off (soft-reset) + // We could also close adapter on !isPowered() here, but its not required - adapter operational. + adapter->close(); + adapter = nullptr; // purge + } + } + // re-create adapter if required + if( nullptr == adapter ) { + adapter = createAdapter(dev_id); + } + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); } } - fprintf(stderr, "****** EOL Adapter's Devices - pre close\n"); - adapter.printSharedPtrListOfDevices(); - adapter.close(); - fprintf(stderr, "****** EOL Adapter's Devices - post close\n"); - adapter.printSharedPtrListOfDevices(); + fprintf(stderr, "****** EOL Adapter's Devices - pre close: %s\n", adapter->toString().c_str()); + if( nullptr != adapter ) { + adapter->printSharedPtrListOfDevices(); + adapter->close(); + fprintf(stderr, "****** EOL Adapter's Devices - post close\n"); + adapter->printSharedPtrListOfDevices(); + } } #include <cstdio> diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java index 5988f5d6..7c490f24 100644 --- a/examples/java/DBTScanner10.java +++ b/examples/java/DBTScanner10.java @@ -525,29 +525,27 @@ public class DBTScanner10 { return HCIStatusCode.SUCCESS == status; } - public void runTest(final BluetoothManager manager) { - final BluetoothAdapter adapter; - { - final List<BluetoothAdapter> adapters = manager.getAdapters(); - for(int i=0; i < adapters.size(); i++) { - println("Adapter["+i+"]: "+adapters.get(i)); - } - if( adapters.size() <= dev_id ) { - println("No adapter dev_id "+dev_id+" available, adapter count "+adapters.size()); - System.exit(-1); - } - if( 0 > dev_id ) { - adapter = manager.getDefaultAdapter(); - } else { - adapter = adapters.get(dev_id); - } - if( !adapter.isEnabled() ) { - println("Adapter not enabled: device "+adapter.getName()+", address "+adapter.getAddress()+": "+adapter.toString()); - System.exit(-1); + private BluetoothAdapter createAdapter(final BluetoothManager mngr, final int dev_id0) { + BluetoothAdapter adapter; + if( 0 > dev_id0 ) { + adapter = mngr.getDefaultAdapter(); + } else { + adapter = mngr.getAdapter(dev_id0); + if( !adapter.isPowered() ) { + adapter = mngr.getDefaultAdapter(); } } + final int dev_id = null != adapter ? adapter.getDevID() : -1; + if( 0 > dev_id ) { + println("Adapter not available (1): Request "+dev_id0+", deduced "+dev_id); + return null; + } - timestamp_t0 = BluetoothUtils.currentTimeMillis(); + if( !adapter.isPowered() ) { // should have been covered above + println("Adapter not powered (2): "+adapter.toString()); + return null; + } + println("Using adapter: "+adapter.toString()); adapter.addStatusListener(statusListener, null); adapter.enableDiscoverableNotifications(new BooleanNotification("Discoverable", timestamp_t0)); @@ -558,8 +556,6 @@ public class DBTScanner10 { adapter.enablePoweredNotifications(new BooleanNotification("Powered", timestamp_t0)); - boolean done = false; - if( USE_WHITELIST ) { for(final Iterator<String> wliter = whitelist.iterator(); wliter.hasNext(); ) { final String addr = wliter.next(); @@ -568,9 +564,17 @@ public class DBTScanner10 { } } else { if( !startDiscovery(adapter, "kick-off") ) { - done = true; } } + return adapter; + } + + public void runTest(final BluetoothManager manager) { + BluetoothAdapter adapter = createAdapter(manager, dev_id); + + timestamp_t0 = BluetoothUtils.currentTimeMillis(); + + boolean done = false; while( !done ) { if( 0 == MULTI_MEASUREMENTS.get() || @@ -583,8 +587,21 @@ public class DBTScanner10 { println("****** DevicesProcessed "+Arrays.toString(devicesProcessed.toArray())); done = true; } else { + // validate existing adapter + if( null != adapter ) { + if( !adapter.isValid() /* || !adapter.isPowered() */ ) { + // In case of removed adapter, not just powered-off (soft-reset) + // We could also close adapter on !isPowered() here, but its not required - adapter operational. + adapter.close(); + adapter = null; // purge + } + } + // re-create adapter if required + if( null == adapter ) { + adapter = createAdapter(manager, dev_id); + } try { - Thread.sleep(3000); + Thread.sleep(2000); } catch (final InterruptedException e) { e.printStackTrace(); } diff --git a/java/direct_bt/tinyb/DBTAdapter.java b/java/direct_bt/tinyb/DBTAdapter.java index 62e92930..594760cd 100644 --- a/java/direct_bt/tinyb/DBTAdapter.java +++ b/java/direct_bt/tinyb/DBTAdapter.java @@ -121,6 +121,9 @@ public class DBTAdapter extends DBTObject implements BluetoothAdapter poweredOff(); + // notify manager that this instance is gone for good + ((DBTManager)DBTManager.getManager()).removeAdapterCB(dev_id, 0x0100 /* custom opc */); + super.close(); } diff --git a/java/direct_bt/tinyb/DBTManager.java b/java/direct_bt/tinyb/DBTManager.java index 436764a7..5f663fcd 100644 --- a/java/direct_bt/tinyb/DBTManager.java +++ b/java/direct_bt/tinyb/DBTManager.java @@ -30,6 +30,7 @@ import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; import org.tinyb.BluetoothAdapter; import org.tinyb.BluetoothDevice; @@ -64,7 +65,6 @@ public class DBTManager implements BluetoothManager } }, "DBTManager_ShutdownHook" ) ) ; return null; } } ) ; - } private static synchronized void shutdownImpl(final boolean _isJVMShuttingDown) { @@ -150,7 +150,7 @@ public class DBTManager implements BluetoothManager public static void setUnifyUUID128Bit(final boolean v) { unifyUUID128Bit=v; } private long nativeInstance; - private final List<BluetoothAdapter> adapters = new ArrayList<BluetoothAdapter>(); + private final List<BluetoothAdapter> adapters = new CopyOnWriteArrayList<BluetoothAdapter>(); private final Settings settings; @Override @@ -278,6 +278,50 @@ public class DBTManager implements BluetoothManager public boolean getDiscovering() throws BluetoothException { return getDefaultAdapter().getDiscovering(); } private native List<BluetoothAdapter> getAdapterListImpl(); + private native BluetoothAdapter getAdapterImpl(int dev_id); + + /** callback from native adapter remove */ + /* pp */ final void removeAdapterCB(final int dev_id, final int opc_reason) { + for( int i=0; i<adapters.size(); i++) { + final BluetoothAdapter a = adapters.get(i); + if( dev_id == a.getDevID() ) { + adapters.remove(i); + if( DEBUG ) { + System.err.println("DBTManager.removeAdapterCB[dev_id "+dev_id+", opc 0x"+Integer.toHexString(opc_reason)+ + "]: removed "+a.toString()+", size "+adapters.size()); + } + return; + } + } + if( DEBUG ) { + System.err.println("DBTManager.removeAdapterCB[dev_id "+dev_id+", opc 0x"+Integer.toHexString(opc_reason)+ + "]: not found, size "+adapters.size()); + } + } + /** callback from native adapter add or POWERED on */ + private final void updatedAdapterCB(final int dev_id, final int opc_reason) { + final BluetoothAdapter preInstance = getAdapter(dev_id); + if( null != preInstance ) { + if( DEBUG ) { + System.err.println("DBTManager.updatedAdapterCB[dev_id "+dev_id+", opc 0x"+Integer.toHexString(opc_reason)+ + "]: existing "+preInstance.toString()+", size "+adapters.size()); + } + return; + } + final BluetoothAdapter newInstance = getAdapterImpl(dev_id); + if( null == newInstance ) { + if( DEBUG ) { + System.err.println("DBTManager.updatedAdapterCB[dev_id "+dev_id+", opc 0x"+Integer.toHexString(opc_reason)+ + "]: Adapter not found, size "+adapters.size()); + } + return; + } + final boolean added = adapters.add(newInstance); + if( DEBUG ) { + System.err.println("DBTManager.updatedAdapterCB[dev_id "+dev_id+", opc 0x"+Integer.toHexString(opc_reason)+ + "]: added "+added+": new "+newInstance.toString()+", size "+adapters.size()); + } + } private native void initImpl(final boolean unifyUUID128Bit, final int btMode) throws BluetoothException; private native void deleteImpl(long nativeInstance); diff --git a/java/jni/direct_bt/DBTManager.cxx b/java/jni/direct_bt/DBTManager.cxx index 7306cb5c..84405cf5 100644 --- a/java/jni/direct_bt/DBTManager.cxx +++ b/java/jni/direct_bt/DBTManager.cxx @@ -38,6 +38,75 @@ using namespace direct_bt; using namespace jau; +static const std::string _removeAdapterCBMethodName("removeAdapterCB"); +static const std::string _removeAdapterCBMethodArgs("(II)V"); +static const std::string _updatedAdapterCBMethodName("updatedAdapterCB"); +static const std::string _updatedAdapterCBMethodArgs("(II)V"); + +struct BooleanMgmtCBContext { + MgmtEvent::Opcode opc; + JNIGlobalRef jmgmtRef; + jmethodID mid; + + bool operator==(const BooleanMgmtCBContext& rhs) const + { + if( &rhs == this ) { + return true; + } + return rhs.opc == opc && + rhs.jmgmtRef == jmgmtRef; + } + + bool operator!=(const BooleanMgmtCBContext& rhs) const + { return !( *this == rhs ); } + +}; +typedef std::shared_ptr<BooleanMgmtCBContext> BooleanMgmtCBContextRef; + +static void _addMgmtCBOnce(JNIEnv *env, DBTManager & mgmt, JNIGlobalRef jmgmtRef, MgmtEvent::Opcode opc, + const std::string &jmethodName, const std::string &jmethodArgs) +{ + try { + bool(*nativeCallback)(BooleanMgmtCBContextRef&, std::shared_ptr<MgmtEvent>) = + [](BooleanMgmtCBContextRef& ctx_ref, std::shared_ptr<MgmtEvent> e)->bool { + JNIEnv *env_ = *jni_env; + const int dev_id = e->getDevID(); + + if( MgmtEvent::Opcode::INDEX_REMOVED == ctx_ref->opc ) { + env_->CallVoidMethod(*(ctx_ref->jmgmtRef), ctx_ref->mid, dev_id, ctx_ref->opc); // remove + } else if( MgmtEvent::Opcode::INDEX_ADDED == ctx_ref->opc ) { + env_->CallVoidMethod(*(ctx_ref->jmgmtRef), ctx_ref->mid, dev_id, ctx_ref->opc); // updated + } else if( MgmtEvent::Opcode::NEW_SETTINGS == ctx_ref->opc ) { + const MgmtEvtNewSettings &event = *static_cast<const MgmtEvtNewSettings *>(e.get()); + if( isAdapterSettingBitSet(event.getSettings(), AdapterSetting::POWERED) ) { + // probably newly POWERED on + env_->CallVoidMethod(*(ctx_ref->jmgmtRef), ctx_ref->mid, dev_id, ctx_ref->opc); // updated + } + } + return true; + }; + + + jclass mgmtClazz = jau::search_class(env, jmgmtRef.getObject()); + jau::java_exception_check_and_throw(env, E_FILE_LINE); + if( nullptr == mgmtClazz ) { + throw jau::InternalError("DBTManager not found", E_FILE_LINE); + } + jmethodID mid = jau::search_method(env, mgmtClazz, jmethodName.c_str(), jmethodArgs.c_str(), false); + jau::java_exception_check_and_throw(env, E_FILE_LINE); + if( nullptr == mid ) { + throw jau::InternalError("DBTManager has no "+jmethodName+"."+jmethodArgs+" method, for "+mgmt.toString(), E_FILE_LINE); + } + BooleanMgmtCBContext * ctx = new BooleanMgmtCBContext{opc, jmgmtRef, mid }; + + // move BooleanDeviceCBContextRef into CaptureInvocationFunc and operator== includes javaCallback comparison + FunctionDef<bool, std::shared_ptr<MgmtEvent>> funcDef = bindCaptureFunc(BooleanMgmtCBContextRef(ctx), nativeCallback); + mgmt.addMgmtEventCallback(-1, opc, funcDef); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + void Java_direct_1bt_tinyb_DBTManager_initImpl(JNIEnv *env, jobject obj, jboolean unifyUUID128Bit, jint jbtMode) { directBTJNISettings.setUnifyUUID128Bit(unifyUUID128Bit); @@ -48,6 +117,11 @@ void Java_direct_1bt_tinyb_DBTManager_initImpl(JNIEnv *env, jobject obj, jboolea java_exception_check_and_throw(env, E_FILE_LINE); manager->setJavaObject( std::shared_ptr<JavaAnon>( new JavaGlobalObj(obj, nullptr) ) ); JavaGlobalObj::check(manager->getJavaObject(), E_FILE_LINE); + + JNIGlobalRef jmgmtRef = JavaGlobalObj::GetJavaObject(manager->getJavaObject()); + _addMgmtCBOnce(env, *manager, jmgmtRef, MgmtEvent::Opcode::INDEX_REMOVED, _removeAdapterCBMethodName, _removeAdapterCBMethodArgs); + _addMgmtCBOnce(env, *manager, jmgmtRef, MgmtEvent::Opcode::INDEX_ADDED, _updatedAdapterCBMethodName, _updatedAdapterCBMethodArgs); + _addMgmtCBOnce(env, *manager, jmgmtRef, MgmtEvent::Opcode::NEW_SETTINGS, _updatedAdapterCBMethodName, _updatedAdapterCBMethodArgs); DBG_PRINT("Java_direct_1bt_tinyb_DBTManager_init: Manager %s", manager->toString().c_str()); } catch(...) { rethrow_and_raise_java_exception(env); @@ -118,3 +192,30 @@ jobject Java_direct_1bt_tinyb_DBTManager_getAdapterListImpl(JNIEnv *env, jobject return nullptr; } +jobject Java_direct_1bt_tinyb_DBTManager_getAdapterImpl(JNIEnv *env, jobject obj, jint dev_id) +{ + try { + DBTManager *manager = getInstance<DBTManager>(env, obj); + + if( nullptr == manager->getAdapterInfo(dev_id) ) { + ERR_PRINT("DBTManager::getAdapterImpl: Adapter dev_id %d: Not found", dev_id); + return nullptr; + } + DBTAdapter * adapter = new DBTAdapter( dev_id ); + DBG_PRINT("DBTManager::getAdapterImpl: Adapter dev_id %d: %s", dev_id, adapter->toString().c_str()); + + if( !adapter->isValid() ) { + throw BluetoothException("Invalid adapter @ dev_id "+std::to_string( dev_id ), E_FILE_LINE); + } + if( !adapter->hasDevId() ) { + throw BluetoothException("Invalid adapter dev-id @ dev_id "+std::to_string( dev_id ), E_FILE_LINE); + } + if( dev_id != adapter->dev_id ) { // just make sure idx == dev_id + throw BluetoothException("Invalid adapter dev-id "+std::to_string( adapter->dev_id )+" != dev_id "+std::to_string( dev_id ), E_FILE_LINE); + } + return jau::convert_instance_to_jobject<DBTAdapter>(env, adapter, _adapterClazzCtorArgs.c_str(), _createJavaAdapter); + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return nullptr; +} 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; } |