summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--api/direct_bt/DBTAdapter.hpp4
-rw-r--r--api/direct_bt/DBTManager.hpp2
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp85
-rw-r--r--examples/java/DBTScanner10.java65
-rw-r--r--java/direct_bt/tinyb/DBTAdapter.java3
-rw-r--r--java/direct_bt/tinyb/DBTManager.java48
-rw-r--r--java/jni/direct_bt/DBTManager.cxx101
-rw-r--r--src/direct_bt/DBTAdapter.cpp38
-rw-r--r--src/direct_bt/DBTManager.cpp95
-rw-r--r--src/direct_bt/MgmtTypes.cpp7
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;
}