diff options
28 files changed, 610 insertions, 751 deletions
diff --git a/api/direct_bt/BTAdapter.hpp b/api/direct_bt/BTAdapter.hpp index ee16f881..54574bc1 100644 --- a/api/direct_bt/BTAdapter.hpp +++ b/api/direct_bt/BTAdapter.hpp @@ -263,7 +263,7 @@ namespace direct_bt { (void)timestamp; } - virtual ~AdapterStatusListener() {} + ~AdapterStatusListener() noexcept override {} std::string toString() const noexcept override { return "AdapterStatusListener["+jau::to_hexstring(this)+"]"; } @@ -292,24 +292,6 @@ namespace direct_bt { // ************************************************* // ************************************************* - namespace impl { - struct StatusListenerPair { - /** The actual listener */ - AdapterStatusListenerRef listener; - /** The optional weak device reference. Weak, b/c it shall not block destruction */ - std::weak_ptr<BTDevice> wbr_device; - - bool match(const BTDeviceRef& device) const noexcept { - BTDeviceRef sda = wbr_device.lock(); - if( nullptr != sda && nullptr != device ) { - return *sda == *device; - } else { - return true; - } - } - }; - } - /** * BTAdapter represents one local Bluetooth Controller. * @@ -397,7 +379,23 @@ namespace direct_bt { jau::simple_timer smp_watchdog; jau::fraction_i64 smp_timeoutfunc(jau::simple_timer& timer); - typedef jau::cow_darray<impl::StatusListenerPair> statusListenerList_t; + struct StatusListenerPair { + /** The actual listener */ + AdapterStatusListenerRef listener; + /** The optional weak device reference. Weak, b/c it shall not block destruction */ + std::weak_ptr<BTDevice> wbr_device; + + bool match(const BTDeviceRef& device) const noexcept { + BTDeviceRef sda = wbr_device.lock(); + if( nullptr != sda && nullptr != device ) { + return *sda == *device; + } else { + return true; + } + } + }; + typedef jau::cow_darray<StatusListenerPair> statusListenerList_t; + static statusListenerList_t::equal_comparator adapterStatusListenerRefEqComparator; statusListenerList_t statusListenerList; // Storing SMPKeyBin entries, referenced by their remote address, i.e. BTDevice address. @@ -563,7 +561,7 @@ namespace direct_bt { void sendDeviceUpdated(std::string cause, BTDeviceRef device, uint64_t timestamp, EIRDataType updateMask) noexcept; - int removeAllStatusListener(const BTDevice& d); + int removeAllStatusListener(const BTDevice& d) noexcept; public: @@ -961,14 +959,14 @@ namespace direct_bt { * @see removeStatusListener() * @see removeAllStatusListener() */ - bool addStatusListener(const AdapterStatusListenerRef& l); + bool addStatusListener(const AdapterStatusListenerRef& l) noexcept; /** * Please use BTDevice::addStatusListener() for clarity, merely existing here to allow JNI access. */ - bool addStatusListener(const BTDeviceRef& d, const AdapterStatusListenerRef& l); + bool addStatusListener(const BTDeviceRef& d, const AdapterStatusListenerRef& l) noexcept; - bool addStatusListener(const BTDevice& d, const AdapterStatusListenerRef& l); + bool addStatusListener(const BTDevice& d, const AdapterStatusListenerRef& l) noexcept; /** * Remove the given listener from the list. @@ -979,7 +977,7 @@ namespace direct_bt { * @see BTDevice::removeStatusListener() * @see addStatusListener() */ - bool removeStatusListener(const AdapterStatusListenerRef& l); + bool removeStatusListener(const AdapterStatusListenerRef& l) noexcept; /** * Remove the given listener from the list. @@ -990,7 +988,7 @@ namespace direct_bt { * @see BTDevice::removeStatusListener() * @see addStatusListener() */ - bool removeStatusListener(const AdapterStatusListener * l); + bool removeStatusListener(const AdapterStatusListener * l) noexcept; /** * Remove all status listener from the list. diff --git a/api/direct_bt/BTDevice.hpp b/api/direct_bt/BTDevice.hpp index bf2f5266..fc109d56 100644 --- a/api/direct_bt/BTDevice.hpp +++ b/api/direct_bt/BTDevice.hpp @@ -384,7 +384,7 @@ namespace direct_bt { * @see BTAdapter::removeAllStatusListener() * @see removeStatusListener() */ - bool addStatusListener(const AdapterStatusListenerRef& l); + bool addStatusListener(const AdapterStatusListenerRef& l) noexcept; /** * Remove the given listener from the list. @@ -398,7 +398,7 @@ namespace direct_bt { * @see BTAdapter::removeAllStatusListener() * @see addStatusListener() */ - bool removeStatusListener(const AdapterStatusListenerRef& l); + bool removeStatusListener(const AdapterStatusListenerRef& l) noexcept; /** * Retrieves the current connection info for this device and returns the ConnectionInfo reference if successful, @@ -1161,7 +1161,12 @@ namespace direct_bt { * @return true if the given listener is not element of the list and has been newly added, otherwise false. * @throws IllegalStateException if the GATTHandler is null, i.e. not connected */ - bool addCharListener(std::shared_ptr<BTGattCharListener> l); + bool addCharListener(const BTGattCharListenerRef& l) noexcept; + + /** + * Please use BTGattChar::addCharListener() for clarity, merely existing here to allow JNI access. + */ + bool addCharListener(const BTGattCharListenerRef& l, const BTGattCharRef& d) noexcept; /** * Remove the given {@link BTGattCharListener} from the listener list. @@ -1171,7 +1176,7 @@ namespace direct_bt { * @param listener A {@link BTGattCharListener} instance * @return true if the given listener is an element of the list and has been removed, otherwise false. */ - bool removeCharListener(std::shared_ptr<BTGattCharListener> l) noexcept; + bool removeCharListener(const BTGattCharListenerRef& l) noexcept; /** * Remove all {@link BTGattCharListener} from the list, which are associated to the given {@link BTGattChar}. @@ -1182,7 +1187,7 @@ namespace direct_bt { * @param associatedCharacteristic the match criteria to remove any BTGattCharListener from the list * @return number of removed listener. */ - int removeAllAssociatedCharListener(std::shared_ptr<BTGattChar> associatedCharacteristic) noexcept; + int removeAllAssociatedCharListener(const BTGattCharRef& associatedCharacteristic) noexcept; int removeAllAssociatedCharListener(const BTGattChar * associatedCharacteristic) noexcept; diff --git a/api/direct_bt/BTGattChar.hpp b/api/direct_bt/BTGattChar.hpp index 8882778a..43727dfb 100644 --- a/api/direct_bt/BTGattChar.hpp +++ b/api/direct_bt/BTGattChar.hpp @@ -57,9 +57,17 @@ namespace direct_bt { class BTGattHandler; // forward + typedef std::shared_ptr<BTGattHandler> BTGattHandlerRef; + + class BTDevice; // forward + typedef std::shared_ptr<BTDevice> BTDeviceRef; + class BTGattService; // forward typedef std::shared_ptr<BTGattService> BTGattServiceRef; + class BTGattCharListener; // forward + typedef std::shared_ptr<BTGattCharListener> BTGattCharListenerRef; + /** * Representing a Gatt Characteristic object from the ::GATTRole::Client perspective. * @@ -96,61 +104,6 @@ namespace direct_bt { }; /** - * {@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 monotonic timestamp at reception, jau::getCurrentMilliseconds() - */ - virtual void notificationReceived(BTGattCharRef charDecl, - const jau::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 monotonic timestamp at reception, see jau::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 jau::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); } - }; - - /** * Characteristic Handle of this instance. * <p> * Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle). @@ -193,9 +146,9 @@ namespace direct_bt { return std::string(JAVA_DBT_PACKAGE "DBTGattChar"); } - std::shared_ptr<BTGattService> getServiceUnchecked() const noexcept { return wbr_service.lock(); } - std::shared_ptr<BTGattHandler> getGattHandlerUnchecked() const noexcept; - std::shared_ptr<BTDevice> getDeviceUnchecked() const noexcept; + BTGattServiceRef getServiceUnchecked() const noexcept { return wbr_service.lock(); } + BTGattHandlerRef getGattHandlerUnchecked() const noexcept; + BTDeviceRef getDeviceUnchecked() const noexcept; bool hasProperties(const PropertyBitVal v) const noexcept { return v == ( properties & v ); } @@ -305,18 +258,15 @@ namespace direct_bt { bool disableIndicationNotification() noexcept; /** - * Add the given BTGattChar::Listener to the listener list if not already present. + * Add the given BTGattCharListener to the listener list if not already present. * * 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. + * will call the respective BTGattCharListener callback method. * * Returns true if the given listener is not element of the list and has been newly added, * otherwise false. * - * Implementation wraps given BTGattChar::Listener into an AssociatedBTGattCharListener - * to restrict the listener to listen only to this BTGattChar instance. - * * Convenience delegation call to BTGattHandler via BTDevice * * @see BTGattChar::disableIndicationNotification() @@ -326,10 +276,10 @@ namespace direct_bt { * @see BTGattChar::removeCharListener() * @see BTGattChar::removeAllAssociatedCharListener() */ - bool addCharListener(std::shared_ptr<Listener> l) noexcept; + bool addCharListener(const BTGattCharListenerRef& l) noexcept; /** - * Add the given BTGattChar::Listener to the listener list if not already present + * Add the given BTGattCharListener 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. * @@ -338,15 +288,12 @@ namespace direct_bt { * Implementation uses enableNotificationOrIndication(bool[]) to enable either. * * Occurring notifications and indications for this characteristic - * will call the respective BTGattChar::Listener callback method. + * will call the respective BTGattCharListener callback method. * * Returns 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. * - * Implementation wraps given BTGattChar::Listener into an AssociatedBTGattCharListener - * to restrict the listener to listen only to this BTGattChar instance. - * * @param enabledState array of size 2, holding the resulting enabled state for notification and indication * using enableNotificationOrIndication(bool[]) * @@ -357,10 +304,10 @@ namespace direct_bt { * @see BTGattChar::removeCharListener() * @see BTGattChar::removeAllAssociatedCharListener() */ - bool addCharListener(std::shared_ptr<Listener> l, bool enabledState[2]) noexcept; + bool addCharListener(const BTGattCharListenerRef& l, bool enabledState[2]) noexcept; /** - * Remove the given associated BTGattChar::Listener from the listener list if present. + * Remove the given associated BTGattCharListener from the listener list if present. * * To disables the notification and/or indication for this characteristic at BLE level * use disableIndicationNotification() when desired. @@ -376,10 +323,10 @@ namespace direct_bt { * @see BTGattChar::removeAllAssociatedCharListener() * @since 2.4.0 */ - bool removeCharListener(std::shared_ptr<Listener> l) noexcept; + bool removeCharListener(const BTGattCharListenerRef& l) noexcept; /** - * Removes all associated BTGattChar::Listener and and {@link BTGattCharListener} from the listener list. + * Removes all associated BTGattCharListener and and {@link BTGattCharListener} from the listener list. * * Also disables the notification and/or indication for this characteristic at BLE level * if `disableIndicationNotification == true`. @@ -475,21 +422,18 @@ namespace direct_bt { * {@link BTGattChar} event listener for notification and indication events. * <p> * A listener instance may be attached to a BTGattChar instance via - * {@link BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener>)} to listen to its events. + * {@link BTGattChar::addCharListener(BTGattCharListenerRef)} to listen to its events. * </p> * <p> * A listener instance may be attached to a BTGattHandler via - * {@link BTGattHandler::addCharListener(std::shared_ptr<BTGattCharListener>)} + * {@link BTGattHandler::addCharListener(BTGattCharListenerRef)} * to listen to all events of the device or the matching filtered events. * </p> * <p> - * 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 { + class BTGattCharListener : public jau::JavaUplink { public: /** * Returns a unique string denominating the type of this instance. @@ -500,22 +444,6 @@ namespace direct_bt { virtual const char * type_name() const noexcept; /** - * Custom filter for all event methods, - * which will not be called if this method returns false. - * <p> - * User may override this method to test whether the methods shall be called - * for the given BTGattChar. - * </p> - * <p> - * Defaults to true; - * </p> - */ - virtual bool match(const BTGattChar & characteristic) noexcept { - (void)characteristic; - return true; - } - - /** * Called from native BLE stack, initiated by a received notification associated * with the given {@link BTGattChar}. * @param charDecl {@link BTGattChar} related to this notification @@ -537,13 +465,20 @@ namespace direct_bt { const jau::TROOctets& charValue, const uint64_t timestamp, const bool confirmationSent) = 0; - virtual ~BTGattCharListener() noexcept {} + ~BTGattCharListener() noexcept override {} /** Return a simple description about this instance. */ - virtual std::string toString() { + std::string toString() const noexcept override { return std::string(type_name())+"["+jau::to_string(this)+"]"; } + std::string get_java_class() const noexcept override { + return java_class(); + } + static std::string java_class() noexcept { + return std::string(JAVA_MAIN_PACKAGE "BTGattCharListener"); + } + /** * Default comparison operator, merely testing for same memory reference. * <p> @@ -558,27 +493,6 @@ namespace direct_bt { }; typedef std::shared_ptr<BTGattCharListener> BTGattCharListenerRef; - class AssociatedBTGattCharListener : public BTGattCharListener { - private: - const BTGattChar * associatedChar; - - public: - /** - * Passing the associated BTGattChar to filter out non matching events. - */ - AssociatedBTGattCharListener(const BTGattChar * characteristicMatch) noexcept - : associatedChar(characteristicMatch) { } - - const char * type_name() const noexcept override; - - bool match(const BTGattChar & characteristic) noexcept override { - if( nullptr == associatedChar ) { - return true; - } - return *associatedChar == characteristic; - } - }; - } // namespace direct_bt #endif /* BT_GATT_CHARACTERISTIC_HPP_ */ diff --git a/api/direct_bt/BTGattCmd.hpp b/api/direct_bt/BTGattCmd.hpp index 80bda1d8..275968df 100644 --- a/api/direct_bt/BTGattCmd.hpp +++ b/api/direct_bt/BTGattCmd.hpp @@ -78,7 +78,7 @@ namespace direct_bt { BTGattCharRef rspCharRef; bool setup_done; - class ResponseCharListener : public BTGattChar::Listener { + class ResponseCharListener : public BTGattCharListener { private: BTGattCmd& source; jau::POctets& rsp_data; diff --git a/api/direct_bt/BTGattHandler.hpp b/api/direct_bt/BTGattHandler.hpp index 549ff5d3..4dc08c9e 100644 --- a/api/direct_bt/BTGattHandler.hpp +++ b/api/direct_bt/BTGattHandler.hpp @@ -438,8 +438,6 @@ namespace direct_bt { typedef jau::cow_darray<NativeGattCharListenerRef> NativeGattCharListenerList_t; typedef jau::darray<NativeGattCharListener::Section> NativeGattCharSections_t; - typedef jau::cow_darray<BTGattCharListenerRef> BTGattCharListenerList_t; - private: /** BTGattHandler's device weak back-reference */ std::weak_ptr<BTDevice> wbr_device; @@ -462,7 +460,26 @@ namespace direct_bt { /** send immediate confirmation of indication events from device, defaults to true. */ jau::relaxed_atomic_bool sendIndicationConfirmation = true; - BTGattCharListenerList_t btGattCharListenerList; + + struct GattCharListenerPair { + /** The actual listener */ + BTGattCharListenerRef listener; + /** The optional weak device reference. Weak, b/c it shall not block destruction */ + std::weak_ptr<BTGattChar> wbr_characteristic; + + bool match(const BTGattChar& characteristic) const noexcept { + BTGattCharRef sda = wbr_characteristic.lock(); + if( nullptr != sda ) { + return *sda == characteristic; + } else { + return true; + } + } + }; + typedef jau::cow_darray<GattCharListenerPair> gattCharListenerList_t; + static gattCharListenerList_t::equal_comparator gattCharListenerRefEqComparator; + gattCharListenerList_t gattCharListenerList; + NativeGattCharListenerList_t nativeGattCharListenerList; /** Pass through user Gatt-Server database, non-nullptr if ::GATTRole::Server */ @@ -811,6 +828,11 @@ namespace direct_bt { bool addCharListener(const BTGattCharListenerRef& l) noexcept; /** + * Please use BTGattChar::addCharListener() for clarity, merely existing here to allow JNI access. + */ + bool addCharListener(const BTGattCharListenerRef& l, const BTGattCharRef& d) noexcept; + + /** * Remove the given listener from the list. * <p> * Returns true if the given listener is an element of the list and has been removed, @@ -829,11 +851,9 @@ namespace direct_bt { bool removeCharListener(const BTGattCharListener * l) noexcept; /** - * Remove all {@link BTGattCharListener} from the list, which are associated to the given {@link BTGattChar}. - * <p> - * Implementation tests all listener's BTGattCharListener::match(const BTGattChar & characteristic) - * to match with the given associated characteristic. - * </p> + * Remove all {@link BTGattCharListener} from the list, which are associated to the given {@link BTGattChar} + * when added via BTGattChar::addCharListener(). + * * @param associatedCharacteristic the match criteria to remove any BTGattCharListener from the list * @return number of removed listener. */ @@ -870,17 +890,7 @@ namespace direct_bt { /** * Return event listener count. */ - jau::nsize_t getCharListenerCount() const noexcept { return btGattCharListenerList.size() + nativeGattCharListenerList.size(); } - - /** - * Return a thread safe snapshot of the BTGattCharListener array - */ - BTGattCharListenerList_t::storage_ref_t getBTGattCharListener() const noexcept { return btGattCharListenerList.snapshot(); } - - /** - * Return a thread safe snapshot of the NativeGattCharListener array - */ - NativeGattCharListenerList_t::storage_ref_t getNativeGattCharListener() const noexcept { return nativeGattCharListenerList.snapshot(); } + jau::nsize_t getCharListenerCount() const noexcept { return gattCharListenerList.size() + nativeGattCharListenerList.size(); } /** * Print a list of all BTGattCharListener and NativeGattCharListener. @@ -1011,6 +1021,7 @@ namespace direct_bt { std::string toString() const noexcept; }; + typedef std::shared_ptr<BTGattHandler> BTGattHandlerRef; } // namespace direct_bt diff --git a/api/direct_bt/BTGattService.hpp b/api/direct_bt/BTGattService.hpp index 4feef69b..f4e6006b 100644 --- a/api/direct_bt/BTGattService.hpp +++ b/api/direct_bt/BTGattService.hpp @@ -57,7 +57,10 @@ namespace direct_bt { class BTGattHandler; // forward + typedef std::shared_ptr<BTGattHandler> BTGattHandlerRef; + class BTDevice; // forward + typedef std::shared_ptr<BTDevice> BTDeviceRef; /** * Representing a Gatt Service object from the ::GATTRole::Client perspective. @@ -113,11 +116,11 @@ namespace direct_bt { return std::string(JAVA_DBT_PACKAGE "DBTGattService"); } - std::shared_ptr<BTGattHandler> getGattHandlerUnchecked() const noexcept { return wbr_handler.lock(); } - std::shared_ptr<BTGattHandler> getGattHandlerChecked() const; + BTGattHandlerRef getGattHandlerUnchecked() const noexcept { return wbr_handler.lock(); } + BTGattHandlerRef getGattHandlerChecked() const; - std::shared_ptr<BTDevice> getDeviceUnchecked() const noexcept; - std::shared_ptr<BTDevice> getDeviceChecked() const; + BTDeviceRef getDeviceUnchecked() const noexcept; + BTDeviceRef getDeviceChecked() const; /** * Find a BTGattChar by its char_uuid. @@ -125,10 +128,19 @@ namespace direct_bt { * @parameter char_uuid the jau::uuid_t of the desired BTGattChar, within this BTGattService. * @return The matching characteristic or null if not found */ - std::shared_ptr<BTGattChar> findGattChar(const jau::uuid_t& char_uuid) noexcept; + BTGattCharRef findGattChar(const jau::uuid_t& char_uuid) noexcept; + + /** + * Find a BTGattChar by itself, i.e. mapping BTGattChar instance to BTGattCharRef. + * + * @parameter characteristic the desired BTGattChar, within this BTGattService. + * @return The matching characteristic or null if not found + */ + BTGattCharRef findGattChar(const BTGattChar& characteristic) noexcept; std::string toString() const noexcept override; }; + typedef std::shared_ptr<BTGattService> BTGattServiceRef; inline bool operator==(const BTGattService& lhs, const BTGattService& rhs) noexcept { return lhs.handle == rhs.handle && lhs.end_handle == rhs.end_handle; /** unique attribute handles */ } diff --git a/examples/dbt_scanner10.cpp b/examples/dbt_scanner10.cpp index b6db78b3..c3e28dad 100644 --- a/examples/dbt_scanner10.cpp +++ b/examples/dbt_scanner10.cpp @@ -324,7 +324,7 @@ class MyAdapterStatusListener : public AdapterStatusListener { static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT); -class MyGATTEventListener : public BTGattChar::Listener { +class MyGATTEventListener : public BTGattCharListener { private: int i, j; @@ -559,8 +559,7 @@ static void processReadyDevice(BTDeviceRef device) { bool cccdEnableResult[2]; if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) { // ClientCharConfigDescriptor (CCD) is available - std::shared_ptr<BTGattChar::Listener> cl = std::make_shared<MyGATTEventListener>(i, j); - bool clAdded = serviceChar->addCharListener( cl ); + bool clAdded = serviceChar->addCharListener( std::make_shared<MyGATTEventListener>(i, j) ); { fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], clAdded); diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java index 2845f9e0..98be7aea 100644 --- a/examples/java/DBTScanner10.java +++ b/examples/java/DBTScanner10.java @@ -39,6 +39,7 @@ import org.direct_bt.BTDeviceRegistry; import org.direct_bt.BTException; import org.direct_bt.BTFactory; import org.direct_bt.BTGattChar; +import org.direct_bt.BTGattCharListener; import org.direct_bt.BTGattCmd; import org.direct_bt.BTGattDesc; import org.direct_bt.BTGattService; @@ -277,7 +278,7 @@ public class DBTScanner10 { } }; - class MyGATTEventListener implements BTGattChar.Listener { + class MyGATTEventListener extends BTGattCharListener { private final int i, j; public MyGATTEventListener(final int i_, final int j_) { i=i_; j=j_; } @@ -518,7 +519,7 @@ public class DBTScanner10 { final boolean cccdEnableResult[] = { false, false }; if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) { // ClientCharConfigDescriptor (CCD) is available - final boolean clAdded = null != serviceChar.addCharListener( new MyGATTEventListener(i, j) ); + final boolean clAdded = serviceChar.addCharListener( new MyGATTEventListener(i, j) ); { BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n", i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded); diff --git a/java/jau/direct_bt/DBTDevice.java b/java/jau/direct_bt/DBTDevice.java index eb596a6a..1184b808 100644 --- a/java/jau/direct_bt/DBTDevice.java +++ b/java/jau/direct_bt/DBTDevice.java @@ -669,9 +669,14 @@ public class DBTDevice extends DBTObject implements BTDevice @Override public boolean addCharListener(final BTGattCharListener listener) { - return addCharListener(listener, (DBTGattChar)listener.getAssociatedChar()); + return addCharListenerImpl(listener, null); } - private native boolean addCharListener(final BTGattCharListener listener, final DBTGattChar associatedCharacteristic); + + @Override + public boolean addCharListener(final BTGattCharListener listener, final BTGattChar associatedCharacteristic) { + return addCharListenerImpl(listener, associatedCharacteristic); + } + private native boolean addCharListenerImpl(final BTGattCharListener listener, final BTGattChar associatedCharacteristic); @Override public native boolean removeCharListener(final BTGattCharListener l); diff --git a/java/jau/direct_bt/DBTGattChar.java b/java/jau/direct_bt/DBTGattChar.java index 334b1b45..afa9c7d4 100644 --- a/java/jau/direct_bt/DBTGattChar.java +++ b/java/jau/direct_bt/DBTGattChar.java @@ -97,7 +97,7 @@ public class DBTGattChar extends DBTObject implements BTGattChar final boolean hasIndicate = properties.isSet(GattCharPropertySet.Type.Indicate); if( hasNotify || hasIndicate ) { - final Listener characteristicListener = new Listener() { + final BTGattCharListener characteristicListener = new BTGattCharListener() { @Override public void notificationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp) { System.err.println("GATTCharacteristicListener.notificationReceived: "+charDecl+ @@ -243,38 +243,15 @@ public class DBTGattChar extends DBTObject implements BTGattChar return configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]); } - 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 BTGattCharListener addCharListener(final Listener listener) { - final BTGattCharListener wl = new DelegatedBTGattCharListener(this, listener); - return getService().getDevice().addCharListener( wl ) ? wl : null; + public final boolean addCharListener(final BTGattCharListener listener) { + return getService().getDevice().addCharListener( listener, this ); } @Override - public final BTGattCharListener addCharListener(final Listener listener, final boolean enabledState[/*2*/]) { + public final boolean addCharListener(final BTGattCharListener listener, final boolean enabledState[/*2*/]) { if( !enableNotificationOrIndication(enabledState) ) { - return null; + return false; } return addCharListener( listener ); } diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx index 7ea95178..7c509f90 100644 --- a/java/jni/direct_bt/DBTAdapter.cxx +++ b/java/jni/direct_bt/DBTAdapter.cxx @@ -509,7 +509,7 @@ jboolean Java_jau_direct_1bt_DBTAdapter_addStatusListenerImpl(JNIEnv *env, jobje if( addRes ) { return JNI_TRUE; } - ERR_PRINT("JNIAdapterStatusListener::addStatusListener: FAILED: %s", asl->toString().c_str()); + ERR_PRINT("BTAdapter::addStatusListener: FAILED: %s", asl->toString().c_str()); } catch(...) { rethrow_and_raise_java_exception(env); } diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx index 86d940f9..dcd73336 100644 --- a/java/jni/direct_bt/DBTDevice.cxx +++ b/java/jni/direct_bt/DBTDevice.cxx @@ -24,6 +24,7 @@ */ #include "jau_direct_bt_DBTDevice.h" +#include "org_direct_bt_BTGattCharListener.h" // #define VERBOSE_ON 1 #include <jau/debug.hpp> @@ -46,8 +47,6 @@ class JNIGattCharListener : public BTGattCharListener { private: /** public abstract class BTGattCharListener { - long nativeInstance; - public void notificationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp) { } @@ -59,17 +58,14 @@ class JNIGattCharListener : public BTGattCharListener { }; */ - std::shared_ptr<BTGattChar> associatedCharacteristicRef; JNIGlobalRef listenerObj; // keep listener instance alive - JNIGlobalRef associatedCharacteristicObj; // keeps associated characteristic alive, if not null jmethodID mNotificationReceived = nullptr; jmethodID mIndicationReceived = nullptr; public: - JNIGattCharListener(JNIEnv *env, const std::shared_ptr<BTDevice>& device, jobject listener, const std::shared_ptr<BTGattChar>& associatedCharacteristicRef_) - : associatedCharacteristicRef(associatedCharacteristicRef_), - listenerObj(listener) + JNIGattCharListener(JNIEnv *env, jobject listener) + : listenerObj(listener) { jclass listenerClazz = search_class(env, listenerObj.getObject()); java_exception_check_and_throw(env, E_FILE_LINE); @@ -77,28 +73,11 @@ class JNIGattCharListener : public BTGattCharListener { throw InternalError("BTGattCharListener not found", E_FILE_LINE); } - if( nullptr != associatedCharacteristicRef_ ) { - JavaGlobalObj::check(associatedCharacteristicRef_->getJavaObject(), E_FILE_LINE); - associatedCharacteristicObj = JavaGlobalObj::GetJavaObject(associatedCharacteristicRef_->getJavaObject()); // new global ref - } - mNotificationReceived = search_method(env, listenerClazz, "notificationReceived", _notificationReceivedMethodArgs.c_str(), false); java_exception_check_and_throw(env, E_FILE_LINE); - if( nullptr == mNotificationReceived ) { - throw InternalError("BTGattCharListener has no notificationReceived"+_notificationReceivedMethodArgs+" method, for "+device->toString(), E_FILE_LINE); - } + mIndicationReceived = search_method(env, listenerClazz, "indicationReceived", _indicationReceivedMethodArgs.c_str(), false); java_exception_check_and_throw(env, E_FILE_LINE); - if( nullptr == mNotificationReceived ) { - throw InternalError("BTGattCharListener has no indicationReceived"+_indicationReceivedMethodArgs+" method, for "+device->toString(), E_FILE_LINE); - } - } - - bool match(const BTGattChar & characteristic) noexcept override { - if( nullptr == associatedCharacteristicRef ) { - return true; - } - return characteristic == *associatedCharacteristicRef; } void notificationReceived(BTGattCharRef charDecl, @@ -143,6 +122,41 @@ class JNIGattCharListener : public BTGattCharListener { } }; +/* + * Class: org_direct_bt_BTGattCharListener + * Method: ctorImpl + * Signature: ()J + */ +jlong Java_org_direct_1bt_BTGattCharListener_ctorImpl(JNIEnv *env, jobject obj) { + try { + // new instance + jau::shared_ptr_ref<JNIGattCharListener> ref( new JNIGattCharListener(env, obj) ); + + return ref.release_to_jlong(); + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return (jlong) (intptr_t) nullptr; +} + + +/* + * Class: org_direct_bt_BTGattCharListener + * Method: deleteImpl + * Signature: (J)V + */ +void Java_org_direct_1bt_BTGattCharListener_deleteImpl(JNIEnv *env, jobject obj, jlong nativeInstance) { + (void)obj; + try { + jau::shared_ptr_ref<JNIGattCharListener> sref(nativeInstance, false /* throw_on_nullptr */); // hold copy until done + if( nullptr != sref.pointer() ) { + std::shared_ptr<JNIGattCharListener>* sref_ptr = jau::castInstance<JNIGattCharListener>(nativeInstance); + delete sref_ptr; + } + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} void Java_jau_direct_1bt_DBTDevice_deleteImpl(JNIEnv *env, jobject obj, jlong nativeInstance) { @@ -201,35 +215,42 @@ jstring Java_jau_direct_1bt_DBTDevice_toStringImpl(JNIEnv *env, jobject obj) { return nullptr; } -jboolean Java_jau_direct_1bt_DBTDevice_addCharListener(JNIEnv *env, jobject obj, jobject listener, jobject jAssociatedCharacteristic) { +/* + * Class: jau_direct_bt_DBTDevice + * Method: addCharListenerImpl + * Signature: (Lorg/direct_bt/BTGattCharListener;Lorg/direct_bt/BTGattChar;)Z + */ +jboolean Java_jau_direct_1bt_DBTDevice_addCharListenerImpl(JNIEnv *env, jobject obj, jobject jlistener, jobject jAssociatedCharacteristic) { try { shared_ptr_ref<BTDevice> device(env, obj); // hold until done jau::JavaAnonRef device_java = device->getJavaObject(); // hold until done! JavaGlobalObj::check(device_java, E_FILE_LINE); - if( nullptr == listener ) { - throw IllegalArgumentException("BTGattCharListener argument is null", E_FILE_LINE); - } - { - std::shared_ptr<JNIGattCharListener>* pre_orig = jau::getInstance<JNIGattCharListener>(env, listener, false /* throw_on_nullptr */); - if( !( nullptr == pre_orig || nullptr == *pre_orig ) ) { - throw IllegalStateException("BTGattCharListener's nativeInstance not null, already in use", E_FILE_LINE); - return false; - } + jau::shared_ptr_ref<JNIGattCharListener> gcl(env, jlistener); // hold until done + jau::JavaAnonRef gcl_java = gcl->getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(gcl_java, E_FILE_LINE); + + jau::shared_ptr_ref<BTGattChar> charMatchRef(env, jAssociatedCharacteristic, false /* throw_on_nullptr */); + if( !charMatchRef.is_null() ) { + jau::JavaGlobalObj::check(charMatchRef->getJavaObject(), E_FILE_LINE); } + std::shared_ptr<BTGattHandler> gatt = device->getGattHandler(); if( nullptr == gatt ) { - throw IllegalStateException("BTGattChar's device GATTHandle not connected: "+ device->toString(), E_FILE_LINE); + ERR_PRINT("BTGattChar's device GATTHandle not connected: %s", device->toString().c_str()); + return false; } - jau::shared_ptr_ref<BTGattChar> associatedCharacteristicRef(env, jAssociatedCharacteristic, false /* throw_on_nullptr */); - - shared_ptr_ref<JNIGattCharListener> l( - new JNIGattCharListener(env, device.shared_ptr(), listener, associatedCharacteristicRef.shared_ptr()) ); - if( gatt->addCharListener( l.shared_ptr() ) ) { - l.release_into_object(env, listener); + bool addRes; + if( !charMatchRef.is_null() ) { + addRes = gatt->addCharListener( gcl.shared_ptr(), charMatchRef.shared_ptr() ); + } else { + addRes = gatt->addCharListener( gcl.shared_ptr() ); + } + if( addRes ) { return JNI_TRUE; } + ERR_PRINT("BTDevice::addCharListener: FAILED: %s", gcl->toString().c_str()); } catch(...) { rethrow_and_raise_java_exception(env); } @@ -246,15 +267,9 @@ jboolean Java_jau_direct_1bt_DBTDevice_removeCharListener(JNIEnv *env, jobject o jau::JavaAnonRef device_java = device->getJavaObject(); // hold until done! JavaGlobalObj::check(device_java, E_FILE_LINE); - if( nullptr == jlistener ) { - throw IllegalArgumentException("BTGattCharListener argument is null", E_FILE_LINE); - } - shared_ptr_ref<JNIGattCharListener> pre( env, jlistener, false /* throw_on_nullptr */ ); // hold until done - if( pre.is_null() ) { - WARN_PRINT("BTGattCharListener's nativeInstance is null, not in use"); - return false; - } - jau::clearInstance<JNIGattCharListener>(env, jlistener); + jau::shared_ptr_ref<JNIGattCharListener> gcl(env, jlistener); // hold until done + jau::JavaAnonRef gcl_java = gcl->getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(gcl_java, E_FILE_LINE); std::shared_ptr<BTGattHandler> gatt = device->getGattHandler(); if( nullptr == gatt ) { @@ -263,8 +278,8 @@ jboolean Java_jau_direct_1bt_DBTDevice_removeCharListener(JNIEnv *env, jobject o return false; } - if( ! gatt->removeCharListener(pre.shared_ptr()) ) { - WARN_PRINT("Failed to remove BTGattCharListener with nativeInstance: %p at %s", pre.shared_ptr().get(), device->toString().c_str()); + if( ! gatt->removeCharListener(gcl.shared_ptr()) ) { + WARN_PRINT("Failed to remove BTGattCharListener with nativeInstance: %p at %s", gcl.shared_ptr().get(), device->toString().c_str()); return false; } return true; diff --git a/java/org/direct_bt/AdapterStatusListener.java b/java/org/direct_bt/AdapterStatusListener.java index 5f5e348f..6966c640 100644 --- a/java/org/direct_bt/AdapterStatusListener.java +++ b/java/org/direct_bt/AdapterStatusListener.java @@ -39,6 +39,9 @@ import jau.direct_bt.DBTNativeDownlink; * A listener instance may be attached to a {@link BTAdapter} via * {@link BTAdapter#addStatusListener(AdapterStatusListener, BTDevice)}. * </p> + * <p> + * The listener receiver maintains a unique set of listener instances without duplicates. + * </p> * @since 2.0.0 */ public abstract class AdapterStatusListener extends DBTNativeDownlink { diff --git a/java/org/direct_bt/BTDevice.java b/java/org/direct_bt/BTDevice.java index 4894b04a..efa640f0 100644 --- a/java/org/direct_bt/BTDevice.java +++ b/java/org/direct_bt/BTDevice.java @@ -917,14 +917,16 @@ public interface BTDevice extends BTObject * </p> * @param listener A {@link BTGattCharListener} instance, listening to all {@link BTGattChar} events of this device * @return true if the given listener is not element of the list and has been newly added, otherwise false. - * @throws IllegalStateException if the {@link BTDevice}'s BTGattHandler is null, i.e. not connected - * @throws IllegalStateException if the given {@link BTGattCharListener} is already in use, i.e. added. * @see BTGattChar#configNotificationIndication(boolean, boolean, boolean[]) * @see BTGattChar#enableNotificationOrIndication(boolean[]) * @since 2.0.0 */ - boolean addCharListener(final BTGattCharListener listener) - throws IllegalStateException; + boolean addCharListener(final BTGattCharListener listener); + + /** + * Please use {@link BTGattChar#addCharListener(BTGattCharListener)} for clarity. + */ + boolean addCharListener(final BTGattCharListener listener, final BTGattChar associatedCharacteristic); /** * Remove the given {@link BTGattCharListener} from the listener list. diff --git a/java/org/direct_bt/BTGattChar.java b/java/org/direct_bt/BTGattChar.java index 62cd8653..bb185da7 100644 --- a/java/org/direct_bt/BTGattChar.java +++ b/java/org/direct_bt/BTGattChar.java @@ -45,45 +45,6 @@ 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 monotonic timestamp at reception, see {@link BTUtils#currentTimeMillis()} - */ - 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 monotonic timestamp at reception, see {@link BTUtils#currentTimeMillis()} - * @param confirmationSent if true, the native stack has sent the confirmation, otherwise user is required to do so. - */ - void indicationReceived(final BTGattChar charDecl, - final byte[] value, final long timestamp, - final boolean confirmationSent); - }; - - /** * Find a {@link BTGattDesc} by its desc_uuid. * * @parameter desc_uuid the UUID of the desired {@link BTGattDesc} @@ -133,8 +94,9 @@ public interface BTGattChar extends BTObject * @see #disableIndicationNotification() * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @see #addCharListener(Listener) - * @see #removeCharListener(Listener) + * @see #addCharListener(BTGattCharListener) + * #see #addCharListener(BTGattCharListener, boolean[]) + * @see #removeCharListener(BTGattCharListener) * @see #removeAllAssociatedCharListener(boolean) * @since 2.0.0 */ @@ -163,8 +125,9 @@ public interface BTGattChar extends BTObject * @see #disableIndicationNotification() * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @see #addCharListener(Listener) - * @see #removeCharListener(Listener) + * @see #addCharListener(BTGattCharListener) + * #see #addCharListener(BTGattCharListener, boolean[]) + * @see #removeCharListener(BTGattCharListener) * @see #removeAllAssociatedCharListener(boolean) * @since 2.0.0 */ @@ -188,43 +151,41 @@ public interface BTGattChar extends BTObject * @see #disableIndicationNotification() * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @see #addCharListener(Listener) - * @see #removeCharListener(Listener) + * @see #addCharListener(BTGattCharListener) + * #see #addCharListener(BTGattCharListener, boolean[]) + * @see #removeCharListener(BTGattCharListener) * @see #removeAllAssociatedCharListener(boolean) * @since 2.4.0 */ boolean disableIndicationNotification() throws IllegalStateException; /** - * Add the given {@link BTGattChar.Listener} to the listener list if not already present. + * Add the given BTGattCharListener to the listener list if not already present. * * Occurring notifications and indications for this characteristic, * if enabled via {@link #configNotificationIndication(boolean, boolean, boolean[])} * or {@link #enableNotificationOrIndication(boolean[])}, - * will call the respective {@link BTGattChar.Listener} callback method. - * - * Implementation wraps given {@link BTGattChar.Listener} into a {@link BTGattCharListener} - * to restrict the listener to listen only to this BTGattChar instance. + * will call the respective BTGattCharListener callback method. * - * {@link #removeCharListener(BTGattCharListener)} must be utilized with the returned {@link BTGattCharListener}. + * Returns true if the given listener is not element of the list and has been newly added, + * otherwise false. * - * @param listener A {@link BTGattChar.Listener} instance, listening to this {@link BTGattChar}'s events - * @return if successful, {@link BTGattCharListener} instance wrapping the given {@link BTGattChar.Listener} is returned, otherwise null. + * @param listener A {@link {@link BTGattCharListener}} instance, listening to this {@link BTGattChar}'s events + * @return if successful, true is being returned, otherwise false. * @throws IllegalStateException if the DBTDevice's GATTHandler is null, i.e. not connected - * @throws IllegalStateException if the given {@link BTGattChar.Listener} is already in use, i.e. added. * @see #disableIndicationNotification() * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @see #addCharListener(Listener) - * @see #removeCharListener(Listener) + * @see #addCharListener(BTGattCharListener) + * #see #addCharListener(BTGattCharListener, boolean[]) + * @see #removeCharListener(BTGattCharListener) * @see #removeAllAssociatedCharListener(boolean) * @since 2.4.0 */ - BTGattCharListener addCharListener(final Listener listener) - throws IllegalStateException; + boolean addCharListener(final BTGattCharListener listener) throws IllegalStateException; /** - * Add the given {@link BTGattChar.Listener} to the listener list if not already present + * Add the given BTGattCharListener 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. * @@ -233,30 +194,28 @@ public interface BTGattChar extends BTObject * Implementation uses {@link #enableNotificationOrIndication(boolean[])} to enable either. * * Occurring notifications and indications for this characteristic - * will call the respective {@link BTGattChar.Listener} callback method. + * will call the respective BTGattCharListener callback method. * - * Implementation wraps given {@link BTGattChar.Listener} into a {@link BTGattCharListener} - * to restrict the listener to listen only to this BTGattChar instance. - * - * {@link #removeCharListener(BTGattCharListener)} must be utilized with the returned {@link BTGattCharListener}. + * Returns 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. * * @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 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, - * {@link BTGattCharListener} instance wrapping the given {@link BTGattChar.Listener} is returned, otherwise null. + * @return if successful, true is being returned, otherwise false. * @throws IllegalStateException if the {@link BTDevice}'s GATTHandler is null, i.e. not connected * @throws IllegalStateException if the given {@link BTGattChar.Listener} is already in use, i.e. added. * @see #disableIndicationNotification() * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @see #addCharListener(Listener) - * @see #removeCharListener(Listener) + * @see #addCharListener(BTGattCharListener) + * #see #addCharListener(BTGattCharListener, boolean[]) + * @see #removeCharListener(BTGattCharListener) * @see #removeAllAssociatedCharListener(boolean) * @since 2.4.0 */ - BTGattCharListener addCharListener(final Listener listener, final boolean enabledState[/*2*/]) + boolean addCharListener(final BTGattCharListener listener, final boolean enabledState[/*2*/]) throws IllegalStateException; /** @@ -268,13 +227,12 @@ public interface BTGattChar extends BTObject * @param listener returned {@link BTGattCharListener} from {@link #addCharListener(Listener)} ... * @return true if successful, otherwise false. * - * @throws IllegalStateException if the {@link BTDevice's}'s {@link BTGattHandler} is null, i.e. not connected - * * @see #disableIndicationNotification() * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @see #addCharListener(Listener) - * @see #removeCharListener(Listener) + * @see #addCharListener(BTGattCharListener) + * #see #addCharListener(BTGattCharListener, boolean[]) + * @see #removeCharListener(BTGattCharListener) * @see #removeAllAssociatedCharListener(boolean) * @since 2.4.0 */ @@ -296,8 +254,9 @@ public interface BTGattChar extends BTObject * @see #disableIndicationNotification() * @see #enableNotificationOrIndication(boolean[]) * @see #configNotificationIndication(boolean, boolean, boolean[]) - * @see #addCharListener(Listener) - * @see #removeCharListener(Listener) + * @see #addCharListener(BTGattCharListener) + * #see #addCharListener(BTGattCharListener, boolean[]) + * @see #removeCharListener(BTGattCharListener) * @see #removeAllAssociatedCharListener(boolean) * @since 2.0.0 */ diff --git a/java/org/direct_bt/BTGattCharListener.java b/java/org/direct_bt/BTGattCharListener.java index 55dfa6a5..ffd0610a 100644 --- a/java/org/direct_bt/BTGattCharListener.java +++ b/java/org/direct_bt/BTGattCharListener.java @@ -25,7 +25,7 @@ package org.direct_bt; -import java.lang.ref.WeakReference; +import jau.direct_bt.DBTNativeDownlink; /** * {@link BTGattChar} event listener for notification and indication events. @@ -39,49 +39,18 @@ 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 manager once at a time, - * i.e. you cannot attach the same instance more than once to a {@link BTDevice} - * or {@link BTGattChar}. - * <br> - * To attach multiple listener, one instance per attachment must be created. - * <br> - * This restriction is due to implementation semantics of strictly associating - * one Java {@link BTGattCharListener} instance to one C++ {@code BTGattCharListener} instance. - * 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 manager maintains a unique set of listener instances without duplicates, - * this restriction is more esoteric. + * The listener manager maintains a unique set of listener instances without duplicates. * </p> */ -public abstract class BTGattCharListener { - @SuppressWarnings("unused") - private long nativeInstance; - private final WeakReference<BTGattChar> associatedChar; - - /** - * Returns the weakly associated {@link BTGattChar} to this listener instance. - * <p> - * Returns {@code null} if no association has been made - * or if the associated {@link BTGattChar} has been garbage collected. - * </p> - */ - public final BTGattChar getAssociatedChar() { - return null != associatedChar ? associatedChar.get() : null; +public abstract class BTGattCharListener extends DBTNativeDownlink { + public BTGattCharListener() { + super(); // pending native ctor + initDownlink(ctorImpl()); } + private native long ctorImpl(); - /** - * @param associatedCharacteristic weakly associates this listener instance to one {@link BTGattChar}, - * may be {@code null} for no association. - * @see #getAssociatedChar() - */ - public BTGattCharListener(final BTGattChar associatedCharacteristic) { - if( null != associatedCharacteristic ) { - this.associatedChar = new WeakReference<BTGattChar>(associatedCharacteristic); - } else { - this.associatedChar = null; - } - } + @Override + protected native void deleteImpl(long nativeInstance); /** * Called from native BLE stack, initiated by a received notification associated @@ -109,8 +78,6 @@ public abstract class BTGattCharListener { @Override public String toString() { - final BTGattChar c = getAssociatedChar(); - final String cs = null != c ? c.toString() : "null"; - return "BTGattCharListener[associated "+cs+"]"; + return "BTGattCharListener[]"; } }; diff --git a/java/org/direct_bt/BTGattCmd.java b/java/org/direct_bt/BTGattCmd.java index d6c96296..1576756b 100644 --- a/java/org/direct_bt/BTGattCmd.java +++ b/java/org/direct_bt/BTGattCmd.java @@ -62,10 +62,11 @@ public class BTGattCmd implements AutoCloseable private BTGattChar rspCharRef; private boolean setup_done; - private static class ResponseCharListener implements BTGattChar.Listener { + private static class ResponseCharListener extends BTGattCharListener { private final BTGattCmd source; public ResponseCharListener(final BTGattCmd source_) { + super(); source = source_; } @@ -97,7 +98,6 @@ public class BTGattCmd implements AutoCloseable } } private final ResponseCharListener rspCharListener; - private BTGattCharListener addedRspCharListener; private boolean verbose; private boolean isConnected() { return dev.getConnected(); } @@ -143,8 +143,7 @@ public class BTGattCmd implements AutoCloseable } try { final boolean cccdEnableResult[] = { false, false }; - addedRspCharListener = rspCharRef.addCharListener( rspCharListener, cccdEnableResult ); - if( null != addedRspCharListener ) { + if( rspCharRef.addCharListener( rspCharListener, cccdEnableResult ) ) { return HCIStatusCode.SUCCESS; } else { if( verbose ) { @@ -158,7 +157,6 @@ public class BTGattCmd implements AutoCloseable BTUtils.fprintf_td(System.err, "Exception caught for %s: %s\n", e.toString(), toString()); cmdCharRef = null; rspCharRef = null; - addedRspCharListener = null; return HCIStatusCode.TIMEOUT; } } else { @@ -186,10 +184,8 @@ public class BTGattCmd implements AutoCloseable public synchronized HCIStatusCode close0() { final boolean wasResolved = isResolvedEq(); final BTGattChar rspCharRefCopy = rspCharRef; - final BTGattCharListener addedRspCharListenerCopy = addedRspCharListener; cmdCharRef = null; rspCharRef = null; - addedRspCharListener = null; if( !setup_done ) { return HCIStatusCode.SUCCESS; } @@ -200,9 +196,9 @@ public class BTGattCmd implements AutoCloseable if( !isConnected() ) { return HCIStatusCode.DISCONNECTED; } - if( null != addedRspCharListenerCopy && null != rspCharRefCopy) { + if( null != rspCharRefCopy) { try { - final boolean res1 = rspCharRefCopy.removeCharListener(addedRspCharListenerCopy); + final boolean res1 = rspCharRefCopy.removeCharListener(rspCharListener); final boolean res2 = rspCharRefCopy.disableIndicationNotification(); if( res1 && res2 ) { return HCIStatusCode.SUCCESS; @@ -240,7 +236,6 @@ public class BTGattCmd implements AutoCloseable rspCharRef = null; setup_done = false; rspCharListener = new ResponseCharListener(this); - addedRspCharListener = null; verbose = DEBUG; } @@ -264,7 +259,6 @@ public class BTGattCmd implements AutoCloseable rspCharRef = null; setup_done = false; rspCharListener = null; - addedRspCharListener = null; verbose = DEBUG; } diff --git a/java/org/direct_bt/BTGattService.java b/java/org/direct_bt/BTGattService.java index 62cd04ec..592ca9fc 100644 --- a/java/org/direct_bt/BTGattService.java +++ b/java/org/direct_bt/BTGattService.java @@ -88,9 +88,6 @@ public interface BTGattService extends BTObject if( null == listener ) { throw new IllegalArgumentException("listener argument null"); } - if( null != listener.getAssociatedChar() ) { - throw new IllegalArgumentException("listener's associated characteristic is not null"); - } final boolean res = device.addCharListener(listener); for(final Iterator<BTGattService> is = services.iterator(); is.hasNext(); ) { final BTGattService s = is.next(); diff --git a/scripts/build.sh b/scripts/build.sh index fdd6e238..d948e25c 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -40,11 +40,11 @@ buildit() { cd build-$archabi # CLANG_ARGS="-DCMAKE_C_COMPILER=/usr/bin/clang -DCMAKE_CXX_COMPILER=/usr/bin/clang++" - cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON .. + # cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON .. # cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON -DDEBUG=ON .. # cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DUSE_LIBUNWIND=OFF -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON .. - # cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DUSE_LIBUNWIND=OFF -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON -DDEBUG=ON .. + cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DUSE_LIBUNWIND=OFF -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON -DDEBUG=ON .. # cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON -DUSE_STRIP=OFF .. # cmake $CLANG_ARGS -DCMAKE_INSTALL_PREFIX=$rootdir/dist-$archabi -DBUILDJAVA=ON -DBUILDEXAMPLES=ON -DBUILD_TRIAL=ON -DUSE_STRIP=ON -DJAVAC_DEBUG_ARGS="none" .. diff --git a/src/direct_bt/BTAdapter.cpp b/src/direct_bt/BTAdapter.cpp index 0603b2ed..9abdcbe3 100644 --- a/src/direct_bt/BTAdapter.cpp +++ b/src/direct_bt/BTAdapter.cpp @@ -914,15 +914,16 @@ bool BTAdapter::removeDeviceFromWhitelist(const BDAddressAndType & addressAndTyp return mgmt.removeDeviceFromWhitelist(dev_id, addressAndType); } -static jau::cow_darray<impl::StatusListenerPair>::equal_comparator _adapterStatusListenerRefEqComparator = - [](const impl::StatusListenerPair &a, const impl::StatusListenerPair &b) -> bool { return *a.listener == *b.listener; }; +BTAdapter::statusListenerList_t::equal_comparator BTAdapter::adapterStatusListenerRefEqComparator = + [](const StatusListenerPair &a, const StatusListenerPair &b) -> bool { return *a.listener == *b.listener; }; -bool BTAdapter::addStatusListener(const AdapterStatusListenerRef& l) { +bool BTAdapter::addStatusListener(const AdapterStatusListenerRef& l) noexcept { if( nullptr == l ) { - throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE); + ERR_PRINT("AdapterStatusListener ref is null"); + return false; } - const bool added = statusListenerList.push_back_unique(impl::StatusListenerPair{l, std::weak_ptr<BTDevice>{} }, - _adapterStatusListenerRefEqComparator); + const bool added = statusListenerList.push_back_unique(StatusListenerPair{l, std::weak_ptr<BTDevice>{} }, + adapterStatusListenerRefEqComparator); if( added ) { sendAdapterSettingsInitial(*l, jau::getCurrentMilliseconds()); } @@ -933,15 +934,17 @@ bool BTAdapter::addStatusListener(const AdapterStatusListenerRef& l) { return added; } -bool BTAdapter::addStatusListener(const BTDeviceRef& d, const AdapterStatusListenerRef& l) { +bool BTAdapter::addStatusListener(const BTDeviceRef& d, const AdapterStatusListenerRef& l) noexcept { if( nullptr == l ) { - throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE); + ERR_PRINT("AdapterStatusListener ref is null"); + return false; } if( nullptr == d ) { - throw jau::IllegalArgumentException("Device is null", E_FILE_LINE); + ERR_PRINT("Device ref is null"); + return false; } - const bool added = statusListenerList.push_back_unique(impl::StatusListenerPair{l, d}, - _adapterStatusListenerRefEqComparator); + const bool added = statusListenerList.push_back_unique(StatusListenerPair{l, d}, + adapterStatusListenerRefEqComparator); if( added ) { sendAdapterSettingsInitial(*l, jau::getCurrentMilliseconds()); } @@ -952,17 +955,18 @@ bool BTAdapter::addStatusListener(const BTDeviceRef& d, const AdapterStatusListe return added; } -bool BTAdapter::addStatusListener(const BTDevice& d, const AdapterStatusListenerRef& l) { +bool BTAdapter::addStatusListener(const BTDevice& d, const AdapterStatusListenerRef& l) noexcept { return addStatusListener(getSharedDevice(d), l); } -bool BTAdapter::removeStatusListener(const AdapterStatusListenerRef& l) { +bool BTAdapter::removeStatusListener(const AdapterStatusListenerRef& l) noexcept { if( nullptr == l ) { - throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE); + ERR_PRINT("AdapterStatusListener ref is null"); + return false; } - const int count = statusListenerList.erase_matching(impl::StatusListenerPair{l, std::weak_ptr<BTDevice>{}}, + const int count = statusListenerList.erase_matching(StatusListenerPair{l, std::weak_ptr<BTDevice>{}}, false /* all_matching */, - _adapterStatusListenerRefEqComparator); + adapterStatusListenerRefEqComparator); if( _print_device_lists || jau::environment::get().verbose ) { jau::PLAIN_PRINT(true, "BTAdapter::removeStatusListener.1: res %d, %s", count>0, toString().c_str()); printDeviceLists(); @@ -970,25 +974,22 @@ bool BTAdapter::removeStatusListener(const AdapterStatusListenerRef& l) { return count > 0; } -bool BTAdapter::removeStatusListener(const AdapterStatusListener * l) { +bool BTAdapter::removeStatusListener(const AdapterStatusListener * l) noexcept { if( nullptr == l ) { - throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE); + ERR_PRINT("AdapterStatusListener ref is null"); + return false; } bool res = false; { - auto begin = statusListenerList.begin(); // lock mutex and copy_store - while ( !begin.is_end() ) { - if ( *begin->listener == *l ) { - begin.erase(); + auto it = statusListenerList.begin(); // lock mutex and copy_store + for (; !it.is_end(); ++it ) { + if ( *it->listener == *l ) { + it.erase(); + it.write_back(); res = true; break; - } else { - ++begin; } } - if( res ) { - begin.write_back(); - } } if( _print_device_lists || jau::environment::get().verbose ) { jau::PLAIN_PRINT(true, "BTAdapter::removeStatusListener.2: res %d, %s", res, toString().c_str()); @@ -997,7 +998,7 @@ bool BTAdapter::removeStatusListener(const AdapterStatusListener * l) { return res; } -int BTAdapter::removeAllStatusListener(const BTDevice& d) { +int BTAdapter::removeAllStatusListener(const BTDevice& d) noexcept { int count = 0; int res = statusListenerList.size(); @@ -1586,7 +1587,7 @@ void BTAdapter::sendAdapterSettingsChanged(const AdapterSetting old_settings_, c const uint64_t timestampMS) noexcept { int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { p.listener->adapterSettingsChanged(*this, old_settings_, current_settings, changes, timestampMS); } catch (std::exception &e) { @@ -1613,7 +1614,7 @@ void BTAdapter::sendAdapterSettingsInitial(AdapterStatusListener & asl, const ui void BTAdapter::sendDeviceUpdated(std::string cause, BTDeviceRef device, uint64_t timestamp, EIRDataType updateMask) noexcept { int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(device) ) { p.listener->deviceUpdated(device, updateMask, timestamp); @@ -1696,7 +1697,7 @@ bool BTAdapter::mgmtEvDeviceDiscoveringAny(const ScanType eventScanType, const b checkDiscoveryState(); int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { p.listener->discoveringChanged(*this, currentMetaScanType, eventScanType, eventEnabled, discovery_policy, eventTimestamp); } catch (std::exception &except) { @@ -1996,7 +1997,7 @@ bool BTAdapter::mgmtEvDeviceConnectedHCI(const MgmtEvent& e) noexcept { } int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(device) ) { if( EIRDataType::NONE != updateMask ) { @@ -2046,7 +2047,7 @@ bool BTAdapter::mgmtEvConnectFailedHCI(const MgmtEvent& e) noexcept { if( !device->isConnSecurityAutoEnabled() ) { int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(device) ) { p.listener->deviceDisconnected(device, event.getHCIStatus(), handle, event.getTimestamp()); @@ -2146,7 +2147,7 @@ bool BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept { if( !device->isConnSecurityAutoEnabled() ) { int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(device) ) { p.listener->deviceDisconnected(device, event.getHCIReason(), event.getHCIHandle(), event.getTimestamp()); @@ -2393,7 +2394,7 @@ bool BTAdapter::mgmtEvDeviceFoundHCI(const MgmtEvent& e) noexcept { } int i=0; bool device_used = false; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(dev_shared) ) { device_used = p.listener->deviceFound(dev_shared, eir->getTimestamp()) || device_used; @@ -2433,7 +2434,7 @@ bool BTAdapter::mgmtEvDeviceFoundHCI(const MgmtEvent& e) noexcept { } int i=0; bool device_used = false; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(dev_shared) ) { device_used = p.listener->deviceFound(dev_shared, eir->getTimestamp()) || device_used; @@ -2472,7 +2473,7 @@ bool BTAdapter::mgmtEvDeviceFoundHCI(const MgmtEvent& e) noexcept { addSharedDevice(dev_discovered); // re-add to shared devices! int i=0; bool device_used = false; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(dev_discovered) ) { device_used = p.listener->deviceFound(dev_discovered, eir->getTimestamp()) || device_used; @@ -2615,7 +2616,7 @@ void BTAdapter::sendDevicePairingState(BTDeviceRef device, const SMPPairingState } } int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { if( p.match(device) ) { p.listener->devicePairingState(device, state, mode, timestamp); @@ -2646,7 +2647,7 @@ void BTAdapter::sendDeviceReady(BTDeviceRef device, uint64_t timestamp) noexcept removeDevicePausingDiscovery(*device, false /* off_thread_enable */); } int i=0; - jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { + jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) { try { // Only issue if valid && received connected confirmation (HCI) && not have called disconnect yet. if( device->isValidInstance() && device->getConnected() && device->allowDisconnect ) { diff --git a/src/direct_bt/BTDevice.cpp b/src/direct_bt/BTDevice.cpp index 351184f8..34004979 100644 --- a/src/direct_bt/BTDevice.cpp +++ b/src/direct_bt/BTDevice.cpp @@ -256,11 +256,11 @@ EIRDataType BTDevice::update(GattGenericAccessSvc const &data, const uint64_t ti return res; } -bool BTDevice::addStatusListener(const AdapterStatusListenerRef& l) { +bool BTDevice::addStatusListener(const AdapterStatusListenerRef& l) noexcept { return adapter.addStatusListener(*this, l); } -bool BTDevice::removeStatusListener(const AdapterStatusListenerRef& l) { +bool BTDevice::removeStatusListener(const AdapterStatusListenerRef& l) noexcept { return adapter.removeStatusListener(l); } @@ -2076,16 +2076,25 @@ bool BTDevice::pingGATT() noexcept { return gh->ping(); } -bool BTDevice::addCharListener(std::shared_ptr<BTGattCharListener> l) { +bool BTDevice::addCharListener(const BTGattCharListenerRef& l) noexcept { std::shared_ptr<BTGattHandler> gatt = getGattHandler(); if( nullptr == gatt ) { - throw jau::IllegalStateException("Device's GATTHandle not connected: "+ - toString(), E_FILE_LINE); + ERR_PRINT("Device's GATTHandle not connected: %s", toString().c_str()); + return false; } return gatt->addCharListener(l); } -bool BTDevice::removeCharListener(std::shared_ptr<BTGattCharListener> l) noexcept { +bool BTDevice::addCharListener(const BTGattCharListenerRef& l, const BTGattCharRef& d) noexcept { + std::shared_ptr<BTGattHandler> gatt = getGattHandler(); + if( nullptr == gatt ) { + ERR_PRINT("Device's GATTHandle not connected: %s", toString().c_str()); + return false; + } + return gatt->addCharListener(l, d); +} + +bool BTDevice::removeCharListener(const BTGattCharListenerRef& l) noexcept { std::shared_ptr<BTGattHandler> gatt = getGattHandler(); if( nullptr == gatt ) { // OK to have GATTHandler being shutdown @ disable @@ -2095,7 +2104,7 @@ bool BTDevice::removeCharListener(std::shared_ptr<BTGattCharListener> l) noexcep return gatt->removeCharListener(l); } -int BTDevice::removeAllAssociatedCharListener(std::shared_ptr<BTGattChar> associatedCharacteristic) noexcept { +int BTDevice::removeAllAssociatedCharListener(const BTGattCharRef& associatedCharacteristic) noexcept { std::shared_ptr<BTGattHandler> gatt = getGattHandler(); if( nullptr == gatt ) { // OK to have GATTHandler being shutdown @ disable diff --git a/src/direct_bt/BTGattChar.cpp b/src/direct_bt/BTGattChar.cpp index f33c4f80..c618cae4 100644 --- a/src/direct_bt/BTGattChar.cpp +++ b/src/direct_bt/BTGattChar.cpp @@ -49,17 +49,8 @@ using namespace jau; */ JAU_TYPENAME_CUE(direct_bt::BTGattCharListener) -/** - * Simple access and provision of a typename string representation - * at compile time like RTTI via jau::type_name_cue. - */ -JAU_TYPENAME_CUE(direct_bt::AssociatedBTGattCharListener) - const char * BTGattCharListener::type_name() const noexcept { return jau::type_name_cue<BTGattCharListener>::name(); } -const char * AssociatedBTGattCharListener::type_name() const noexcept { return jau::type_name_cue<AssociatedBTGattCharListener>::name(); } - - #define CHAR_DECL_PROPS_ENUM(X) \ X(BTGattChar,Broadcast,broadcast) \ X(BTGattChar,Read,read) \ @@ -180,7 +171,7 @@ std::string BTGattChar::toShortString() const noexcept { "], ccd-idx "+std::to_string(clientCharConfigIndex)+notify_str+"]"; } -std::shared_ptr<BTGattHandler> BTGattChar::getGattHandlerUnchecked() const noexcept { +BTGattHandlerRef BTGattChar::getGattHandlerUnchecked() const noexcept { std::shared_ptr<BTGattService> s = getServiceUnchecked(); if( nullptr != s ) { return s->getGattHandlerUnchecked(); @@ -188,7 +179,7 @@ std::shared_ptr<BTGattHandler> BTGattChar::getGattHandlerUnchecked() const noexc return nullptr; } -std::shared_ptr<BTDevice> BTGattChar::getDeviceUnchecked() const noexcept { +BTDeviceRef BTGattChar::getDeviceUnchecked() const noexcept { std::shared_ptr<BTGattService> s = getServiceUnchecked(); if( nullptr != s ) { return s->getDeviceUnchecked(); @@ -266,88 +257,39 @@ bool BTGattChar::disableIndicationNotification() noexcept { return configNotificationIndication(false, false, enabledState); } -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) { } - - const char * type_name() const noexcept override; - - 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); - } - - std::string toString() override { - return std::string(type_name())+"["+jau::to_string(this)+", delegate "+jau::to_string(delegate.get())+"]"; - } - - /** - * Comparison operator merely testing for same memory reference of the delegate. - */ - bool operator==(const BTGattCharListener& rhs) const noexcept override; - - bool operator!=(const DelegatedBTGattCharListener& rhs) const noexcept - { return !(*this == rhs); } -}; - -/** - * Simple access and provision of a typename string representation - * at compile time like RTTI via jau::type_name_cue. - */ -JAU_TYPENAME_CUE(DelegatedBTGattCharListener) - -const char * DelegatedBTGattCharListener::type_name() const noexcept { return jau::type_name_cue<DelegatedBTGattCharListener>::name(); } - -bool DelegatedBTGattCharListener::operator==(const BTGattCharListener& rhs) const noexcept -{ - if( 0 != strcmp(rhs.type_name(), jau::type_name_cue<DelegatedBTGattCharListener>::name()) ) { - return false; - } - const DelegatedBTGattCharListener& rhs2 = static_cast<const DelegatedBTGattCharListener&>(rhs); - return delegate.get() == rhs2.delegate.get(); -} - -bool BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener> l) noexcept { - std::shared_ptr<BTDevice> device = getDeviceUnchecked(); +bool BTGattChar::addCharListener(const BTGattCharListenerRef& l) noexcept { + BTDeviceRef device = getDeviceUnchecked(); if( nullptr == device ) { ERR_PRINT("Characteristic's device null: %s", toShortString().c_str()); return false; } - return device->addCharListener( std::make_shared<DelegatedBTGattCharListener>( this, l ) ); + BTGattServiceRef service = getServiceUnchecked(); + if( nullptr == service ) { + ERR_PRINT("Characteristic's service null: %s", toShortString().c_str()); + return false; + } + BTGattCharRef characteristic = service->findGattChar(*this); + if( nullptr == service ) { + ERR_PRINT("Characteristic not in service: %s", toShortString().c_str()); + return false; + } + return device->addCharListener(l, characteristic); } -bool BTGattChar::addCharListener(std::shared_ptr<BTGattChar::Listener> l, bool enabledState[2]) noexcept { +bool BTGattChar::addCharListener(const BTGattCharListenerRef& l, bool enabledState[2]) noexcept { if( !enableNotificationOrIndication(enabledState) ) { return false; } return addCharListener(l); } -bool BTGattChar::removeCharListener(std::shared_ptr<Listener> l) noexcept { +bool BTGattChar::removeCharListener(const BTGattCharListenerRef& l) noexcept { std::shared_ptr<BTDevice> device = getDeviceUnchecked(); if( nullptr == device ) { ERR_PRINT("Characteristic's device null: %s", toShortString().c_str()); return false; } - return device->removeCharListener( std::make_shared<DelegatedBTGattCharListener>( this, l ) ); + return device->removeCharListener(l); } int BTGattChar::removeAllAssociatedCharListener(bool shallDisableIndicationNotification) noexcept { diff --git a/src/direct_bt/BTGattHandler.cpp b/src/direct_bt/BTGattHandler.cpp index 20d46dd8..ebb30be2 100644 --- a/src/direct_bt/BTGattHandler.cpp +++ b/src/direct_bt/BTGattHandler.cpp @@ -102,15 +102,29 @@ bool BTGattHandler::validateConnected() noexcept { return true; } -static jau::cow_darray<BTGattCharListenerRef>::equal_comparator _btGattCharListenerRefEqComparator = - [](const BTGattCharListenerRef& a, const BTGattCharListenerRef& b) -> bool { return *a == *b; }; +BTGattHandler::gattCharListenerList_t::equal_comparator BTGattHandler::gattCharListenerRefEqComparator = + [](const GattCharListenerPair& a, const GattCharListenerPair& b) -> bool { return *a.listener == *b.listener; }; bool BTGattHandler::addCharListener(const BTGattCharListenerRef& l) noexcept { if( nullptr == l ) { ERR_PRINT("GATTCharacteristicListener ref is null"); return false; } - return btGattCharListenerList.push_back_unique(l, _btGattCharListenerRefEqComparator); + return gattCharListenerList.push_back_unique(GattCharListenerPair{l, std::weak_ptr<BTGattChar>{} }, + gattCharListenerRefEqComparator); +} + +bool BTGattHandler::addCharListener(const BTGattCharListenerRef& l, const BTGattCharRef& d) noexcept { + if( nullptr == l ) { + ERR_PRINT("GATTCharacteristicListener ref is null"); + return false; + } + if( nullptr == d ) { + ERR_PRINT("BTGattChar ref is null"); + return false; + } + return gattCharListenerList.push_back_unique(GattCharListenerPair{l, d}, + gattCharListenerRefEqComparator); } bool BTGattHandler::removeCharListener(const BTGattCharListenerRef& l) noexcept { @@ -118,7 +132,9 @@ bool BTGattHandler::removeCharListener(const BTGattCharListenerRef& l) noexcept ERR_PRINT("GATTCharacteristicListener ref is null"); return false; } - const int count = btGattCharListenerList.erase_matching(l, false /* all_matching */, _btGattCharListenerRefEqComparator); + const int count = gattCharListenerList.erase_matching(GattCharListenerPair{l, std::weak_ptr<BTGattChar>{}}, + false /* all_matching */, + gattCharListenerRefEqComparator); return count > 0; } @@ -127,9 +143,9 @@ bool BTGattHandler::removeCharListener(const BTGattCharListener * l) noexcept { ERR_PRINT("GATTCharacteristicListener ref is null"); return false; } - auto it = btGattCharListenerList.begin(); // lock mutex and copy_store + auto it = gattCharListenerList.begin(); // lock mutex and copy_store for (; !it.is_end(); ++it ) { - if ( **it == *l ) { + if ( *it->listener == *l ) { it.erase(); it.write_back(); return true; @@ -159,12 +175,12 @@ bool BTGattHandler::removeCharListener(const BTGattHandler::NativeGattCharListen } void BTGattHandler::printCharListener() noexcept { - jau::INFO_PRINT("BTGattHandler: BTGattChar %u listener", btGattCharListenerList.size()); + jau::INFO_PRINT("BTGattHandler: BTGattChar %u listener", gattCharListenerList.size()); { int i=0; - auto it = btGattCharListenerList.begin(); // lock mutex and copy_store + auto it = gattCharListenerList.begin(); // lock mutex and copy_store for (; !it.is_end(); ++it, ++i ) { - jau::INFO_PRINT("[%d]: %s", i, (*it)->toString().c_str()); + jau::INFO_PRINT("[%d]: %s", i, it->listener->toString().c_str()); } } jau::INFO_PRINT("BTGattHandler: NativeGattChar %u listener", nativeGattCharListenerList.size()); @@ -191,9 +207,9 @@ int BTGattHandler::removeAllAssociatedCharListener(const BTGattChar * associated return false; } int count = 0; - auto it = btGattCharListenerList.begin(); // lock mutex and copy_store + auto it = gattCharListenerList.begin(); // lock mutex and copy_store while( !it.is_end() ) { - if ( (*it)->match(*associatedCharacteristic) ) { + if ( it->match(*associatedCharacteristic) ) { it.erase(); ++count; } else { @@ -207,8 +223,8 @@ int BTGattHandler::removeAllAssociatedCharListener(const BTGattChar * associated } int BTGattHandler::removeAllCharListener() noexcept { - int count = btGattCharListenerList.size(); - btGattCharListenerList.clear(); + int count = gattCharListenerList.size(); + gattCharListenerList.clear(); count += nativeGattCharListenerList.size(); nativeGattCharListenerList.clear(); return count; @@ -424,7 +440,7 @@ void BTGattHandler::l2capReaderWork(jau::service_runner& sr) noexcept { } else if( AttPDUMsg::Opcode::HANDLE_VALUE_NTF == opc ) { // AttPDUMsg::OpcodeType::NOTIFICATION const AttHandleValueRcv * a = static_cast<const AttHandleValueRcv*>(attPDU.get()); COND_PRINT(env.DEBUG_DATA, "GATTHandler::reader: NTF: %s, listener [native %zd, bt %zd]", - a->toString().c_str(), nativeGattCharListenerList.size(), btGattCharListenerList.size()); + a->toString().c_str(), nativeGattCharListenerList.size(), gattCharListenerList.size()); const uint64_t a_timestamp = a->ts_creation; const uint16_t a_handle = a->getHandle(); const jau::TOctetSlice& a_value = a->getValue(); @@ -443,18 +459,18 @@ void BTGattHandler::l2capReaderWork(jau::service_runner& sr) noexcept { i++; }); } - BTGattCharRef decl = findCharacterisicsByValueHandle(services, a_handle); - if( nullptr != decl ) { + BTGattCharRef characteristic = findCharacterisicsByValueHandle(services, a_handle); + if( nullptr != characteristic ) { int i=0; - jau::for_each_fidelity(btGattCharListenerList, [&](std::shared_ptr<BTGattCharListener> &l) { + jau::for_each_fidelity(gattCharListenerList, [&](GattCharListenerPair &p) { try { - if( l->match(*decl) ) { - l->notificationReceived(decl, a_data_view, a_timestamp); + if( p.match(*characteristic) ) { + p.listener->notificationReceived(characteristic, a_data_view, a_timestamp); } } catch (std::exception &e) { ERR_PRINT("GATTHandler::notificationReceived-CBs %d/%zd: BTGattCharListener %s: Caught exception %s", - i+1, btGattCharListenerList.size(), - jau::to_hexstring((void*)l.get()).c_str(), e.what()); + i+1, gattCharListenerList.size(), + jau::to_hexstring((void*)p.listener.get()).c_str(), e.what()); } i++; }); @@ -462,7 +478,7 @@ void BTGattHandler::l2capReaderWork(jau::service_runner& sr) noexcept { } else if( AttPDUMsg::Opcode::HANDLE_VALUE_IND == opc ) { // AttPDUMsg::OpcodeType::INDICATION const AttHandleValueRcv * a = static_cast<const AttHandleValueRcv*>(attPDU.get()); COND_PRINT(env.DEBUG_DATA, "GATTHandler::reader: IND: %s, sendIndicationConfirmation %d, listener [native %zd, bt %zd]", - a->toString().c_str(), sendIndicationConfirmation.load(), nativeGattCharListenerList.size(), btGattCharListenerList.size()); + a->toString().c_str(), sendIndicationConfirmation.load(), nativeGattCharListenerList.size(), gattCharListenerList.size()); bool cfmSent = false; if( sendIndicationConfirmation ) { AttHandleValueCfm cfm; @@ -492,18 +508,18 @@ void BTGattHandler::l2capReaderWork(jau::service_runner& sr) noexcept { i++; }); } - BTGattCharRef decl = findCharacterisicsByValueHandle(services, a_handle); - if( nullptr != decl ) { + BTGattCharRef characteristic = findCharacterisicsByValueHandle(services, a_handle); + if( nullptr != characteristic ) { int i=0; - jau::for_each_fidelity(btGattCharListenerList, [&](std::shared_ptr<BTGattCharListener> &l) { + jau::for_each_fidelity(gattCharListenerList, [&](GattCharListenerPair &p) { try { - if( l->match(*decl) ) { - l->indicationReceived(decl, a_data_view, a_timestamp, cfmSent); + if( p.match(*characteristic) ) { + p.listener->indicationReceived(characteristic, a_data_view, a_timestamp, cfmSent); } } catch (std::exception &e) { ERR_PRINT("GATTHandler::indicationReceived-CBs %d/%zd: BTGattCharListener %s, cfmSent %d: Caught exception %s", - i+1, btGattCharListenerList.size(), - jau::to_hexstring((void*)l.get()).c_str(), cfmSent, e.what()); + i+1, gattCharListenerList.size(), + jau::to_hexstring((void*)p.listener.get()).c_str(), cfmSent, e.what()); } i++; }); @@ -640,7 +656,7 @@ BTGattHandler::BTGattHandler(const BTDeviceRef &device, L2CAPClient& l2cap_att, BTGattHandler::~BTGattHandler() noexcept { DBG_PRINT("GATTHandler::dtor: Start: %s", toString().c_str()); disconnect(false /* disconnect_device */, false /* ioerr_cause */); - btGattCharListenerList.clear(); + gattCharListenerList.clear(); nativeGattCharListenerList.clear(); services.clear(); genericAccess = nullptr; @@ -669,7 +685,7 @@ bool BTGattHandler::disconnect(const bool disconnect_device, const bool ioerr_ca DBG_PRINT("GATTHandler::disconnect: Not connected: disconnect_device %d, ioerr %d: GattHandler[%s], l2cap[%s], stopped %d: %s", disconnect_device, ioerr_cause, getStateString().c_str(), l2cap.getStateString().c_str(), l2cap_service_stopped, toString().c_str()); - btGattCharListenerList.clear(); + gattCharListenerList.clear(); nativeGattCharListenerList.clear(); return false; } @@ -685,7 +701,7 @@ bool BTGattHandler::disconnect(const bool disconnect_device, const bool ioerr_ca const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor DBG_PRINT("GATTHandler::disconnect: Start: disconnect_device %d, ioerr %d: GattHandler[%s], l2cap[%s]: %s", disconnect_device, ioerr_cause, getStateString().c_str(), l2cap.getStateString().c_str(), toString().c_str()); - btGattCharListenerList.clear(); + gattCharListenerList.clear(); nativeGattCharListenerList.clear(); clientMTUExchanged = false; @@ -1546,7 +1562,7 @@ std::string BTGattHandler::toString() const noexcept { return "GattHndlr["+to_string(getRole())+", "+deviceString+ ", mode "+to_string(gattServerHandler->getMode())+ ", mtu "+std::to_string(usedMTU.load())+ - ", listener[BTGatt "+std::to_string(btGattCharListenerList.size())+ + ", listener[BTGatt "+std::to_string(gattCharListenerList.size())+ ", Native "+std::to_string(nativeGattCharListenerList.size())+ "], l2capWorker[running "+std::to_string(l2cap_reader_service.is_running())+ ", shallStop "+std::to_string(l2cap_reader_service.shall_stop())+ diff --git a/src/direct_bt/BTGattService.cpp b/src/direct_bt/BTGattService.cpp index 01000854..a54e429d 100644 --- a/src/direct_bt/BTGattService.cpp +++ b/src/direct_bt/BTGattService.cpp @@ -43,7 +43,7 @@ using namespace direct_bt; using namespace jau; -std::shared_ptr<BTGattHandler> BTGattService::getGattHandlerChecked() const { +BTGattHandlerRef BTGattService::getGattHandlerChecked() const { std::shared_ptr<BTGattHandler> ref = wbr_handler.lock(); if( nullptr == ref ) { throw IllegalStateException("GATTService's GATTHandler already destructed: "+toShortString(), E_FILE_LINE); @@ -51,7 +51,7 @@ std::shared_ptr<BTGattHandler> BTGattService::getGattHandlerChecked() const { return ref; } -std::shared_ptr<BTDevice> BTGattService::getDeviceUnchecked() const noexcept { +BTDeviceRef BTGattService::getDeviceUnchecked() const noexcept { std::shared_ptr<BTGattHandler> h = getGattHandlerUnchecked(); if( nullptr != h ) { return h->getDeviceUnchecked(); @@ -59,11 +59,11 @@ std::shared_ptr<BTDevice> BTGattService::getDeviceUnchecked() const noexcept { return nullptr; } -std::shared_ptr<BTDevice> BTGattService::getDeviceChecked() const { +BTDeviceRef BTGattService::getDeviceChecked() const { return getGattHandlerChecked()->getDeviceChecked(); } -std::shared_ptr<BTGattChar> BTGattService::findGattChar(const jau::uuid_t& char_uuid) noexcept { +BTGattCharRef BTGattService::findGattChar(const jau::uuid_t& char_uuid) noexcept { for(BTGattCharRef& c : characteristicList) { if( nullptr != c && char_uuid.equivalent( *(c->value_type) ) ) { return c; @@ -72,6 +72,15 @@ std::shared_ptr<BTGattChar> BTGattService::findGattChar(const jau::uuid_t& char_ return nullptr; } +BTGattCharRef BTGattService::findGattChar(const BTGattChar& characteristic) noexcept { + for(BTGattCharRef& c : characteristicList) { + if( nullptr != c && characteristic == *c ) { + return c; + } + } + return nullptr; +} + std::string BTGattService::toString() const noexcept { std::string name = ""; if( uuid_t::TypeSize::UUID16_SZ == type->getTypeSize() ) { diff --git a/trial/direct_bt/dbt_client00.hpp b/trial/direct_bt/dbt_client00.hpp index 36508f8a..345f9355 100644 --- a/trial/direct_bt/dbt_client00.hpp +++ b/trial/direct_bt/dbt_client00.hpp @@ -228,22 +228,21 @@ class DBTClient00 : public DBTClientTest { }; - class MyGATTEventListener : public BTGattChar::Listener { + class MyGATTEventListener : public BTGattCharListener { private: DBTClient00& parent; - int i, j; public: - MyGATTEventListener(DBTClient00& p, int i_, int j_) : parent(p), i(i_), j(j_) {} + MyGATTEventListener(DBTClient00& p) : parent(p) {} void notificationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp) override { if( GATT_VERBOSE ) { const uint64_t tR = jau::getCurrentMilliseconds(); - 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()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); + fprintf_td(stderr, "** Characteristic-Notify: UUID %s, td %" PRIu64 " ******\n", + charDecl->value_type->toUUID128String().c_str(), (tR-timestamp)); + fprintf_td(stderr, "** Characteristic: %s ******\n", charDecl->toString().c_str()); + fprintf_td(stderr, "** Value R: %s ******\n", char_value.toString().c_str()); + fprintf_td(stderr, "** Value S: %s ******\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } parent.notificationsReceived++; } @@ -254,11 +253,11 @@ class DBTClient00 : public DBTClientTest { { if( GATT_VERBOSE ) { const uint64_t tR = jau::getCurrentMilliseconds(); - 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()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); + fprintf_td(stderr, "** Characteristic-Indication: UUID %s, td %" PRIu64 ", confirmed %d ******\n", + charDecl->value_type->toUUID128String().c_str(), (tR-timestamp), confirmationSent); + fprintf_td(stderr, "** Characteristic: %s ******\n", charDecl->toString().c_str()); + fprintf_td(stderr, "** Value R: %s ******\n", char_value.toString().c_str()); + fprintf_td(stderr, "** Value S: %s ******\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } parent.indicationsReceived++; } @@ -443,75 +442,76 @@ class DBTClient00 : public DBTClientTest { // cmd.close(); // done via dtor } - for(size_t i=0; i<primServices.size(); i++) { - BTGattService & primService = *primServices.at(i); - if( GATT_VERBOSE ) { - fprintf_td(stderr, " [%2.2d] Service UUID %s (%s)\n", i, - primService.type->toUUID128String().c_str(), - primService.type->getTypeSizeString().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++) { - BTGattCharRef & serviceChar = serviceCharacteristics.at(j); + std::vector<BTGattCharListenerRef> gattListener; + int loop = 0; + do { + for(size_t i=0; i<primServices.size(); i++) { + BTGattService & primService = *primServices.at(i); if( GATT_VERBOSE ) { - fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic: UUID %s (%s)\n", i, j, - serviceChar->value_type->toUUID128String().c_str(), - serviceChar->value_type->getTypeSizeString().c_str()); - fprintf_td(stderr, " [%2.2d.%2.2d] %s\n", i, j, serviceChar->toString().c_str()); + fprintf_td(stderr, " [%2.2d] Service UUID %s (%s)\n", i, + primService.type->toUUID128String().c_str(), + primService.type->getTypeSizeString().c_str()); + fprintf_td(stderr, " [%2.2d] %s\n", i, primService.toString().c_str()); } - if( serviceChar->hasProperties(BTGattChar::PropertyBitVal::Read) ) { - POctets value(BTGattHandler::number(BTGattHandler::Defaults::MAX_ATT_MTU), 0, jau::endian::little); - if( serviceChar->readValue(value) ) { - std::string sval = dfa_utf8_decode(value.get_ptr(), value.size()); + jau::darray<BTGattCharRef> & serviceCharacteristics = primService.characteristicList; + for(size_t j=0; j<serviceCharacteristics.size(); j++) { + BTGattCharRef & serviceChar = serviceCharacteristics.at(j); + if( GATT_VERBOSE ) { + fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic: UUID %s (%s)\n", i, j, + serviceChar->value_type->toUUID128String().c_str(), + serviceChar->value_type->getTypeSizeString().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, jau::endian::little); + if( serviceChar->readValue(value) ) { + std::string sval = dfa_utf8_decode(value.get_ptr(), value.size()); + if( GATT_VERBOSE ) { + fprintf_td(stderr, " [%2.2d.%2.2d] value: %s ('%s')\n", (int)i, (int)j, value.toString().c_str(), sval.c_str()); + } + } + } + jau::darray<BTGattDescRef> & charDescList = serviceChar->descriptorList; + for(size_t k=0; k<charDescList.size(); k++) { + BTGattDesc & charDesc = *charDescList.at(k); if( GATT_VERBOSE ) { - fprintf_td(stderr, " [%2.2d.%2.2d] value: %s ('%s')\n", (int)i, (int)j, value.toString().c_str(), sval.c_str()); + fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] Descriptor: UUID %s (%s)\n", i, j, k, + charDesc.type->toUUID128String().c_str(), + charDesc.type->getTypeSizeString().c_str()); + fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] %s\n", i, j, k, charDesc.toString().c_str()); } } - } - jau::darray<BTGattDescRef> & charDescList = serviceChar->descriptorList; - for(size_t k=0; k<charDescList.size(); k++) { - BTGattDesc & charDesc = *charDescList.at(k); - if( GATT_VERBOSE ) { - fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] Descriptor: UUID %s (%s)\n", i, j, k, - charDesc.type->toUUID128String().c_str(), - charDesc.type->getTypeSizeString().c_str()); - fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] %s\n", i, j, k, charDesc.toString().c_str()); + if( 0 == loop ) { + bool cccdEnableResult[2]; + if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) { + // ClientCharConfigDescriptor (CCD) is available + std::shared_ptr<BTGattCharListener> gattEventListener = std::make_shared<MyGATTEventListener>(*this); + bool clAdded = serviceChar->addCharListener( gattEventListener ); + REQUIRE( true == clAdded ); + if( clAdded ) { + gattListener.push_back(gattEventListener); + } + if( GATT_VERBOSE ) { + fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", + (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], clAdded); + fprintf_td(stderr, "\n"); + } + } } } - bool cccdEnableResult[2]; - if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) { - // ClientCharConfigDescriptor (CCD) is available - std::shared_ptr<BTGattChar::Listener> cl = std::make_shared<MyGATTEventListener>(*this, i, j); - bool clAdded = serviceChar->addCharListener( cl ); - if( GATT_VERBOSE ) { - fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", - (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], clAdded); - fprintf_td(stderr, "\n"); - } + if( GATT_VERBOSE ) { + fprintf_td(stderr, "\n"); } } - if( GATT_VERBOSE ) { - fprintf_td(stderr, "\n"); - } - } + success = notificationsReceived >= 2 || indicationsReceived >= 2; + ++loop; + } while( !success && device->getConnected() ); - { - const fraction_timespec t0 = getMonotonicTime(); - bool timeout = false; - do { - success = completedGATTCommands >= 1 && ( notificationsReceived >= 2 || indicationsReceived >= 2 ); - if( !success ) { - const fraction_i64 td = ( getMonotonicTime() - t0 ).to_fraction_i64(); - timeout = 3_s < td; - if( !timeout ) { - jau::sleep_for( 17_ms ); - } - } - } while( !success && !timeout ); + for(BTGattCharListenerRef gcl : gattListener) { + REQUIRE( true == device->removeCharListener(gcl) ); } - { + if( device->getConnected() ) { // Tell server we have successfully completed the test. BTGattCmd cmd = BTGattCmd(*device, "FinalHandshake", DBTConstants::CommandUUID, DBTConstants::ResponseUUID, 256); cmd.setVerbose(true); diff --git a/trial/direct_bt/dbt_client01.hpp b/trial/direct_bt/dbt_client01.hpp index 99e93b44..1490ab4e 100644 --- a/trial/direct_bt/dbt_client01.hpp +++ b/trial/direct_bt/dbt_client01.hpp @@ -245,22 +245,21 @@ class DBTClient01 : public DBTClientTest { }; - class MyGATTEventListener : public BTGattChar::Listener { + class MyGATTEventListener : public BTGattCharListener { private: DBTClient01& parent; - int i, j; public: - MyGATTEventListener(DBTClient01& p, int i_, int j_) : parent(p), i(i_), j(j_) {} + MyGATTEventListener(DBTClient01& p) : parent(p) {} void notificationReceived(BTGattCharRef charDecl, const TROOctets& char_value, const uint64_t timestamp) override { if( GATT_VERBOSE ) { const uint64_t tR = jau::getCurrentMilliseconds(); - 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()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); + fprintf_td(stderr, "** Characteristic-Notify: UUID %s, td %" PRIu64 " ******\n", + charDecl->value_type->toUUID128String().c_str(), (tR-timestamp)); + fprintf_td(stderr, "** Characteristic: %s ******\n", charDecl->toString().c_str()); + fprintf_td(stderr, "** Value R: %s ******\n", char_value.toString().c_str()); + fprintf_td(stderr, "** Value S: %s ******\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } parent.notificationsReceived++; } @@ -271,11 +270,11 @@ class DBTClient01 : public DBTClientTest { { if( GATT_VERBOSE ) { const uint64_t tR = jau::getCurrentMilliseconds(); - 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()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value R: %s ******\n", i, j, char_value.toString().c_str()); - fprintf_td(stderr, "**[%2.2d.%2.2d] Value S: %s ******\n", i, j, jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); + fprintf_td(stderr, "** Characteristic-Indication: UUID %s, td %" PRIu64 ", confirmed %d ******\n", + charDecl->value_type->toUUID128String().c_str(), (tR-timestamp), confirmationSent); + fprintf_td(stderr, "** Characteristic: %s ******\n", charDecl->toString().c_str()); + fprintf_td(stderr, "** Value R: %s ******\n", char_value.toString().c_str()); + fprintf_td(stderr, "** Value S: %s ******\n", jau::dfa_utf8_decode(char_value.get_ptr(), char_value.size()).c_str()); } parent.indicationsReceived++; } @@ -438,6 +437,8 @@ class DBTClient01 : public DBTClientTest { td13, td12, td23, td35); } + std::vector<BTGattCharListenerRef> gattListener; + int loop = 0; do { for(size_t i=0; i<primServices.size(); i++) { BTGattService & primService = *primServices.at(i); @@ -475,15 +476,21 @@ class DBTClient01 : public DBTClientTest { fprintf_td(stderr, " [%2.2d.%2.2d.%2.2d] %s\n", i, j, k, charDesc.toString().c_str()); } } - bool cccdEnableResult[2]; - if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) { - // ClientCharConfigDescriptor (CCD) is available - std::shared_ptr<BTGattChar::Listener> cl = std::make_shared<MyGATTEventListener>(*this, i, j); - bool clAdded = serviceChar->addCharListener( cl ); - if( GATT_VERBOSE ) { - fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", - (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], clAdded); - fprintf_td(stderr, "\n"); + if( 0 == loop ) { + bool cccdEnableResult[2]; + if( serviceChar->enableNotificationOrIndication( cccdEnableResult ) ) { + // ClientCharConfigDescriptor (CCD) is available + std::shared_ptr<BTGattCharListener> gattEventListener = std::make_shared<MyGATTEventListener>(*this); + bool clAdded = serviceChar->addCharListener( gattEventListener ); + REQUIRE( true == clAdded ); + if( clAdded ) { + gattListener.push_back(gattEventListener); + } + if( GATT_VERBOSE ) { + fprintf_td(stderr, " [%2.2d.%2.2d] Characteristic-Listener: Notification(%d), Indication(%d): Added %d\n", + (int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], clAdded); + fprintf_td(stderr, "\n"); + } } } } @@ -492,8 +499,13 @@ class DBTClient01 : public DBTClientTest { } } success = notificationsReceived >= 2 || indicationsReceived >= 2; + ++loop; } while( !success && device->getConnected() ); + for(BTGattCharListenerRef gcl : gattListener) { + REQUIRE( true == device->removeCharListener(gcl) ); + } + if( device->getConnected() ) { // Tell server we have successfully completed the test. BTGattCmd cmd = BTGattCmd(*device, "FinalHandshake", DBTConstants::CommandUUID, DBTConstants::ResponseUUID, 256); diff --git a/trial/java/trial/org/direct_bt/DBTClient00.java b/trial/java/trial/org/direct_bt/DBTClient00.java index 10da69b1..db9ec99f 100644 --- a/trial/java/trial/org/direct_bt/DBTClient00.java +++ b/trial/java/trial/org/direct_bt/DBTClient00.java @@ -24,6 +24,7 @@ package trial.org.direct_bt; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -37,6 +38,7 @@ import org.direct_bt.BTAdapter; import org.direct_bt.BTDevice; import org.direct_bt.BTDeviceRegistry; import org.direct_bt.BTGattChar; +import org.direct_bt.BTGattCharListener; import org.direct_bt.BTGattCmd; import org.direct_bt.BTGattDesc; import org.direct_bt.BTGattService; @@ -55,8 +57,7 @@ import org.direct_bt.SMPKeyBin; import org.direct_bt.SMPPairingState; import org.direct_bt.ScanType; import org.jau.net.EUI48; - -import trial.org.direct_bt.DBTClient01.MyAdapterStatusListener; +import org.junit.Assert; /** * This central BTRole::Master participant works with DBTServer00. @@ -307,21 +308,17 @@ public class DBTClient00 implements DBTClientTest { } }; - class MyGATTEventListener implements BTGattChar.Listener { - private final int i, j; - - public MyGATTEventListener(final int i_, final int j_) { i=i_; j=j_; } - + class MyGATTEventListener extends BTGattCharListener { @Override public void notificationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp) { if( GATT_VERBOSE ) { final long tR = BTUtils.currentTimeMillis(); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Notify: UUID %s, td %d ******\n", - i, j, charDecl.getUUID(), (tR-timestamp)); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length)); + BTUtils.fprintf_td(System.err, "** Characteristic-Notify: UUID %s, td %d ******\n", + charDecl.getUUID(), (tR-timestamp)); + BTUtils.fprintf_td(System.err, "** Characteristic: %s ******\n", charDecl.toString()); + BTUtils.fprintf_td(System.err, "** Value R: size %d, ro: %s ******\n", value.length, BTUtils.bytesHexString(value, 0, -1, true)); + BTUtils.fprintf_td(System.err, "** Value S: %s ******\n", BTUtils.decodeUTF8String(value, 0, value.length)); } notificationsReceived.incrementAndGet(); } @@ -331,11 +328,11 @@ public class DBTClient00 implements DBTClientTest { final byte[] value, final long timestamp, final boolean confirmationSent) { if( GATT_VERBOSE ) { final long tR = BTUtils.currentTimeMillis(); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n", - i, j, charDecl.getUUID(), (tR-timestamp), confirmationSent); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length)); + BTUtils.fprintf_td(System.err, "** Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n", + charDecl.getUUID(), (tR-timestamp), confirmationSent); + BTUtils.fprintf_td(System.err, "** Characteristic: %s ******\n", charDecl.toString()); + BTUtils.fprintf_td(System.err, "** Value R: size %d, ro: %s ******\n", value.length, BTUtils.bytesHexString(value, 0, -1, true)); + BTUtils.fprintf_td(System.err, "** Value S: %s ******\n", BTUtils.decodeUTF8String(value, 0, value.length)); } indicationsReceived.incrementAndGet(); } @@ -469,74 +466,77 @@ public class DBTClient00 implements DBTClientTest { cmd.close(); } - try { - int i=0; - for(final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) { - final BTGattService primService = srvIter.next(); - if( GATT_VERBOSE ) { - BTUtils.fprintf_td(System.err, " [%02d] Service UUID %s\n", i, primService.getUUID()); - BTUtils.fprintf_td(System.err, " [%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(); + final List<BTGattCharListener> gattListener = new ArrayList<BTGattCharListener>(); + int loop = 0; + do { + try { + int i=0; + for(final Iterator<BTGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) { + final BTGattService primService = srvIter.next(); if( GATT_VERBOSE ) { - BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic: UUID %s\n", i, j, serviceChar.getUUID()); - BTUtils.fprintf_td(System.err, " [%02d.%02d] %s\n", i, j, serviceChar.toString()); + BTUtils.fprintf_td(System.err, " [%02d] Service UUID %s\n", i, primService.getUUID()); + BTUtils.fprintf_td(System.err, " [%02d] %s\n", i, primService.toString()); } - final GattCharPropertySet properties = serviceChar.getProperties(); - if( properties.isSet(GattCharPropertySet.Type.Read) ) { - final byte[] value = serviceChar.readValue(); - final String svalue = BTUtils.decodeUTF8String(value, 0, value.length); + 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( GATT_VERBOSE ) { - BTUtils.fprintf_td(System.err, " [%02d.%02d] value: %s ('%s')\n", i, j, BTUtils.bytesHexString(value, 0, -1, true), svalue); + BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic: UUID %s\n", i, j, serviceChar.getUUID()); + BTUtils.fprintf_td(System.err, " [%02d.%02d] %s\n", i, j, serviceChar.toString()); } - } - int k=0; - final List<BTGattDesc> charDescList = serviceChar.getDescriptors(); - for(final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) { - final BTGattDesc charDesc = descIter.next(); - if( GATT_VERBOSE ) { - BTUtils.fprintf_td(System.err, " [%02d.%02d.%02d] Descriptor: UUID %s\n", i, j, k, charDesc.getUUID()); - BTUtils.fprintf_td(System.err, " [%02d.%02d.%02d] %s\n", i, j, k, charDesc.toString()); + final GattCharPropertySet properties = serviceChar.getProperties(); + if( properties.isSet(GattCharPropertySet.Type.Read) ) { + final byte[] value = serviceChar.readValue(); + final String svalue = BTUtils.decodeUTF8String(value, 0, value.length); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d] value: %s ('%s')\n", i, j, BTUtils.bytesHexString(value, 0, -1, true), svalue); + } } - } - final boolean cccdEnableResult[] = { false, false }; - if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) { - // ClientCharConfigDescriptor (CCD) is available - final boolean clAdded = null != serviceChar.addCharListener( new MyGATTEventListener(i, j) ); - if( GATT_VERBOSE ) { - BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n", - i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded); - BTUtils.fprintf_td(System.err, "\n"); + int k=0; + final List<BTGattDesc> charDescList = serviceChar.getDescriptors(); + for(final Iterator<BTGattDesc> descIter = charDescList.iterator(); descIter.hasNext(); k++) { + final BTGattDesc charDesc = descIter.next(); + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d.%02d] Descriptor: UUID %s\n", i, j, k, charDesc.getUUID()); + BTUtils.fprintf_td(System.err, " [%02d.%02d.%02d] %s\n", i, j, k, charDesc.toString()); + } + } + if( 0 == loop ) { + final boolean cccdEnableResult[] = { false, false }; + if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) { + // ClientCharConfigDescriptor (CCD) is available + final MyGATTEventListener gattEventListener = new MyGATTEventListener(); + final boolean clAdded = serviceChar.addCharListener( gattEventListener ); + Assert.assertTrue(clAdded); + if( clAdded ) { + gattListener.add(gattEventListener); + } + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n", + i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded); + BTUtils.fprintf_td(System.err, "\n"); + } + } } } + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, "\n"); + } } - if( GATT_VERBOSE ) { - BTUtils.fprintf_td(System.err, "\n"); - } + success = notificationsReceived.get() >= 2 || indicationsReceived.get() >= 2; + ++loop; + } catch( final Exception ex) { + BTUtils.println(System.err, "****** Client Processing Ready Device: Exception.2 caught for " + device.toString() + ": "+ex.getMessage()); + ex.printStackTrace(); } - } catch( final Exception ex) { - BTUtils.println(System.err, "****** Client Processing Ready Device: Exception.2 caught for " + device.toString() + ": "+ex.getMessage()); - ex.printStackTrace(); - } + } while( !success && device.getConnected() ); - { - final long t0 = BTUtils.currentTimeMillis(); - boolean timeout = false; - do { - success = completedGATTCommands.get() >= 1 && ( notificationsReceived.get() >= 2 || indicationsReceived.get() >= 2 ); - if( !success ) { - timeout = 3000 < ( BTUtils.currentTimeMillis() - t0 ); // 3s timeout - if( !timeout ) { - try { Thread.sleep(17); } catch (final InterruptedException e) { } - } - } - } while( !success && !timeout ); + for(final BTGattCharListener gcl : gattListener) { + Assert.assertTrue( device.removeCharListener(gcl) ); } - { + if( device.getConnected() ) { // Tell server we have successfully completed the test. final BTGattCmd cmd = new BTGattCmd(device, "FinalHandshake", null /* service_uuid */, DBTConstants.CommandUUID, DBTConstants.ResponseUUID); cmd.setVerbose(true); diff --git a/trial/java/trial/org/direct_bt/DBTClient01.java b/trial/java/trial/org/direct_bt/DBTClient01.java index eca16ba8..6a9488a7 100644 --- a/trial/java/trial/org/direct_bt/DBTClient01.java +++ b/trial/java/trial/org/direct_bt/DBTClient01.java @@ -24,6 +24,7 @@ package trial.org.direct_bt; +import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; @@ -37,6 +38,7 @@ import org.direct_bt.BTAdapter; import org.direct_bt.BTDevice; import org.direct_bt.BTDeviceRegistry; import org.direct_bt.BTGattChar; +import org.direct_bt.BTGattCharListener; import org.direct_bt.BTGattCmd; import org.direct_bt.BTGattDesc; import org.direct_bt.BTGattService; @@ -55,6 +57,7 @@ import org.direct_bt.SMPKeyBin; import org.direct_bt.SMPPairingState; import org.direct_bt.ScanType; import org.jau.net.EUI48; +import org.junit.Assert; /** * This central BTRole::Master participant works with DBTServer00. @@ -309,21 +312,17 @@ public class DBTClient01 implements DBTClientTest { } }; - class MyGATTEventListener implements BTGattChar.Listener { - private final int i, j; - - public MyGATTEventListener(final int i_, final int j_) { i=i_; j=j_; } - + class MyGATTEventListener extends BTGattCharListener { @Override public void notificationReceived(final BTGattChar charDecl, final byte[] value, final long timestamp) { if( GATT_VERBOSE ) { final long tR = BTUtils.currentTimeMillis(); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Notify: UUID %s, td %d ******\n", - i, j, charDecl.getUUID(), (tR-timestamp)); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length)); + BTUtils.fprintf_td(System.err, "** Characteristic-Notify: UUID %s, td %d ******\n", + charDecl.getUUID(), (tR-timestamp)); + BTUtils.fprintf_td(System.err, "** Characteristic: %s ******\n", charDecl.toString()); + BTUtils.fprintf_td(System.err, "** Value R: size %d, ro: %s ******\n", value.length, BTUtils.bytesHexString(value, 0, -1, true)); + BTUtils.fprintf_td(System.err, "** Value S: %s ******\n", BTUtils.decodeUTF8String(value, 0, value.length)); } notificationsReceived.incrementAndGet(); } @@ -333,11 +332,11 @@ public class DBTClient01 implements DBTClientTest { final byte[] value, final long timestamp, final boolean confirmationSent) { if( GATT_VERBOSE ) { final long tR = BTUtils.currentTimeMillis(); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n", - i, j, charDecl.getUUID(), (tR-timestamp), confirmationSent); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Characteristic: %s ******\n", i, j, charDecl.toString()); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value R: size %d, ro: %s ******\n", i, j, value.length, BTUtils.bytesHexString(value, 0, -1, true)); - BTUtils.fprintf_td(System.err, "**[%02d.%02d] Value S: %s ******\n", i, j, BTUtils.decodeUTF8String(value, 0, value.length)); + BTUtils.fprintf_td(System.err, "** Characteristic-Indication: UUID %s, td %d, confirmed %b ******\n", + charDecl.getUUID(), (tR-timestamp), confirmationSent); + BTUtils.fprintf_td(System.err, "** Characteristic: %s ******\n", charDecl.toString()); + BTUtils.fprintf_td(System.err, "** Value R: size %d, ro: %s ******\n", value.length, BTUtils.bytesHexString(value, 0, -1, true)); + BTUtils.fprintf_td(System.err, "** Value S: %s ******\n", BTUtils.decodeUTF8String(value, 0, value.length)); } indicationsReceived.incrementAndGet(); } @@ -449,6 +448,8 @@ public class DBTClient01 implements DBTClientTest { "PERF: get-gatt-services " + td35 + " ms,"+System.lineSeparator()); } + final List<BTGattCharListener> gattListener = new ArrayList<BTGattCharListener>(); + int loop = 0; do { try { int i=0; @@ -483,14 +484,21 @@ public class DBTClient01 implements DBTClientTest { BTUtils.fprintf_td(System.err, " [%02d.%02d.%02d] %s\n", i, j, k, charDesc.toString()); } } - final boolean cccdEnableResult[] = { false, false }; - if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) { - // ClientCharConfigDescriptor (CCD) is available - final boolean clAdded = null != serviceChar.addCharListener( new MyGATTEventListener(i, j) ); - if( GATT_VERBOSE ) { - BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n", - i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded); - BTUtils.fprintf_td(System.err, "\n"); + if( 0 == loop ) { + final boolean cccdEnableResult[] = { false, false }; + if( serviceChar.enableNotificationOrIndication( cccdEnableResult ) ) { + // ClientCharConfigDescriptor (CCD) is available + final MyGATTEventListener gattEventListener = new MyGATTEventListener(); + final boolean clAdded = serviceChar.addCharListener( gattEventListener ); + Assert.assertTrue(clAdded); + if( clAdded ) { + gattListener.add(gattEventListener); + } + if( GATT_VERBOSE ) { + BTUtils.fprintf_td(System.err, " [%02d.%02d] Characteristic-Listener: Notification(%b), Indication(%b): Added %b\n", + i, j, cccdEnableResult[0], cccdEnableResult[1], clAdded); + BTUtils.fprintf_td(System.err, "\n"); + } } } } @@ -498,15 +506,18 @@ public class DBTClient01 implements DBTClientTest { BTUtils.fprintf_td(System.err, "\n"); } } - success = notificationsReceived.get() >= 2 || indicationsReceived.get() >= 2; - + ++loop; } catch( final Exception ex) { BTUtils.println(System.err, "****** Client Processing Ready Device: Exception.2 caught for " + device.toString() + ": "+ex.getMessage()); ex.printStackTrace(); } } while( !success && device.getConnected() ); + for(final BTGattCharListener gcl : gattListener) { + Assert.assertTrue( device.removeCharListener(gcl) ); + } + if( device.getConnected() ) { // Tell server we have successfully completed the test. final BTGattCmd cmd = new BTGattCmd(device, "FinalHandshake", null /* service_uuid */, DBTConstants.CommandUUID, DBTConstants.ResponseUUID); |