diff options
author | Sven Gothel <[email protected]> | 2021-08-28 07:28:29 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2021-08-28 07:28:29 +0200 |
commit | 0b9089769663fc237ef5d4258726258c70cb19bc (patch) | |
tree | 1053174e83b13783d1808de83088479067f3ca19 | |
parent | 80b3f83ff70c23e919f3e43f23860927b24f9654 (diff) |
API Change C++/Java: BTGattChar::addCharListener(..): Use naive BTGattChar::Listener and have implementation handle the BTGattChar association match for notificationsv2.2.13
Turns out previous API and code moved the burden to user to only have the calling BTGattChar object to be matched,
which is not only not intuitive, but also a potential source of issues.
Now we provide a naive interface BTGattChar::Listener to be passed
and BTGattChar::addCharListener(..) wraps its instance in a BTGattCharListener to be delegated.
BTGattChar::removeCharListener(..) has been removed due to high implementation
burden on the Java side. This shall not cause harm to the user, as it isn't useful in a user application.
BTGattChar::removeAllAssociatedCharListener(..) is available on the C++ and Java side,
i.e. fixed API in C++ where we had BTGattChar::removeAllCharListener(..).
Tested and demonstrated in dbt_scanner10.cpp and DBTScanner10.java, as usual.
Both examples also print the service, characteristic and declaration in a more readable fashion.
-rw-r--r-- | README.md | 6 | ||||
-rw-r--r-- | api/direct_bt/BTDevice.hpp | 2 | ||||
-rw-r--r-- | api/direct_bt/BTGattChar.hpp | 159 | ||||
-rw-r--r-- | examples/direct_bt_scanner00/dbt_scanner00.cpp | 7 | ||||
-rw-r--r-- | examples/direct_bt_scanner10/dbt_scanner10.cpp | 62 | ||||
-rw-r--r-- | examples/java/DBTScanner10.java | 78 | ||||
-rw-r--r-- | java/jau/direct_bt/DBTGattChar.java | 40 | ||||
-rw-r--r-- | java/org/direct_bt/BTGattChar.java | 107 | ||||
-rw-r--r-- | java/org/direct_bt/BTGattCharListener.java | 6 | ||||
-rw-r--r-- | java/tinyb/dbus/DBusGattCharacteristic.java | 9 | ||||
-rw-r--r-- | src/direct_bt/BTDevice.cpp | 10 | ||||
-rw-r--r-- | src/direct_bt/BTGattChar.cpp | 52 |
12 files changed, 337 insertions, 201 deletions
@@ -373,6 +373,12 @@ make doc * TODO +**2.2.13** + +* Revised API: BTGattChar::addCharListener(..) in C++ and Java for a more intuitive use. +* Fix EUI48Sub::scanEUI48Sub(..): Fail on missing expected colon, i.e. after each two digits +* Fix JNIAdapterStatusListener::deviceConnected(..): NewObject(.., deviceClazzCtor, ..) used wrong argument order + **2.2.11** * Fix EUI48 unit test and refine on application permissions for launching applications diff --git a/api/direct_bt/BTDevice.hpp b/api/direct_bt/BTDevice.hpp index 9f084b77..0b44afb1 100644 --- a/api/direct_bt/BTDevice.hpp +++ b/api/direct_bt/BTDevice.hpp @@ -919,6 +919,8 @@ namespace direct_bt { */ int removeAllAssociatedCharListener(std::shared_ptr<BTGattChar> associatedCharacteristic) noexcept; + int removeAllAssociatedCharListener(const BTGattChar * associatedCharacteristic) noexcept; + /** * Remove all {@link BTGattCharListener} from the list. * @return number of removed listener. diff --git a/api/direct_bt/BTGattChar.hpp b/api/direct_bt/BTGattChar.hpp index 14eaf4ef..f3aa7ff1 100644 --- a/api/direct_bt/BTGattChar.hpp +++ b/api/direct_bt/BTGattChar.hpp @@ -56,8 +56,6 @@ */ namespace direct_bt { - class BTGattCharListener; // forward - class BTGattHandler; // forward class BTGattService; // forward typedef std::shared_ptr<BTGattService> BTGattServiceRef; @@ -105,6 +103,61 @@ namespace direct_bt { static jau::darray<std::unique_ptr<std::string>> getPropertiesStringList(const PropertyBitVal properties) noexcept; /** + * {@link BTGattChar} event listener for notification and indication events. + * <p> + * This listener instance is attached to a BTGattChar via + * {@link BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener>)} or + * {@link BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener>, bool[])} + * to listen to events associated with the BTGattChar instance. + * </p> + * <p> + * The listener manager maintains a unique set of listener instances without duplicates. + * </p> + * <p> + * Implementation will utilize a BTGattCharListener instance for the listener manager, + * delegating matching BTGattChar events to this instance. + * </p> + */ + class Listener { + public: + /** + * Called from native BLE stack, initiated by a received notification associated + * with the given {@link BTGattChar}. + * @param charDecl {@link BTGattChar} related to this notification + * @param charValue the notification value + * @param timestamp the indication monotonic timestamp, see getCurrentMilliseconds() + */ + virtual void notificationReceived(BTGattCharRef charDecl, + const TROOctets& charValue, const uint64_t timestamp) = 0; + + /** + * Called from native BLE stack, initiated by a received indication associated + * with the given {@link BTGattChar}. + * @param charDecl {@link BTGattChar} related to this indication + * @param charValue the indication value + * @param timestamp the indication monotonic timestamp, see {@link BluetoothUtils#getCurrentMilliseconds()} + * @param confirmationSent if true, the native stack has sent the confirmation, otherwise user is required to do so. + */ + virtual void indicationReceived(BTGattCharRef charDecl, + const TROOctets& charValue, const uint64_t timestamp, + const bool confirmationSent) = 0; + + virtual ~Listener() noexcept {} + + /** + * Default comparison operator, merely testing for same memory reference. + * <p> + * Specializations may override. + * </p> + */ + virtual bool operator==(const Listener& rhs) const noexcept + { return this == &rhs; } + + bool operator!=(const Listener& rhs) const noexcept + { return !(*this == rhs); } + }; + + /** * Characteristics's Service Handle - key to service's handle range, retrieved from Characteristics data. * <p> * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle). @@ -225,42 +278,44 @@ namespace direct_bt { bool enableNotificationOrIndication(bool enabledState[2]); /** - * Add the given BTGattCharListener to the listener list if not already present. + * Add the given BTGattChar::Listener to the listener list if not already present. * <p> - * Occurring notifications and indications, if enabled via configNotificationIndication(bool, bool, bool[]) - * or enableNotificationOrIndication(bool[]), - * will call the respective BTGattCharListener callback method. + * Occurring notifications and indications for this characteristic, + * if enabled via configNotificationIndication(bool, bool, bool[]) or enableNotificationOrIndication(bool[]), + * will call the respective BTGattChar::Listener callback method. * </p> * <p> * Returns true if the given listener is not element of the list and has been newly added, * otherwise false. * </p> * <p> - * Convenience delegation call to BTGattHandler via BTDevice + * Implementation wraps given BTGattChar::Listener into an AssociatedBTGattCharListener + * to restrict the listener to listen only to this BTGattChar instance. * </p> * <p> - * To restrict the listener to listen only to this BTGattChar instance, - * user has to implement BTGattCharListener::match(BTGattCharRef) accordingly. - * <br> - * For this purpose, use may derive from AssociatedBTGattCharListener, - * which provides these simple matching filter facilities. + * Convenience delegation call to BTGattHandler via BTDevice * </p> * @throws IllegalStateException if the {@link BTDevice's}'s {@link BTGattHandler} is null, i.e. not connected + * @see BTGattChar::enableNotificationOrIndication() + * @see BTGattChar::configNotificationIndication() + * @see BTGattChar::addCharListener() + * @see BTGattChar::removeCharListener() + * @see BTGattChar::removeAllAssociatedCharListener() */ - bool addCharListener(std::shared_ptr<BTGattCharListener> l); + bool addCharListener(std::shared_ptr<Listener> l); /** - * Add the given BTGattCharListener to the listener list if not already present + * Add the given BTGattChar::Listener to the listener list if not already present * and if enabling the notification <i>or</i> indication for this characteristic at BLE level was successful.<br> * Notification and/or indication configuration is only performed per characteristic if changed. * <p> - * Implementation will attempt to enable notification only, if available, - * otherwise indication if available. <br> + * Implementation will enable notification if available, + * otherwise indication will be enabled if available. <br> * Implementation uses enableNotificationOrIndication(bool[]) to enable either. * </p> * <p> - * Occurring notifications and indications will call the respective BTGattCharListener - * callback method. + * Occurring notifications and indications for this characteristic + * will call the respective BTGattChar::Listener callback method. * </p> * <p> * Returns true if enabling the notification and/or indication was successful @@ -268,61 +323,40 @@ namespace direct_bt { * otherwise false. * </p> * <p> - * To restrict the listener to listen only to this BTGattChar instance, - * user has to implement BTGattCharListener::match(BTGattCharRef) accordingly. - * <br> - * For this purpose, use may derive from AssociatedBTGattCharListener, - * which provides these simple matching filter facilities. + * Implementation wraps given BTGattChar::Listener into an AssociatedBTGattCharListener + * to restrict the listener to listen only to this BTGattChar instance. * </p> * @param enabledState array of size 2, holding the resulting enabled state for notification and indication * using enableNotificationOrIndication(bool[]) * @throws IllegalStateException if the {@link BTDevice's}'s {@link BTGattHandler} is null, i.e. not connected + * @see BTGattChar::enableNotificationOrIndication() + * @see BTGattChar::configNotificationIndication() + * @see BTGattChar::addCharListener() + * @see BTGattChar::removeCharListener() + * @see BTGattChar::removeAllAssociatedCharListener() */ - bool addCharListener(std::shared_ptr<BTGattCharListener> l, bool enabledState[2]); - - /** - * Disables the notification and/or indication for this characteristic at BLE level - * if `disableIndicationNotification == true` - * and removes the given {@link BTGattCharListener} from the listener list. - * <p> - * Returns true if the given listener is an element of the list and has been removed, - * otherwise false. - * </p> - * <p> - * Convenience delegation call to BTGattHandler via BTDevice - * performing addCharListener(..) - * and {@link #configNotificationIndication(bool, bool, bool[]) if `disableIndicationNotification == true` - * </p> - * <p> - * If the BTDevice's BTGattHandler is null, i.e. not connected, `false` is being returned. - * </p> - * @param l - * @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic - * using {@link #configNotificationIndication(bool, bool, bool[]) - * @return - */ - bool removeCharListener(std::shared_ptr<BTGattCharListener> l, bool disableIndicationNotification); + bool addCharListener(std::shared_ptr<Listener> l, bool enabledState[2]); /** * Disables the notification and/or indication for this characteristic at BLE level * if `disableIndicationNotification == true` - * and removes all {@link BTGattCharListener} from the listener list. + * and removes all associated BTGattChar::Listener and {@link BTGattCharListener} from the listener list. * <p> * Returns the number of removed event listener. * </p> * <p> - * Convenience delegation call to BTGattHandler via BTDevice - * performing addCharListener(..) - * and configNotificationIndication(..) if `disableIndicationNotification == true`. - * </p> - * <p> * If the BTDevice's BTGattHandler is null, i.e. not connected, `zero` is being returned. * </p> * @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic * using {@link #configNotificationIndication(bool, bool, bool[]) * @return + * @see BTGattChar::enableNotificationOrIndication() + * @see BTGattChar::configNotificationIndication() + * @see BTGattChar::addCharListener() + * @see BTGattChar::removeCharListener() + * @see BTGattChar::removeAllAssociatedCharListener() */ - int removeAllCharListener(bool disableIndicationNotification); + int removeAllAssociatedCharListener(bool disableIndicationNotification); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value @@ -382,20 +416,19 @@ namespace direct_bt { /** * {@link BTGattChar} event listener for notification and indication events. * <p> - * A listener instance may be attached to a {@link BTGattChar} via - * {@link BTGattChar::addCharListener(std::shared_ptr<BTGattCharListener>)} to listen to events, - * see method's API doc for {@link BTGattChar} filtering. + * A listener instance may be attached to a BTGattChar instance via + * {@link BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener>)} to listen to its events. * </p> * <p> - * User may utilize {@link AssociatedBTGattCharListener} to listen to only one {@link BTGattChar}. - * </p> - * <p> - * A listener instance may be attached to a {@link BTGattHandler} via + * A listener instance may be attached to a BTGattHandler via * {@link BTGattHandler::addCharListener(std::shared_ptr<BTGattCharListener>)} * to listen to all events of the device or the matching filtered events. * </p> * <p> - * The listener receiver maintains a unique set of listener instances without duplicates. + * User may utilize {@link AssociatedBTGattCharListener} to listen to only one {@link BTGattChar}. + * </p> + * <p> + * The listener manager maintains a unique set of listener instances without duplicates. * </p> */ class BTGattCharListener { @@ -453,7 +486,7 @@ namespace direct_bt { { return !(*this == rhs); } }; - class AssociatedBTGattCharListener : public BTGattCharListener{ + class AssociatedBTGattCharListener : public BTGattCharListener { private: const BTGattChar * associatedChar; diff --git a/examples/direct_bt_scanner00/dbt_scanner00.cpp b/examples/direct_bt_scanner00/dbt_scanner00.cpp index 5ca42638..2f340a05 100644 --- a/examples/direct_bt_scanner00/dbt_scanner00.cpp +++ b/examples/direct_bt_scanner00/dbt_scanner00.cpp @@ -115,11 +115,10 @@ class MyAdapterStatusListener : public AdapterStatusListener { static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT); -class MyGATTEventListener : public AssociatedBTGattCharListener { +class MyGATTEventListener : public BTGattChar::Listener { public: - MyGATTEventListener(const BTGattChar * characteristicMatch) - : AssociatedBTGattCharListener(characteristicMatch) {} + MyGATTEventListener() {} void notificationReceived(BTGattCharRef charDecl, const TROOctets& charValue, const uint64_t timestamp) override { const std::shared_ptr<BTDevice> dev = charDecl->getDeviceChecked(); @@ -307,7 +306,7 @@ int main(int argc, char *argv[]) fprintf(stderr, " [%2.2d.%2.2d] Config Notification(%d), Indication(%d): Result %d\n", (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], cccdRet); if( cccdRet ) { - serviceChar.addCharListener( std::shared_ptr<BTGattCharListener>( new MyGATTEventListener(&serviceChar) ) ); + serviceChar.addCharListener( std::shared_ptr<BTGattChar::Listener>( new MyGATTEventListener() ) ); } } } diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp index 664f564e..4fff044f 100644 --- a/examples/direct_bt_scanner10/dbt_scanner10.cpp +++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp @@ -314,41 +314,43 @@ class MyAdapterStatusListener : public AdapterStatusListener { static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT); -class MyGATTEventListener : public AssociatedBTGattCharListener { +class MyGATTEventListener : public BTGattChar::Listener { + private: + int i, j; + public: - MyGATTEventListener(const BTGattChar * characteristicMatch) - : AssociatedBTGattCharListener(characteristicMatch) {} + MyGATTEventListener(int i_, int j_) : i(i_), j(j_) {} void notificationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp) override { - const std::shared_ptr<BTDevice> dev = charDecl->getDeviceChecked(); const uint64_t tR = getCurrentMilliseconds(); - fprintf_td(stderr, "****** GATT Notify (td %" PRIu64 " ms, dev-discovered %" PRIu64 " ms): From %s\n", - (tR-timestamp), (tR-dev->getLastDiscoveryTimestamp()), dev->toString().c_str()); - if( nullptr != charDecl ) { - fprintf_td(stderr, "****** decl %s\n", charDecl->toString().c_str()); + fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic-Notify: UUID %s, td %" PRIu64 " ******\n", + i, j, charDecl->value_type->toUUID128String().c_str(), (tR-timestamp)); + fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic: %s ******\n", i, j, charDecl->toString().c_str()); + if( _TEMPERATURE_MEASUREMENT == *charDecl->value_type ) { + std::shared_ptr<GattTemperatureMeasurement> temp = GattTemperatureMeasurement::get(char_value); + if( nullptr != temp ) { + fprintf_td(stderr, "**[%2.2d.%2.2d] Value T: %s ******\n", i, j, temp->toString().c_str()); + } } - fprintf_td(stderr, "****** rawv %s\n", char_value.toString().c_str()); + fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); } void indicationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp, const bool confirmationSent) override { - const std::shared_ptr<BTDevice> dev = charDecl->getDeviceChecked(); const uint64_t tR = getCurrentMilliseconds(); - fprintf_td(stderr, "****** GATT Indication (confirmed %d, td(msg %" PRIu64 " ms, dev-discovered %" PRIu64 " ms): From %s\n", - confirmationSent, (tR-timestamp), (tR-dev->getLastDiscoveryTimestamp()), dev->toString().c_str()); - if( nullptr != charDecl ) { - fprintf_td(stderr, "****** decl %s\n", charDecl->toString().c_str()); - if( _TEMPERATURE_MEASUREMENT == *charDecl->value_type ) { - std::shared_ptr<GattTemperatureMeasurement> temp = GattTemperatureMeasurement::get(char_value); - if( nullptr != temp ) { - fprintf_td(stderr, "****** valu %s\n", temp->toString().c_str()); - } + fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic-Indication: UUID %s, td %" PRIu64 ", confirmed %d ******\n", + i, j, charDecl->value_type->toUUID128String().c_str(), (tR-timestamp), confirmationSent); + fprintf_td(stderr, "**[%2.2d.%2.2d] Characteristic: %s ******\n", i, j, charDecl->toString().c_str()); + if( _TEMPERATURE_MEASUREMENT == *charDecl->value_type ) { + std::shared_ptr<GattTemperatureMeasurement> temp = GattTemperatureMeasurement::get(char_value); + if( nullptr != temp ) { + fprintf_td(stderr, "**[%2.2d.%2.2d] Value T: %s ******\n", i, j, temp->toString().c_str()); } } - fprintf_td(stderr, "****** rawv %s\n", char_value.toString().c_str()); + fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); } }; @@ -503,21 +505,22 @@ static void processReadyDevice(std::shared_ptr<BTDevice> device) { for(size_t i=0; i<primServices.size(); i++) { BTGattService & primService = *primServices.at(i); if( !QUIET ) { - // fprintf_td(stderr, " [%2.2d] Service %s\n", (int)i, primService.toString().c_str()); - fprintf_td(stderr, " [%2.2d] Service Characteristics\n", (int)i); + fprintf_td(stderr, " [%2.2d] Service UUID %s\n", i, primService.type->toUUID128String().c_str()); + fprintf_td(stderr, " [%2.2d] %s\n", i, primService.toString().c_str()); } jau::darray<BTGattCharRef> & serviceCharacteristics = primService.characteristicList; for(size_t j=0; j<serviceCharacteristics.size(); j++) { BTGattChar & serviceChar = *serviceCharacteristics.at(j); if( !QUIET ) { - fprintf_td(stderr, " [%2.2d.%2.2d] CharDef: %s\n", (int)i, (int)j, serviceChar.toString().c_str()); + fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic: UUID %s\n", i, j, serviceChar.value_type->toUUID128String().c_str()); + fprintf_td(stderr, " [%2.2d.%2.2d] %s\n", i, j, serviceChar.toString().c_str()); } if( serviceChar.hasProperties(BTGattChar::PropertyBitVal::Read) ) { POctets value(BTGattHandler::number(BTGattHandler::Defaults::MAX_ATT_MTU), 0); if( serviceChar.readValue(value) ) { std::string sval = dfa_utf8_decode(value.get_ptr(), value.getSize()); if( !QUIET ) { - fprintf_td(stderr, " [%2.2d.%2.2d] CharVal: %s ('%s')\n", (int)i, (int)j, value.toString().c_str(), sval.c_str()); + fprintf_td(stderr, " [%2.2d.%2.2d] value: %s ('%s')\n", (int)i, (int)j, value.toString().c_str(), sval.c_str()); } } } @@ -525,17 +528,20 @@ static void processReadyDevice(std::shared_ptr<BTDevice> device) { for(size_t k=0; k<charDescList.size(); k++) { BTGattDesc & charDesc = *charDescList.at(k); if( !QUIET ) { - fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] Desc: %s\n", (int)i, (int)j, (int)k, charDesc.toString().c_str()); + fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] Descriptor: UUID %s\n", i, j, k, charDesc.type->toUUID128String().c_str()); + fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] %s\n", i, j, k, charDesc.toString().c_str()); } } bool cccdEnableResult[2]; - bool cccdRet = serviceChar.addCharListener( std::shared_ptr<BTGattCharListener>( new MyGATTEventListener(&serviceChar) ), - cccdEnableResult ); + bool cccdRet = serviceChar.addCharListener( std::shared_ptr<BTGattChar::Listener>( new MyGATTEventListener(i, j) ), + cccdEnableResult ); if( !QUIET ) { - fprintf_td(stderr, " [%2.2d.%2.2d] addCharacteristicListener Notification(%d), Indication(%d): Result %d\n", + fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], cccdRet); + fprintf_td(stderr, "\n"); } } + fprintf_td(stderr, "\n"); } // FIXME sleep 1s for potential callbacks .. std::this_thread::sleep_for(std::chrono::milliseconds(1000)); diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java index 6ebef06d..14ffee1e 100644 --- a/examples/java/DBTScanner10.java +++ b/examples/java/DBTScanner10.java @@ -303,6 +303,36 @@ public class DBTScanner10 { } }; + class MyGATTEventListener implements BTGattChar.Listener { + private final int i, j; + + public MyGATTEventListener(final int i_, final int j_) { i=i_; j=j_; } + + @Override + public void notificationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp) { + final long tR = BTUtils.currentTimeMillis(); + printf("**[%02d.%02d] Characteristic-Notify: UUID %s, td %d ******\n", + i, j, charDecl.getUUID(), (tR-timestamp)); + printf("**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); + printf("**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); + + shutdownTest(); + } + + @Override + public void indicationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp, final boolean confirmationSent) { + final long tR = BTUtils.currentTimeMillis(); + printf("**[%02d.%02d] Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n", + i, j, charDecl.getUUID(), (tR-timestamp), confirmationSent); + printf("**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); + printf("**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); + + shutdownTest(); + } + } + private void connectDiscoveredDevice(final BTDevice device) { BTUtils.println(System.err, "****** Connecting Device: Start " + device.toString()); @@ -439,7 +469,7 @@ public class DBTScanner10 { BTUtils.println(System.err, "Char UUID "+charIdentifier); BTUtils.println(System.err, " over device : "+char2); if( null != char2 ) { - final BTGattCharListener charPingPongListener = new BTGattCharListener(null) { + final BTGattChar.Listener charPingPongListener = new BTGattChar.Listener() { @Override public void notificationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp) { @@ -469,54 +499,29 @@ public class DBTScanner10 { } } } - { - final BTGattCharListener myCharacteristicListener = new BTGattCharListener(null) { - @Override - public void notificationReceived(final BTGattChar charDecl, - final byte[] value, final long timestamp) { - BTUtils.println(System.err, "****** GATT notificationReceived: "+charDecl+ - ", value "+BTUtils.bytesHexString(value, 0, -1, true)); - shutdownTest(); - - } - - @Override - public void indicationReceived(final BTGattChar charDecl, - final byte[] value, final long timestamp, final boolean confirmationSent) { - BTUtils.println(System.err, "****** GATT indicationReceived: "+charDecl+ - ", value "+BTUtils.bytesHexString(value, 0, -1, true)); - shutdownTest(); - } - }; - final boolean addedCharacteristicListenerRes = - BTGattService.addCharListenerToAll(device, primServices, myCharacteristicListener); - if( !QUIET ) { - BTUtils.println(System.err, "Added GATTCharacteristicListener: "+addedCharacteristicListenerRes); - } - } try { int i=0; for(final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) { final BTGattService primService = srvIter.next(); if( !QUIET ) { - printf(" [%02d] Service %s, uuid %s\n", i, primService.toString(), primService.getUUID()); - printf(" [%02d] Service Characteristics\n", i); + printf(" [%02d] Service UUID %s\n", i, primService.getUUID()); + printf(" [%02d] %s\n", i, primService.toString()); } int j=0; final List<BTGattChar> serviceCharacteristics = primService.getChars(); for(final Iterator<BTGattChar> charIter = serviceCharacteristics.iterator(); charIter.hasNext(); j++) { final BTGattChar serviceChar = charIter.next(); if( !QUIET ) { - printf(" [%02d.%02d] CharDef: %s, uuid %s\n", i, j, serviceChar.toString(), serviceChar.getUUID()); + printf(" [%02d.%02d] Characteristic: UUID %s\n", i, j, serviceChar.getUUID()); + printf(" [%02d.%02d] %s\n", i, j, serviceChar.toString()); } final List<String> properties = Arrays.asList(serviceChar.getFlags()); if( properties.contains("read") ) { final byte[] value = serviceChar.readValue(); final String svalue = BTUtils.decodeUTF8String(value, 0, value.length); if( !QUIET ) { - printf(" [%02d.%02d] CharVal: %s ('%s')\n", - i, j, BTUtils.bytesHexString(value, 0, -1, true), svalue); + printf(" [%02d.%02d] value: %s ('%s')\n", i, j, BTUtils.bytesHexString(value, 0, -1, true), svalue); } } int k=0; @@ -524,10 +529,19 @@ public class DBTScanner10 { for(final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) { final BTGattDesc charDesc = descIter.next(); if( !QUIET ) { - printf(" [%02d.%02d.%02d] Desc: %s, uuid %s\n", i, j, k, charDesc.toString(), charDesc.getUUID()); + printf(" [%02d.%02d.%02d] Descriptor: UUID %s\n", i, j, k, charDesc.getUUID()); + printf(" [%02d.%02d.%02d] %s\n", i, j, k, charDesc.toString()); } } + final boolean cccdEnableResult[] = { false, false }; + final boolean cccdRet = serviceChar.addCharListener( new MyGATTEventListener(i, j), cccdEnableResult ); + if( !QUIET ) { + printf(" [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n", + i, j, cccdEnableResult[0], cccdEnableResult[1], cccdRet); + printf("\n"); + } } + printf("\n"); } } catch( final Exception ex) { BTUtils.println(System.err, "Caught "+ex.getMessage()); diff --git a/java/jau/direct_bt/DBTGattChar.java b/java/jau/direct_bt/DBTGattChar.java index ff71938a..b3ec3222 100644 --- a/java/jau/direct_bt/DBTGattChar.java +++ b/java/jau/direct_bt/DBTGattChar.java @@ -126,7 +126,7 @@ public class DBTGattChar extends DBTObject implements BTGattChar { // This characteristicListener serves TinyB 'enableValueNotification(..)' and 'getValue()' (cached value) // backwards compatibility only! - final BTGattCharListener characteristicListener = new BTGattCharListener(this) { + final Listener characteristicListener = new Listener() { @Override public void notificationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp) { final DBTGattChar cd = (DBTGattChar)charDecl; @@ -301,25 +301,39 @@ public class DBTGattChar extends DBTObject implements BTGattChar return configNotificationIndication(enableNotification, enableIndication, enabledState); } + static private class DelegatedBTGattCharListener extends BTGattCharListener { + private final Listener delegate; + + public DelegatedBTGattCharListener(final BTGattChar characteristicMatch, final Listener l) { + super(characteristicMatch); + delegate = l; + } + + @Override + public void notificationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp) { + delegate.notificationReceived(charDecl, value, timestamp); + } + + @Override + public void indicationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp, + final boolean confirmationSent) { + delegate.indicationReceived(charDecl, value, timestamp, confirmationSent); + } + }; + @Override - public final boolean addCharListener(final BTGattCharListener listener) { - return getService().getDevice().addCharListener(listener); + public final boolean addCharListener(final Listener listener) { + return getService().getDevice().addCharListener( new DelegatedBTGattCharListener(this, listener) ); } @Override - public final boolean addCharListener(final BTGattCharListener listener, final boolean enabledState[/*2*/]) { + public final boolean addCharListener(final Listener listener, final boolean enabledState[/*2*/]) { if( !enableNotificationOrIndication(enabledState) ) { return false; } - return getService().getDevice().addCharListener(listener); - } - - @Override - public final boolean removeCharListener(final BTGattCharListener l, final boolean disableIndicationNotification) { - if( disableIndicationNotification ) { - configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]); - } - return getService().getDevice().removeCharListener(l); + return addCharListener( listener ); } @Override diff --git a/java/org/direct_bt/BTGattChar.java b/java/org/direct_bt/BTGattChar.java index 5dfb2373..078fbd0f 100644 --- a/java/org/direct_bt/BTGattChar.java +++ b/java/org/direct_bt/BTGattChar.java @@ -38,6 +38,45 @@ import java.util.List; */ public interface BTGattChar extends BTObject { + /** + * {@link BTGattChar} event listener for notification and indication events. + * <p> + * This listener instance is attached to a {@link BTGattChar} via + * {@link BTGattChar#addCharListener(Listener)} or {@link BTGattChar#addCharListener(Listener, boolean[])} + * to listen to events associated with the {@link BTGattChar} instance. + * </p> + * <p> + * The listener manager maintains a unique set of listener instances without duplicates. + * </p> + * <p> + * Implementation will utilize a {@link BTGattCharListener) for the listener manager, + * delegating matching {@link BTGattChar} events to this instance. + * </p> + */ + static public interface Listener { + /** + * Called from native BLE stack, initiated by a received notification associated + * with the given {@link BTGattChar}. + * @param charDecl {@link BTGattChar} related to this notification + * @param value the notification value + * @param timestamp the indication monotonic timestamp, see {@link BTUtils#currentTimeMillis()} + */ + public void notificationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp); + + /** + * Called from native BLE stack, initiated by a received indication associated + * with the given {@link BTGattChar}. + * @param charDecl {@link BTGattChar} related to this indication + * @param value the indication value + * @param timestamp the indication monotonic timestamp, see {@link BTUtils#currentTimeMillis()} + * @param confirmationSent if true, the native stack has sent the confirmation, otherwise user is required to do so. + */ + public void indicationReceived(final BTGattChar charDecl, + final byte[] value, final long timestamp, + final boolean confirmationSent); + }; + @Override public BTGattChar clone(); @@ -130,80 +169,69 @@ public interface BTGattChar extends BTObject throws IllegalStateException; /** - * Add the given {@link BTGattCharListener} to the listener list if not already present. + * Add the given {@link BTGattChar.Listener} to the listener list if not already present. * <p> - * Occurring notifications and indications, if enabled via {@link #configNotificationIndication(boolean, boolean, boolean[])} + * Occurring notifications and indications for this characteristic, + * if enabled via {@link #configNotificationIndication(boolean, boolean, boolean[])} * or {@link #enableNotificationOrIndication(boolean[])}, - * will call the respective BTGattCharListener callback method. + * will call the respective {@link BTGattChar.Listener} callback method. * </p> - * @param listener A {@link BTGattCharListener} instance, listening to this {@link BTGattChar}'s events + * <p> + * Implementation wraps given {@link BTGattChar.Listener} into a {@link BTGattCharListener} + * to restrict the listener to listen only to this BTGattChar instance. + * </p> + * @param listener A {@link BTGattChar.Listener} instance, listening to this {@link BTGattChar}'s events * @return true if the given listener is not element of the list and has been newly added, otherwise false. * @throws IllegalStateException if the DBTDevice's GATTHandler is null, i.e. not connected - * @throws IllegalStateException if the given {@link BTGattCharListener} is already in use, i.e. added. + * @throws IllegalStateException if the given {@link BTGattChar.Listener} is already in use, i.e. added. * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) + * @see #addCharListener(Listener, boolean[]) + * @see #removeCharListener(Listener, boolean) + * @see #removeAllAssociatedCharListener(boolean) * @since 2.0.0 * @implNote not implemented in tinyb.dbus */ - public boolean addCharListener(final BTGattCharListener listener) + public boolean addCharListener(final Listener listener) throws IllegalStateException; /** - * Add the given {@link BTGattCharListener} to the listener list if not already present + * Add the given {@link BTGattChar.Listener} to the listener list if not already present * and if enabling the notification <i>or</i> indication for this characteristic at BLE level was successful.<br> * Notification and/or indication configuration is only performed per characteristic if changed. * <p> - * Implementation will attempt to enable notification only, if available, - * otherwise indication if available. <br> - * Implementation uses {@link #enableNotificationOrIndication(boolean[])} to enable one. + * Implementation will enable notification if available, + * otherwise indication will be enabled if available. <br> + * Implementation uses {@link #enableNotificationOrIndication(boolean[])} to enable either. * </p> * <p> - * Occurring notifications and indications will call the respective {@link BTGattCharListener} - * callback method. + * Occurring notifications and indications for this characteristic + * will call the respective {@link BTGattChar.Listener} callback method. * </p> - * @param listener A {@link BTGattCharListener} instance, listening to this {@link BTGattChar}'s events + * @param listener A {@link BTGattChar.Listener} instance, listening to this {@link BTGattChar}'s events * @param enabledState array of size 2, holding the resulting enabled state for notification and indication * using {@link #enableNotificationOrIndication(boolean[])} * @return true if enabling the notification and/or indication was successful * and if the given listener is not element of the list and has been newly added, otherwise false. * @throws IllegalStateException if the {@link BTDevice}'s GATTHandler is null, i.e. not connected - * @throws IllegalStateException if the given {@link BTGattCharListener} is already in use, i.e. added. + * @throws IllegalStateException if the given {@link BTGattChar.Listener} is already in use, i.e. added. * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) + * @see #addCharListener(Listener) + * @see #removeCharListener(Listener, boolean) + * @see #removeAllAssociatedCharListener(boolean) * @since 2.0.0 * @implNote not implemented in tinyb.dbus */ - public boolean addCharListener(final BTGattCharListener listener, final boolean enabledState[/*2*/]) + public boolean addCharListener(final Listener listener, final boolean enabledState[/*2*/]) throws IllegalStateException; /** - * Disables the notification and/or indication for this characteristic at BLE level - * if {@code disableIndicationNotification == true} - * and removes the given {@link BTGattCharListener} from the listener list. - * <p> - * If the DBTDevice's GATTHandler is null, i.e. not connected, {@code false} is being returned. - * </p> - * - * @param listener A {@link BTGattCharListener} instance - * @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic - * using {@link #configNotificationIndication(boolean, boolean, boolean[])} - * @return true if the given listener is an element of the list and has been removed, otherwise false. - * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @since 2.0.0 - * @implNote not implemented in tinyb.dbus - */ - public boolean removeCharListener(final BTGattCharListener l, final boolean disableIndicationNotification); - - /** * Disables the notification and/or indication for this characteristic BLE level * if {@code disableIndicationNotification == true} - * and removes all {@link BTGattCharListener} from the listener list, + * and removes all associated {@link BTGattChar.Listener} or {@link BTGattCharListener} from the listener list, * which are associated with this characteristic instance. * <p> - * Implementation tests all listener's {@link BTGattCharListener#getAssociatedChar()} - * to match with this characteristic instance. - * </p> - * <p> * If the DBTDevice's GATTHandler is null, i.e. not connected, {@code false} is being returned. * </p> * @@ -212,6 +240,9 @@ public interface BTGattChar extends BTObject * @return number of removed listener. * @see #configNotificationIndication(boolean, boolean, boolean[]) * @see BTDevice#removeAllAssociatedCharListener(BTGattChar) + * @see #addCharListener(Listener) + * @see #addCharListener(Listener, boolean[]) + * @see #removeCharListener(Listener, boolean) * @since 2.0.0 * @implNote not implemented in tinyb.dbus */ diff --git a/java/org/direct_bt/BTGattCharListener.java b/java/org/direct_bt/BTGattCharListener.java index d24a3d49..862ef4b0 100644 --- a/java/org/direct_bt/BTGattCharListener.java +++ b/java/org/direct_bt/BTGattCharListener.java @@ -31,7 +31,7 @@ import java.lang.ref.WeakReference; * {@link BTGattChar} event listener for notification and indication events. * <p> * A listener instance may be attached to a {@link BTGattChar} via - * {@link BTGattChar#addCharListener(BTGattCharListener)} to listen to its events. + * {@link BTGattChar#addCharListener(org.direct_bt.BTGattChar.Listener)} to listen to its events. * </p> * <p> * A listener instance may be attached to a {@link BTDevice} via @@ -39,7 +39,7 @@ import java.lang.ref.WeakReference; * to listen to all events of the device or the matching filtered events. * </p> * <p> - * One {@link BTGattCharListener} instance can only be attached to a listener receiver once at a time, + * One {@link BTGattCharListener} instance can only be attached to a listener manager once at a time, * i.e. you cannot attach the same instance more than once to a {@link BTDevice} * or {@link BTGattChar}. * <br> @@ -50,7 +50,7 @@ import java.lang.ref.WeakReference; * The latter will be added to the native list of listeners. * This class's {@code nativeInstance} field links the Java instance to mentioned C++ listener. * <br> - * Since the listener receiver maintains a unique set of listener instances without duplicates, + * Since the listener manager maintains a unique set of listener instances without duplicates, * this restriction is more esoteric. * </p> */ diff --git a/java/tinyb/dbus/DBusGattCharacteristic.java b/java/tinyb/dbus/DBusGattCharacteristic.java index df270cfc..d37e1713 100644 --- a/java/tinyb/dbus/DBusGattCharacteristic.java +++ b/java/tinyb/dbus/DBusGattCharacteristic.java @@ -37,7 +37,6 @@ import org.direct_bt.BTGattService; import org.direct_bt.BTManager; import org.direct_bt.BTNotification; import org.direct_bt.BTType; -import org.direct_bt.BTGattCharListener; public class DBusGattCharacteristic extends DBusObject implements BTGattChar { @@ -110,7 +109,7 @@ public class DBusGattCharacteristic extends DBusObject implements BTGattChar } @Override - public boolean addCharListener(final BTGattCharListener listener) { + public boolean addCharListener(final Listener listener) { return false; // FIXME } @Override @@ -126,16 +125,12 @@ public class DBusGattCharacteristic extends DBusObject implements BTGattChar return false; // FIXME } @Override - public boolean addCharListener(final BTGattCharListener listener, final boolean[] enabledState) + public boolean addCharListener(final Listener listener, final boolean[] enabledState) throws IllegalStateException { return false; // FIXME } @Override - public boolean removeCharListener(final BTGattCharListener l, final boolean disableIndicationNotification) { - return false; // FIXME - } - @Override public int removeAllAssociatedCharListener(final boolean disableIndicationNotification) { return 0; // FIXME } diff --git a/src/direct_bt/BTDevice.cpp b/src/direct_bt/BTDevice.cpp index 503912dd..5f98d855 100644 --- a/src/direct_bt/BTDevice.cpp +++ b/src/direct_bt/BTDevice.cpp @@ -1625,6 +1625,16 @@ int BTDevice::removeAllAssociatedCharListener(std::shared_ptr<BTGattChar> associ return gatt->removeAllAssociatedCharListener( associatedCharacteristic ); } +int BTDevice::removeAllAssociatedCharListener(const BTGattChar * associatedCharacteristic) noexcept { + std::shared_ptr<BTGattHandler> gatt = getGattHandler(); + if( nullptr == gatt ) { + // OK to have GATTHandler being shutdown @ disable + DBG_PRINT("Device's GATTHandle not connected: %s", toString().c_str()); + return false; + } + return gatt->removeAllAssociatedCharListener( associatedCharacteristic ); +} + int BTDevice::removeAllCharListener() noexcept { std::shared_ptr<BTGattHandler> gatt = getGattHandler(); if( nullptr == gatt ) { diff --git a/src/direct_bt/BTGattChar.cpp b/src/direct_bt/BTGattChar.cpp index 41c93b7f..519a8029 100644 --- a/src/direct_bt/BTGattChar.cpp +++ b/src/direct_bt/BTGattChar.cpp @@ -248,31 +248,57 @@ bool BTGattChar::enableNotificationOrIndication(bool enabledState[2]) { return configNotificationIndication(enableNotification, enableIndication, enabledState); } -bool BTGattChar::addCharListener(std::shared_ptr<BTGattCharListener> l) { - return getDeviceChecked()->addCharListener(l); +class DelegatedBTGattCharListener : public BTGattCharListener { + private: + const BTGattChar * associatedChar; + std::shared_ptr<BTGattChar::Listener> delegate; + + public: + DelegatedBTGattCharListener(const BTGattChar * characteristicMatch, std::shared_ptr<BTGattChar::Listener> l) noexcept + : associatedChar(characteristicMatch), delegate(l) { } + + bool match(const BTGattChar & characteristic) noexcept override { + if( nullptr == associatedChar ) { + return true; + } + return *associatedChar == characteristic; + } + + void notificationReceived(BTGattCharRef charDecl, + const TROOctets& charValue, const uint64_t timestamp) override { + delegate->notificationReceived(charDecl, charValue, timestamp); + } + + void indicationReceived(BTGattCharRef charDecl, + const TROOctets& charValue, const uint64_t timestamp, + const bool confirmationSent) override { + delegate->indicationReceived(charDecl, charValue, timestamp, confirmationSent); + } + + bool operator==(const DelegatedBTGattCharListener& rhs) const noexcept + { return delegate.get() == rhs.delegate.get(); } + + bool operator!=(const DelegatedBTGattCharListener& rhs) const noexcept + { return !(*this == rhs); } +}; + +bool BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener> l) { + return getDeviceChecked()->addCharListener( std::shared_ptr<BTGattCharListener>( new DelegatedBTGattCharListener(this, l) ) ); } -bool BTGattChar::addCharListener(std::shared_ptr<BTGattCharListener> l, bool enabledState[2]) { +bool BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener> l, bool enabledState[2]) { if( !enableNotificationOrIndication(enabledState) ) { return false; } return addCharListener(l); } -bool BTGattChar::removeCharListener(std::shared_ptr<BTGattCharListener> l, bool disableIndicationNotification) { - if( disableIndicationNotification ) { - bool enabledState[2]; - configNotificationIndication(false, false, enabledState); - } - return getDeviceChecked()->removeCharListener(l); -} - -int BTGattChar::removeAllCharListener(bool disableIndicationNotification) { +int BTGattChar::removeAllAssociatedCharListener(bool disableIndicationNotification) { if( disableIndicationNotification ) { bool enabledState[2]; configNotificationIndication(false, false, enabledState); } - return getDeviceChecked()->removeAllCharListener(); + return getDeviceChecked()->removeAllAssociatedCharListener(this); } bool BTGattChar::readValue(POctets & res, int expectedLength) { |