summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/direct_bt/DBTAdapter.hpp4
-rw-r--r--api/direct_bt/DBTManager.hpp127
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp111
-rw-r--r--examples/java/DBTScanner10.java115
-rw-r--r--java/direct_bt/tinyb/DBTManager.java54
-rw-r--r--java/org/tinyb/BluetoothManager.java41
-rw-r--r--java/tinyb/dbus/DBusManager.java7
-rw-r--r--scripts/run-dbt_scanner10.sh4
-rw-r--r--src/direct_bt/DBTManager.cpp39
9 files changed, 418 insertions, 84 deletions
diff --git a/api/direct_bt/DBTAdapter.hpp b/api/direct_bt/DBTAdapter.hpp
index 532f9e2d..86fcbc16 100644
--- a/api/direct_bt/DBTAdapter.hpp
+++ b/api/direct_bt/DBTAdapter.hpp
@@ -280,7 +280,7 @@ namespace direct_bt {
* Using the default adapter device
* <p>
* The default adapter is either the first POWERED adapter,
- * or the one with index == dev_id == 0.
+ * or none - in which case this instance !isValid()
* </p>
*/
DBTAdapter() noexcept;
@@ -297,7 +297,7 @@ namespace direct_bt {
* or the default adapter device if dev_id < 0.
* <p>
* The default adapter is either the first POWERED adapter,
- * or the one with index == dev_id == 0.
+ * or none - in which case this instance !isValid().
* </p>
*
* @param[in] dev_id an already identified HCI device id
diff --git a/api/direct_bt/DBTManager.hpp b/api/direct_bt/DBTManager.hpp
index c1b351d2..7d28344f 100644
--- a/api/direct_bt/DBTManager.hpp
+++ b/api/direct_bt/DBTManager.hpp
@@ -136,6 +136,57 @@ namespace direct_bt {
};
/**
+ * Callback function to receive change events regarding the system's adapter set,
+ * e.g. about a removed or added adapter due to user interaction or 'cold reset'.
+ * <p>
+ * When a new callback is added, all available adapter's will be reported as added,
+ * this allows a fully event driven workflow.
+ * </p>
+ * <p>
+ * Note that the 'adapter added' callback, i.e. 'added==true',
+ * is performed on a dedicated thread allowing the user to perform complex operations.
+ * </p>
+ * <p>
+ * User shall not perform complex operations on the 'adapter removed' callback,
+ * i.e. 'added==false'.
+ * </p>
+ *
+ * @param added true if adapter was newly added, otherwise removed from system
+ * @param adapterInfo the adapter's AdapterInfo, inclusive the dev_id
+ * @return ignored
+ * @see ChangedAdapterSetCallback
+ * @see DBTManager::addChangedAdapterSetCallback()
+ * @see DBTManager::removeChangedAdapterSetCallback()
+ */
+ typedef bool (*ChangedAdapterSetFunc)(bool added, const AdapterInfo& adapterInfo);
+
+ /**
+ * Callback jau::FunctionDef to receive change events regarding the system's adapter set,
+ * e.g. about a removed or added adapter due to user interaction or 'cold reset'.
+ * <p>
+ * When a new callback is added, all available adapter's will be reported as added,
+ * this allows a fully event driven workflow.
+ * </p>
+ * <p>
+ * Note that the 'adapter added' callback, i.e. 'added==true',
+ * is performed on a dedicated thread allowing the user to perform complex operations.
+ * </p>
+ * <p>
+ * User shall not perform complex operations on the 'adapter removed' callback,
+ * i.e. 'added==false'.
+ * </p>
+ *
+ * @param added true if adapter was newly added, otherwise removed from system
+ * @param adapterInfo the adapter's AdapterInfo, inclusive the dev_id
+ * @return ignored
+ * @see ChangedAdapterSetFunc
+ * @see DBTManager::addChangedAdapterSetCallback()
+ * @see DBTManager::removeChangedAdapterSetCallback()
+ */
+ typedef jau::FunctionDef<bool, bool, const AdapterInfo&> ChangedAdapterSetCallback;
+ typedef jau::cow_vector<ChangedAdapterSetCallback> ChangedAdapterSetCallbackList;
+
+ /**
* A thread safe singleton handler of the Linux Kernel's BlueZ manager control channel.
* <p>
* Implementation utilizes a lock free ringbuffer receiving data within its separate thread.
@@ -187,6 +238,8 @@ namespace direct_bt {
return static_cast<uint16_t>(opc) < mgmtAdapterEventCallbackLists.size();
}
+ ChangedAdapterSetCallbackList mgmtChangedAdapterSetCallbackList;
+
jau::cow_vector<std::shared_ptr<AdapterInfo>> adapterInfos;
void mgmtReaderThreadImpl() noexcept;
@@ -226,6 +279,18 @@ namespace direct_bt {
bool mgmtEvPinCodeRequestCB(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvUserPasskeyRequestCB(std::shared_ptr<MgmtEvent> e) noexcept;
+ /**
+ * Adds the given AdapterInfo if representing a new dev_id.
+ * @return true if newly added dev_id, otherwise false if dev_id already exists.
+ */
+ bool addAdapterInfo(std::shared_ptr<AdapterInfo> ai) noexcept;
+
+ /**
+ * Removes the AdapterInfo with the given dev_id
+ * @return the removed instance or nullptr if not found.
+ */
+ std::shared_ptr<AdapterInfo> removeAdapterInfo(const uint16_t dev_id) noexcept;
+
public:
/**
* Retrieves the singleton instance.
@@ -296,18 +361,6 @@ namespace direct_bt {
std::shared_ptr<AdapterInfo> getAdapterInfo(const uint16_t dev_id) const noexcept;
/**
- * Adds the given AdapterInfo if representing a new dev_id.
- * @return true if newly added dev_id, otherwise false if dev_id already exists.
- */
- bool addAdapterInfo(std::shared_ptr<AdapterInfo> ai) noexcept;
-
- /**
- * Removes the AdapterInfo with the given dev_id
- * @return the removed instance or nullptr if not found.
- */
- std::shared_ptr<AdapterInfo> removeAdapterInfo(const uint16_t dev_id) noexcept;
-
- /**
* Returns the current BTMode of given adapter dev_idx or BTMode::NONE if dev_id adapter is not available.
*/
BTMode getCurrentBTMode(uint16_t dev_id) const noexcept;
@@ -410,6 +463,56 @@ namespace direct_bt {
/** Manually send a MgmtEvent to all of its listeners. */
void sendMgmtEvent(std::shared_ptr<MgmtEvent> event) noexcept;
+
+ /** ChangedAdapterSetCallback handling */
+
+ /**
+ * Adds the given ChangedAdapterSetCallback to this manager.
+ * <p>
+ * When a new callback is added, all available adapter's will be reported as added,
+ * this allows a fully event driven workflow.
+ * </p>
+ * <p>
+ * Note that the 'adapter added' callback, i.e. 'added==true',
+ * is performed on a dedicated thread allowing the user to perform complex operations.
+ * </p>
+ * <p>
+ * User shall not perform complex operations on the 'adapter removed' callback,
+ * i.e. 'added==false'.
+ * </p>
+ */
+ void addChangedAdapterSetCallback(const ChangedAdapterSetCallback & l);
+
+ /**
+ * Remove the given ChangedAdapterSetCallback from this manager.
+ * @param l the to be removed element
+ * @return the number of removed elements
+ */
+ int removeChangedAdapterSetCallback(const ChangedAdapterSetCallback & l);
+
+ /**
+ * Adds the given ChangedAdapterSetFunc to this manager.
+ * <p>
+ * When a new callback is added, all available adapter's will be reported as added,
+ * this allows a fully event driven workflow.
+ * </p>
+ * <p>
+ * Note that the 'adapter added' callback, i.e. 'added==true',
+ * is performed on a dedicated thread allowing the user to perform complex operations.
+ * </p>
+ * <p>
+ * User shall not perform complex operations on the 'adapter removed' callback,
+ * i.e. 'added==false'.
+ * </p>
+ */
+ void addChangedAdapterSetCallback(ChangedAdapterSetFunc f);
+
+ /**
+ * Remove the given ChangedAdapterSetFunc from this manager.
+ * @param l the to be removed element
+ * @return the number of removed elements
+ */
+ int removeChangedAdapterSetCallback(ChangedAdapterSetFunc f);
};
} // namespace direct_bt
diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp
index 9a9d162b..4c95f823 100644
--- a/examples/direct_bt_scanner10/dbt_scanner10.cpp
+++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp
@@ -506,16 +506,20 @@ static bool startDiscovery(DBTAdapter *a, std::string msg) {
return HCIStatusCode::SUCCESS == status;
}
-static std::shared_ptr<DBTAdapter> createAdapter(const int dev_id0) {
+static std::shared_ptr<DBTAdapter> createAdapter(const int dev_id0, const bool validate_dev_id) {
// 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;
+ if( validate_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;
+ }
} else {
- dev_id = -1;
+ dev_id = dev_id0;
}
if( 0 > dev_id ) {
fprintf(stderr, "Adapter not available (1): Request %d, deduced %d\n", dev_id0, dev_id);
@@ -531,7 +535,6 @@ static std::shared_ptr<DBTAdapter> createAdapter(const int dev_id0) {
fprintf(stderr, "Adapter not powered (2): %s\n", adapter->toString().c_str());
return nullptr;
}
- fprintf(stderr, "Using adapter: %s\n", adapter->toString().c_str());
adapter->addStatusListener(std::shared_ptr<AdapterStatusListener>(new MyAdapterStatusListener()));
@@ -548,12 +551,68 @@ static std::shared_ptr<DBTAdapter> createAdapter(const int dev_id0) {
return adapter;
}
-void test(int dev_id) {
+static jau::cow_vector<std::shared_ptr<DBTAdapter>> adapterList;
+
+static std::shared_ptr<DBTAdapter> getAdapter(const uint16_t dev_id) {
+ std::shared_ptr<std::vector<std::shared_ptr<DBTAdapter>>> snapshot = adapterList.get_snapshot();
+ auto begin = snapshot->begin();
+ auto it = std::find_if(begin, snapshot->end(), [&](std::shared_ptr<DBTAdapter> const& p) -> bool {
+ return p->dev_id == dev_id;
+ });
+ if ( it == std::end(*snapshot) ) {
+ return nullptr;
+ } else {
+ return *it;
+ }
+}
+static int removeAdapter(const uint16_t dev_id) {
+ const std::lock_guard<std::recursive_mutex> lock(adapterList.get_write_mutex());
+ std::shared_ptr<std::vector<std::shared_ptr<DBTAdapter>>> store = adapterList.copy_store();
+ int count = 0;
+ for(auto it = store->begin(); it != store->end(); ) {
+ if ( (*it)->dev_id == dev_id ) {
+ it = store->erase(it);
+ count++;
+ } else {
+ ++it;
+ }
+ }
+ if( 0 < count ) {
+ adapterList.set_store(std::move(store));
+ }
+ return count;
+}
+
+static bool myChangedAdapterSetFunc(const bool added, const AdapterInfo& adapterInfo) {
+ if( added ) {
+ std::shared_ptr<DBTAdapter> pre = getAdapter(adapterInfo.dev_id);
+ if( nullptr != pre ) {
+ fprintf(stderr, "****** Adapter ADDED__: Not new %s\n", pre->toString().c_str());
+ } else {
+ if( adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED) ) {
+ std::shared_ptr<DBTAdapter> adapter = createAdapter(adapterInfo.dev_id, false /* validate_dev_id */);
+ if( nullptr != adapter ) {
+ adapterList.push_back(adapter);
+ fprintf(stderr, "****** Adapter ADDED__: Created %s\n", adapter->toString().c_str());
+ }
+ } else {
+ fprintf(stderr, "****** Adapter ADDED__: Ignored %s\n", adapterInfo.toString().c_str());
+ }
+ }
+ } else {
+ const int count = removeAdapter(adapterInfo.dev_id);
+ fprintf(stderr, "****** Adapter REMOVED: count %d, %s\n", count, adapterInfo.toString().c_str());
+ }
+ return true;
+}
+
+void test() {
bool done = false;
timestamp_t0 = getCurrentMilliseconds();
- std::shared_ptr<DBTAdapter> adapter = createAdapter(dev_id);
+ DBTManager & mngr = DBTManager::get(BTMode::LE);
+ mngr.addChangedAdapterSetCallback(myChangedAdapterSetFunc);
while( !done ) {
if( 0 == MULTI_MEASUREMENTS ||
@@ -566,28 +625,22 @@ void test(int dev_id) {
printDevicesProcessed("****** DevicesProcessed ");
done = true;
} else {
- // 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: %s\n", adapter->toString().c_str());
- if( nullptr != adapter ) {
+
+ jau::for_each_cow(adapterList, [](std::shared_ptr<DBTAdapter>& adapter) {
+ fprintf(stderr, "****** EOL Adapter's Devices - pre close: %s\n", adapter->toString().c_str());
adapter->printSharedPtrListOfDevices();
adapter->close();
fprintf(stderr, "****** EOL Adapter's Devices - post close\n");
adapter->printSharedPtrListOfDevices();
+ });
+ fprintf(stderr, "****** EOL Adapter's Devices - post close all\n");
+
+ {
+ int count = mngr.removeChangedAdapterSetCallback(myChangedAdapterSetFunc);
+ fprintf(stderr, "****** EOL Removed ChangedAdapterSetCallback %d\n", count);
}
}
@@ -595,7 +648,6 @@ void test(int dev_id) {
int main(int argc, char *argv[])
{
- int dev_id = -1; // default
BTMode btMode = BTMode::NONE;
bool waitForEnter=false;
@@ -612,8 +664,6 @@ int main(int argc, char *argv[])
setenv("direct_bt.hci", argv[++i], 1 /* overwrite */);
} else if( !strcmp("-dbt_mgmt", argv[i]) && argc > (i+1) ) {
setenv("direct_bt.mgmt", argv[++i], 1 /* overwrite */);
- } else if( !strcmp("-dev_id", argv[i]) && argc > (i+1) ) {
- dev_id = atoi(argv[++i]);
} else if( !strcmp("-btmode", argv[i]) && argc > (i+1) ) {
btMode = getBTMode(argv[++i]);
if( BTMode::NONE != btMode ) {
@@ -650,7 +700,7 @@ int main(int argc, char *argv[])
}
fprintf(stderr, "pid %d\n", getpid());
- fprintf(stderr, "Run with '[-dev_id <adapter-index>] [-btmode LE|BREDR|DUAL] "
+ fprintf(stderr, "Run with '[-btmode LE|BREDR|DUAL] "
"[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] "
"[-resetEachCon connectionCount] "
"(-mac <device_address>)* (-wl <device_address>)* "
@@ -670,7 +720,6 @@ int main(int argc, char *argv[])
fprintf(stderr, "USE_WHITELIST %d\n", USE_WHITELIST);
fprintf(stderr, "SHOW_UPDATE_EVENTS %d\n", SHOW_UPDATE_EVENTS);
fprintf(stderr, "QUIET %d\n", QUIET);
- fprintf(stderr, "dev_id %d\n", dev_id);
fprintf(stderr, "btmode %s\n", getBTModeString(btMode).c_str());
printList( "waitForDevice: ", waitForDevices);
@@ -680,7 +729,7 @@ int main(int argc, char *argv[])
getchar();
}
fprintf(stderr, "****** TEST start\n");
- test(dev_id);
+ test();
fprintf(stderr, "****** TEST end\n");
if( true ) {
// Just for testing purpose, i.e. triggering DBTManager::close() within the test controlled app,
diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java
index 7c490f24..78283bc9 100644
--- a/examples/java/DBTScanner10.java
+++ b/examples/java/DBTScanner10.java
@@ -30,7 +30,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
import org.tinyb.AdapterSettings;
import org.tinyb.BluetoothAdapter;
@@ -54,6 +57,7 @@ import org.tinyb.HCIStatusCode;
import org.tinyb.HCIWhitelistConnectType;
import org.tinyb.PairingMode;
import org.tinyb.ScanType;
+import org.tinyb.BluetoothManager.ChangedAdapterSetListener;
import direct_bt.tinyb.DBTManager;
@@ -87,8 +91,6 @@ public class DBTScanner10 {
boolean SHOW_UPDATE_EVENTS = false;
boolean QUIET = false;
- int dev_id = -1; // use default
-
int shutdownTest = 0;
static void printf(final String format, final Object... args) {
@@ -525,25 +527,10 @@ public class DBTScanner10 {
return HCIStatusCode.SUCCESS == status;
}
- 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;
- }
-
+ private boolean initAdapter(final BluetoothAdapter adapter) {
if( !adapter.isPowered() ) { // should have been covered above
println("Adapter not powered (2): "+adapter.toString());
- return null;
+ return false;
}
println("Using adapter: "+adapter.toString());
@@ -564,18 +551,71 @@ public class DBTScanner10 {
}
} else {
if( !startDiscovery(adapter, "kick-off") ) {
+ return false;
}
}
- return adapter;
+ return true;
}
- public void runTest(final BluetoothManager manager) {
- BluetoothAdapter adapter = createAdapter(manager, dev_id);
+ private final List<BluetoothAdapter> adapters = new CopyOnWriteArrayList<BluetoothAdapter>();
+
+ @SuppressWarnings("unused")
+ private BluetoothAdapter getAdapter(final int dev_id) {
+ for( int i=0; i<adapters.size(); i++) {
+ final BluetoothAdapter a = adapters.get(i);
+ if( dev_id == a.getDevID() ) {
+ return a;
+ }
+ }
+ return null;
+ }
+
+ private int removeAdapter(final int dev_id) {
+ // Using removeIf allows performing test on all object and remove within one write-lock cycle
+ final int count[] = { 0 };
+ adapters.removeIf(new Predicate<BluetoothAdapter>() {
+ @Override
+ public boolean test(final BluetoothAdapter t) {
+ if( dev_id == t.getDevID() ) {
+ count[0]++;
+ return true;
+ }
+ return false;
+ }
+ });
+ return count[0];
+ }
+ private final BluetoothManager.ChangedAdapterSetListener myChangedAdapterSetListener =
+ new BluetoothManager.ChangedAdapterSetListener() {
+ @Override
+ public void adapterAdded(final BluetoothAdapter adapter) {
+ if( adapter.isPowered() ) {
+ if( initAdapter( adapter ) ) {
+ adapters.add(adapter);
+ println("****** Adapter ADDED__: NewInit " + adapter);
+ } else {
+ println("****** Adapter ADDED__: Failed_ " + adapter);
+ }
+ } else {
+ println("****** Adapter ADDED__: Ignored " + adapter);
+ }
+ }
+
+ @Override
+ public void adapterRemoved(final BluetoothAdapter adapter) {
+ final int count = removeAdapter(adapter.getDevID());
+ println("****** Adapter REMOVED: count "+count+", " + adapter);
+ }
+ };
+
+ public void runTest(final BluetoothManager manager) {
timestamp_t0 = BluetoothUtils.currentTimeMillis();
boolean done = false;
+ manager.addChangedAdapterSetListener(myChangedAdapterSetListener);
+
while( !done ) {
if( 0 == MULTI_MEASUREMENTS.get() ||
( -1 == MULTI_MEASUREMENTS.get() && !waitForDevices.isEmpty() && devicesProcessed.containsAll(waitForDevices) )
@@ -587,19 +627,6 @@ 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(2000);
} catch (final InterruptedException e) {
@@ -608,6 +635,19 @@ public class DBTScanner10 {
}
}
+ adapters.forEach(new Consumer<BluetoothAdapter>() {
+ @Override
+ public void accept(final BluetoothAdapter a) {
+ println("****** EOL Adapter's Devices - pre_ close: " + a);
+ a.close();
+ println("****** EOL Adapter's Devices - post close: " + a);
+ } } );
+
+ {
+ final int count = manager.removeChangedAdapterSetListener(myChangedAdapterSetListener);
+ println("****** EOL Removed ChangedAdapterSetCallback " + count);
+ }
+
// All implicit via destructor or shutdown hook!
// manager.shutdown(); /* implies: adapter.close(); */
}
@@ -663,8 +703,6 @@ public class DBTScanner10 {
test.SHOW_UPDATE_EVENTS = true;
} else if( arg.equals("-quiet") ) {
test.QUIET = true;
- } else if( arg.equals("-dev_id") && args.length > (i+1) ) {
- test.dev_id = Integer.valueOf(args[++i]).intValue();
} else if( arg.equals("-shutdown") && args.length > (i+1) ) {
test.shutdownTest = Integer.valueOf(args[++i]).intValue();
} else if( arg.equals("-mac") && args.length > (i+1) ) {
@@ -690,7 +728,7 @@ public class DBTScanner10 {
test.RESET_ADAPTER_EACH_CONN = Integer.valueOf(args[++i]).intValue();
}
}
- println("Run with '[-default_dev_id <adapter-index>] [-dev_id <adapter-index>] [-btmode LE|BREDR|DUAL] "+
+ println("Run with '[-btmode LE|BREDR|DUAL] "+
"[-bluetoothManager <BluetoothManager-Implementation-Class-Name>] "+
"[-disconnect] [-enableGATTPing] [-count <number>] [-single] (-char <uuid>)* [-show_update_events] [-quiet] "+
"[-resetEachCon connectionCount] "+
@@ -714,7 +752,6 @@ public class DBTScanner10 {
println("SHOW_UPDATE_EVENTS "+test.SHOW_UPDATE_EVENTS);
println("QUIET "+test.QUIET);
- println("dev_id "+test.dev_id);
println("waitForDevice: "+Arrays.toString(test.waitForDevices.toArray()));
println("characteristicList: "+Arrays.toString(test.charIdentifierList.toArray()));
diff --git a/java/direct_bt/tinyb/DBTManager.java b/java/direct_bt/tinyb/DBTManager.java
index 5f663fcd..b7f05fcd 100644
--- a/java/direct_bt/tinyb/DBTManager.java
+++ b/java/direct_bt/tinyb/DBTManager.java
@@ -31,6 +31,8 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
import org.tinyb.BluetoothAdapter;
import org.tinyb.BluetoothDevice;
@@ -43,6 +45,7 @@ import org.tinyb.BluetoothObject;
import org.tinyb.BluetoothManager;
import org.tinyb.BluetoothType;
import org.tinyb.HCIStatusCode;
+import org.tinyb.BluetoothManager.ChangedAdapterSetListener;
public class DBTManager implements BluetoothManager
{
@@ -151,6 +154,8 @@ public class DBTManager implements BluetoothManager
private long nativeInstance;
private final List<BluetoothAdapter> adapters = new CopyOnWriteArrayList<BluetoothAdapter>();
+ private final List<ChangedAdapterSetListener> changedAdapterSetListenerList = new CopyOnWriteArrayList<ChangedAdapterSetListener>();
+
private final Settings settings;
@Override
@@ -277,6 +282,39 @@ public class DBTManager implements BluetoothManager
@Override
public boolean getDiscovering() throws BluetoothException { return getDefaultAdapter().getDiscovering(); }
+ @Override
+ public final void addChangedAdapterSetListener(final ChangedAdapterSetListener l) {
+ changedAdapterSetListenerList.add(l);
+
+ adapters.forEach(new Consumer<BluetoothAdapter>() {
+ @Override
+ public void accept(final BluetoothAdapter adapter) {
+ changedAdapterSetListenerList.forEach(new Consumer<ChangedAdapterSetListener>() {
+ @Override
+ public void accept(final ChangedAdapterSetListener t) {
+ t.adapterAdded(adapter);
+ } } );
+ }
+ });
+ }
+
+ @Override
+ public final int removeChangedAdapterSetListener(final ChangedAdapterSetListener l) {
+ // Using removeIf allows performing test on all object and remove within one write-lock cycle
+ final int count[] = { 0 };
+ changedAdapterSetListenerList.removeIf(new Predicate<ChangedAdapterSetListener>() {
+ @Override
+ public boolean test(final ChangedAdapterSetListener t) {
+ if( t.equals(l) ) {
+ count[0]++;
+ return true;
+ }
+ return false;
+ }
+ });
+ return count[0];
+ }
+
private native List<BluetoothAdapter> getAdapterListImpl();
private native BluetoothAdapter getAdapterImpl(int dev_id);
@@ -290,6 +328,14 @@ public class DBTManager implements BluetoothManager
System.err.println("DBTManager.removeAdapterCB[dev_id "+dev_id+", opc 0x"+Integer.toHexString(opc_reason)+
"]: removed "+a.toString()+", size "+adapters.size());
}
+ if( 0x0005 == opc_reason ) {
+ // MgmtEvent::Opcode::INDEX_REMOVED = 0x0005
+ changedAdapterSetListenerList.forEach(new Consumer<ChangedAdapterSetListener>() {
+ @Override
+ public void accept(final ChangedAdapterSetListener t) {
+ t.adapterRemoved(a);
+ } } );
+ }
return;
}
}
@@ -321,6 +367,14 @@ public class DBTManager implements BluetoothManager
System.err.println("DBTManager.updatedAdapterCB[dev_id "+dev_id+", opc 0x"+Integer.toHexString(opc_reason)+
"]: added "+added+": new "+newInstance.toString()+", size "+adapters.size());
}
+ if( added && 0x0004 == opc_reason ) {
+ // MgmtEvent::Opcode::INDEX_ADDED = 0x0004
+ changedAdapterSetListenerList.forEach(new Consumer<ChangedAdapterSetListener>() {
+ @Override
+ public void accept(final ChangedAdapterSetListener t) {
+ t.adapterAdded(newInstance);
+ } } );
+ }
}
private native void initImpl(final boolean unifyUUID128Bit, final int btMode) throws BluetoothException;
diff --git a/java/org/tinyb/BluetoothManager.java b/java/org/tinyb/BluetoothManager.java
index b8754303..2a558805 100644
--- a/java/org/tinyb/BluetoothManager.java
+++ b/java/org/tinyb/BluetoothManager.java
@@ -75,6 +75,27 @@ public interface BluetoothManager
String toString();
}
+
+ /**
+ * Event listener to receive change events regarding the system's {@link BluetoothAdapter} set,
+ * e.g. about a removed or added {@link BluetoothAdapter} due to user interaction or 'cold reset'.
+ * @since 2.0.0
+ * @implNote Not implemented on tinyb.dbus
+ */
+ public static interface ChangedAdapterSetListener {
+ /**
+ * {@link BluetoothAdapter} was added to the system.
+ * @param adapter the newly added {@link BluetoothAdapter} to the system
+ */
+ void adapterAdded(final BluetoothAdapter adapter);
+
+ /**
+ * {@link BluetoothAdapter} was removed from the system.
+ * @param adapter the removed {@link BluetoothAdapter} from the system
+ */
+ void adapterRemoved(final BluetoothAdapter adapter);
+ }
+
/**
* Returns this implmentation's {@link Settings}.
*/
@@ -269,6 +290,26 @@ public interface BluetoothManager
public boolean getDiscovering() throws BluetoothException;
/**
+ * Add the given {@link ChangedAdapterSetListener} to this manager.
+ * <p>
+ * When a new callback is added, all available adapter's will be reported as added,
+ * this allows a fully event driven workflow.
+ * </p>
+ * @since 2.0.0
+ * @implNote Not implemented on tinyb.dbus
+ */
+ void addChangedAdapterSetListener(final ChangedAdapterSetListener l);
+
+ /**
+ * Remove the given {@link ChangedAdapterSetListener} from this manager.
+ * @param l the to be removed element
+ * @return the number of removed elements
+ * @since 2.0.0
+ * @implNote Not implemented on tinyb.dbus
+ */
+ int removeChangedAdapterSetListener(final ChangedAdapterSetListener l);
+
+ /**
* Release the native memory associated with this object and all related Bluetooth resources.
* The object should not be used following a call to close
* <p>
diff --git a/java/tinyb/dbus/DBusManager.java b/java/tinyb/dbus/DBusManager.java
index 0c87ba49..db7e87f7 100644
--- a/java/tinyb/dbus/DBusManager.java
+++ b/java/tinyb/dbus/DBusManager.java
@@ -38,6 +38,7 @@ import org.tinyb.BluetoothObject;
import org.tinyb.BluetoothManager;
import org.tinyb.BluetoothType;
import org.tinyb.HCIStatusCode;
+import org.tinyb.BluetoothManager.ChangedAdapterSetListener;
public class DBusManager implements BluetoothManager
{
@@ -173,4 +174,10 @@ public class DBusManager implements BluetoothManager
public void shutdown() {
delete();
}
+
+ @Override
+ public final void addChangedAdapterSetListener(final ChangedAdapterSetListener l) {} // FIXME
+
+ @Override
+ public final int removeChangedAdapterSetListener(final ChangedAdapterSetListener l) { return 0; } // FIXME
}
diff --git a/scripts/run-dbt_scanner10.sh b/scripts/run-dbt_scanner10.sh
index b7e1ba51..decd767a 100644
--- a/scripts/run-dbt_scanner10.sh
+++ b/scripts/run-dbt_scanner10.sh
@@ -11,6 +11,10 @@
# ../scripts/run-dbt_scanner10.sh -wait -wl C0:26:DA:01:DA:B1 2>&1 | tee ~/scanner-h01-dbt10.log
# ../scripts/run-dbt_scanner10.sh -wait 2>&1 | tee ~/scanner-h01-dbt10.log
#
+# To do a BT adapter removal/add via software, assuming the device is '1-4' (Bus 1.Port 4):
+# echo '1-4' > /sys/bus/usb/drivers/usb/unbind
+# echo '1-4' > /sys/bus/usb/drivers/usb/bind
+
sdir=`dirname $(readlink -f $0)`
rootdir=`dirname $sdir`
diff --git a/src/direct_bt/DBTManager.cpp b/src/direct_bt/DBTManager.cpp
index 85a6e1ee..e84262e9 100644
--- a/src/direct_bt/DBTManager.cpp
+++ b/src/direct_bt/DBTManager.cpp
@@ -886,6 +886,7 @@ void DBTManager::clearAllMgmtEventCallbacks() noexcept {
for(size_t i=0; i<mgmtAdapterEventCallbackLists.size(); i++) {
mgmtAdapterEventCallbackLists[i].clear();
}
+ mgmtChangedAdapterSetCallbackList.clear();
}
void DBTManager::processAdapterAdded(std::shared_ptr<MgmtEvent> e) noexcept {
@@ -895,6 +896,9 @@ void DBTManager::processAdapterAdded(std::shared_ptr<MgmtEvent> e) noexcept {
const bool added = addAdapterInfo(ai);
DBG_PRINT("DBTManager::Adapter[%d] Added %d: %s", dev_id, added, ai->toString().c_str());
sendMgmtEvent(e);
+ jau::for_each_cow(mgmtChangedAdapterSetCallbackList, [&](ChangedAdapterSetCallback &cb) {
+ cb.invoke(true /* added */, *ai);
+ });
} else {
DBG_PRINT("DBTManager::Adapter[%d] Added 0: Init failed", dev_id);
}
@@ -902,6 +906,9 @@ void DBTManager::processAdapterAdded(std::shared_ptr<MgmtEvent> e) noexcept {
bool DBTManager::mgmtEvAdapterRemovedCB(std::shared_ptr<MgmtEvent> e) noexcept {
DBG_PRINT("DBTManager:mgmt:AdapterRemoved: Start %s", e->toString().c_str());
std::shared_ptr<AdapterInfo> ai = removeAdapterInfo(e->getDevID());
+ jau::for_each_cow(mgmtChangedAdapterSetCallbackList, [&](ChangedAdapterSetCallback &cb) {
+ cb.invoke(false /* added */, *ai);
+ });
DBG_PRINT("DBTManager:mgmt:AdapterRemoved: End: Removed %s", (nullptr != ai ? ai->toString().c_str() : "none"));
return true;
}
@@ -1008,3 +1015,35 @@ bool DBTManager::mgmtEvUserPasskeyRequestCB(std::shared_ptr<MgmtEvent> e) noexce
(void)event;
return true;
}
+
+/**
+ * ChangedAdapterSetCallback handling
+ */
+
+static ChangedAdapterSetCallbackList::equal_comparator _changedAdapterSetCallbackEqComp =
+ [](const ChangedAdapterSetCallback& a, const ChangedAdapterSetCallback& b) -> bool { return a == b; };
+
+
+void DBTManager::addChangedAdapterSetCallback(const ChangedAdapterSetCallback & l) {
+ mgmtChangedAdapterSetCallbackList.push_back(l);
+}
+int DBTManager::removeChangedAdapterSetCallback(const ChangedAdapterSetCallback & l) {
+ return mgmtChangedAdapterSetCallbackList.erase_matching(l, true /* all_matching */, _changedAdapterSetCallbackEqComp);
+}
+
+void DBTManager::addChangedAdapterSetCallback(ChangedAdapterSetFunc f) {
+ addChangedAdapterSetCallback(
+ ChangedAdapterSetCallback(
+ jau::bindPlainFunc<bool, bool, const AdapterInfo&>(f)
+ ) );
+
+ jau::for_each_cow(adapterInfos, [&](std::shared_ptr<AdapterInfo>& ai) {
+ jau::for_each_cow(mgmtChangedAdapterSetCallbackList, [&](ChangedAdapterSetCallback &cb) {
+ cb.invoke(true /* added */, *ai);
+ });
+ });
+}
+int DBTManager::removeChangedAdapterSetCallback(ChangedAdapterSetFunc f) {
+ ChangedAdapterSetCallback l( jau::bindPlainFunc<bool, bool, const AdapterInfo&>(f) );
+ return mgmtChangedAdapterSetCallbackList.erase_matching(l, true /* all_matching */, _changedAdapterSetCallbackEqComp);
+}