diff options
author | Sven Gothel <[email protected]> | 2022-05-10 03:57:49 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-05-10 03:57:49 +0200 |
commit | 4faded58c1f5662b8baea6b1259cd297986a278c (patch) | |
tree | ab2df60f577efefa43f5472de4c3e79e01f893bf /java | |
parent | 4d5b98cade63a3cb0208bade2b5ff0d531594d74 (diff) |
JNI Lifecycle Fix: BTGattCharListener: Adopt full Java/Native link via DBTNativeDownlink and JavaUplink like AdapterStatusListener change, clean listener API + impl.
AdapterStatusListener adopted fully linked via DBTNativeDownlink (java->native) and JavaUplink (native->java).
This allows intrinsic lifecycle management.
Native destruction leads to its reference removal from the java object
and destruction of the java object removes its reference from the native object.
Both reference removals may lead to their destruction if reaching zero.
(was commit 9c5f25ccd1637728d6e79592279e4b38ecd32f59)
Same applies to BTGattCharListener:
- This removed BTGattChar::Listener, simply use BTGattCharListener
- Using private BTGattHandler::GattCharListenerPair struct for BTGattChar mapping
- No more manual or exposed BTGattChar mapping
- Java: An added BTGattCharListener instance can be used for removal now,
no more wrapper object magic returned.
Further:
- moved removed `namespace impl`, moved StatusListenerPair into private BTAdapter
- have all add/remove*Listener methods noexcept
Unit tests validating BTGattCharListener add and remove.
Diffstat (limited to 'java')
-rw-r--r-- | java/jau/direct_bt/DBTDevice.java | 9 | ||||
-rw-r--r-- | java/jau/direct_bt/DBTGattChar.java | 33 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTAdapter.cxx | 2 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTDevice.cxx | 121 | ||||
-rw-r--r-- | java/org/direct_bt/AdapterStatusListener.java | 3 | ||||
-rw-r--r-- | java/org/direct_bt/BTDevice.java | 10 | ||||
-rw-r--r-- | java/org/direct_bt/BTGattChar.java | 111 | ||||
-rw-r--r-- | java/org/direct_bt/BTGattCharListener.java | 53 | ||||
-rw-r--r-- | java/org/direct_bt/BTGattCmd.java | 16 | ||||
-rw-r--r-- | java/org/direct_bt/BTGattService.java | 3 |
10 files changed, 140 insertions, 221 deletions
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(); |