diff options
author | Sven Gothel <[email protected]> | 2022-05-09 01:47:34 +0200 |
---|---|---|
committer | Sven Gothel <[email protected]> | 2022-05-09 01:47:34 +0200 |
commit | 9c5f25ccd1637728d6e79592279e4b38ecd32f59 (patch) | |
tree | 265b733ff97873a822619f3bff904303072f3ff9 | |
parent | 6c1dbe6d7474283a343eca93c79549cda705bca4 (diff) |
JNI Lifecycle Fix: Have AdapterStatusListener derive from JavaUplink/DBTNativeDownlink, resolving BTAdapter::removeAllStatusListener() and use shared_ptr_ref<T>
- AdapterStatusListener's native instance is now created at java object construction, etc .. same pattern like BTDevice ..
- Java/Native object relation is inherently thread safe due to using use shared_ptr_ref<T>
-rw-r--r-- | api/direct_bt/BTAdapter.hpp | 24 | ||||
-rw-r--r-- | api/direct_bt/BTDevice.hpp | 5 | ||||
-rw-r--r-- | examples/dbt_peripheral00.cpp | 2 | ||||
-rw-r--r-- | examples/dbt_repeater00.cpp | 4 | ||||
-rw-r--r-- | examples/dbt_scanner10.cpp | 2 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTAdapter.cxx | 256 | ||||
-rw-r--r-- | java/org/direct_bt/AdapterStatusListener.java | 21 | ||||
-rw-r--r-- | src/direct_bt/BTAdapter.cpp | 18 | ||||
-rw-r--r-- | trial/direct_bt/dbt_client00.hpp | 2 | ||||
-rw-r--r-- | trial/direct_bt/dbt_client01.hpp | 2 | ||||
-rw-r--r-- | trial/direct_bt/dbt_client_server1x.hpp | 2 | ||||
-rw-r--r-- | trial/direct_bt/dbt_server00.hpp | 2 | ||||
-rw-r--r-- | trial/direct_bt/dbt_server01.hpp | 2 |
13 files changed, 207 insertions, 135 deletions
diff --git a/api/direct_bt/BTAdapter.hpp b/api/direct_bt/BTAdapter.hpp index c5d9fe4e..ef06d53a 100644 --- a/api/direct_bt/BTAdapter.hpp +++ b/api/direct_bt/BTAdapter.hpp @@ -110,13 +110,13 @@ namespace direct_bt { * </p> * <p> * A listener instance may be attached to a {@link BTAdapter} via - * {@link BTAdapter::addStatusListener(std::shared_ptr<AdapterStatusListener>)}. + * {@link BTAdapter::addStatusListener(const AdapterStatusListenerRef&)}. * </p> * <p> * The listener receiver maintains a unique set of listener instances without duplicates. * </p> */ - class AdapterStatusListener { + class AdapterStatusListener : public jau::JavaUplink { public: /** * Custom filter for all 'device*' notification methods, @@ -281,7 +281,14 @@ namespace direct_bt { virtual ~AdapterStatusListener() {} - virtual std::string toString() const = 0; + std::string toString() const noexcept override { return "AdapterStatusListener["+jau::to_hexstring(this)+"]"; } + + std::string get_java_class() const noexcept override { + return java_class(); + } + static std::string java_class() noexcept { + return std::string(JAVA_MAIN_PACKAGE "AdapterStatusListener"); + } /** * Default comparison operator, merely testing for same memory reference. @@ -295,6 +302,7 @@ namespace direct_bt { bool operator!=(const AdapterStatusListener& rhs) const { return !(*this == rhs); } }; + typedef std::shared_ptr<AdapterStatusListener> AdapterStatusListenerRef; // ************************************************* // ************************************************* @@ -303,7 +311,7 @@ namespace direct_bt { namespace impl { struct StatusListenerPair { /** The actual listener */ - std::shared_ptr<AdapterStatusListener> listener; + AdapterStatusListenerRef listener; /** The optional weak device reference. Weak, b/c it shall not block destruction */ std::weak_ptr<BTDevice> wbr_device; }; @@ -960,12 +968,14 @@ namespace direct_bt { * @see removeStatusListener() * @see removeAllStatusListener() */ - bool addStatusListener(std::shared_ptr<AdapterStatusListener> l); + bool addStatusListener(const AdapterStatusListenerRef& l); /** * Please use BTDevice::addStatusListener() for clarity, merely existing here to allow JNI access. */ - bool addStatusListener(const BTDevice& d, std::shared_ptr<AdapterStatusListener> l); + bool addStatusListener(const BTDeviceRef& d, const AdapterStatusListenerRef& l); + + bool addStatusListener(const BTDevice& d, const AdapterStatusListenerRef& l); /** * Remove the given listener from the list. @@ -976,7 +986,7 @@ namespace direct_bt { * @see BTDevice::removeStatusListener() * @see addStatusListener() */ - bool removeStatusListener(std::shared_ptr<AdapterStatusListener> l); + bool removeStatusListener(const AdapterStatusListenerRef& l); /** * Remove the given listener from the list. diff --git a/api/direct_bt/BTDevice.hpp b/api/direct_bt/BTDevice.hpp index 270cb5ae..d9e1a2cf 100644 --- a/api/direct_bt/BTDevice.hpp +++ b/api/direct_bt/BTDevice.hpp @@ -53,6 +53,7 @@ namespace direct_bt { class BTAdapter; // forward class AdapterStatusListener; // forward + typedef std::shared_ptr<AdapterStatusListener> AdapterStatusListenerRef; // forward /** * BTDevice represents one remote Bluetooth device. @@ -383,7 +384,7 @@ namespace direct_bt { * @see BTAdapter::removeAllStatusListener() * @see removeStatusListener() */ - bool addStatusListener(std::shared_ptr<AdapterStatusListener> l); + bool addStatusListener(const AdapterStatusListenerRef& l); /** * Remove the given listener from the list. @@ -397,7 +398,7 @@ namespace direct_bt { * @see BTAdapter::removeAllStatusListener() * @see addStatusListener() */ - bool removeStatusListener(std::shared_ptr<AdapterStatusListener> l); + bool removeStatusListener(const AdapterStatusListenerRef& l); /** * Retrieves the current connection info for this device and returns the ConnectionInfo reference if successful, diff --git a/examples/dbt_peripheral00.cpp b/examples/dbt_peripheral00.cpp index 667d3de2..f84aba70 100644 --- a/examples/dbt_peripheral00.cpp +++ b/examples/dbt_peripheral00.cpp @@ -337,7 +337,7 @@ class MyAdapterStatusListener : public AdapterStatusListener { (void)timestamp; } - std::string toString() const override { + std::string toString() const noexcept override { return "MyAdapterStatusListener[this "+to_hexstring(this)+"]"; } diff --git a/examples/dbt_repeater00.cpp b/examples/dbt_repeater00.cpp index 76bb9ad6..a84a353e 100644 --- a/examples/dbt_repeater00.cpp +++ b/examples/dbt_repeater00.cpp @@ -270,7 +270,7 @@ class AdapterToServerStatusListener : public AdapterStatusListener { dc.detach(); } - std::string toString() const override { + std::string toString() const noexcept override { return "MyAdapterClientStatusListener[this "+to_hexstring(this)+"]"; } @@ -700,7 +700,7 @@ class AdapterToClientStatusListener : public AdapterStatusListener { (void)timestamp; } - std::string toString() const override { + std::string toString() const noexcept override { return "MyAdapterServerStatusListener[this "+to_hexstring(this)+"]"; } diff --git a/examples/dbt_scanner10.cpp b/examples/dbt_scanner10.cpp index 1721be24..b6db78b3 100644 --- a/examples/dbt_scanner10.cpp +++ b/examples/dbt_scanner10.cpp @@ -316,7 +316,7 @@ class MyAdapterStatusListener : public AdapterStatusListener { } } - std::string toString() const override { + std::string toString() const noexcept override { return "MyAdapterStatusListener[this "+to_hexstring(this)+"]"; } diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx index 6ffea073..461cab35 100644 --- a/java/jni/direct_bt/DBTAdapter.cxx +++ b/java/jni/direct_bt/DBTAdapter.cxx @@ -23,6 +23,7 @@ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include "org_direct_bt_AdapterStatusListener.h" #include "jau_direct_bt_DBTAdapter.h" // #define VERBOSE_ON 1 @@ -83,10 +84,8 @@ class JNIAdapterStatusListener : public AdapterStatusListener { */ static std::atomic<int> iname_next; int const iname; - BTDevice const * const deviceMatchRef; - jau::JavaGlobalObj listenerObjRef; + BTDeviceRef deviceMatchRef; - std::shared_ptr<jau::JavaAnon> adapterObjRef; jau::JNIGlobalRef adapterSettingsClazzRef; jmethodID adapterSettingsClazzCtor; jau::JNIGlobalRef eirDataTypeSetClazzRef; @@ -118,7 +117,7 @@ class JNIAdapterStatusListener : public AdapterStatusListener { public: - std::string toString() const override { + std::string toString() const noexcept override { const std::string devMatchAddr = nullptr != deviceMatchRef ? deviceMatchRef->getAddressAndType().toString() : "nil"; return "JNIAdapterStatusListener[this "+jau::to_hexstring(this)+", iname "+std::to_string(iname)+", devMatchAddr "+devMatchAddr+"]"; } @@ -127,14 +126,10 @@ class JNIAdapterStatusListener : public AdapterStatusListener { // listenerObjRef dtor will call notifyDelete and clears the nativeInstance handle } - JNIAdapterStatusListener(JNIEnv *env, BTAdapter *adapter, - jclass listenerClazz, jobject statusListenerObj, jmethodID statusListenerNotifyDeleted, - const BTDevice * _deviceMatchRef) - : iname(iname_next.fetch_add(1)), deviceMatchRef(_deviceMatchRef), - listenerObjRef(statusListenerObj, statusListenerNotifyDeleted) + JNIAdapterStatusListener(JNIEnv *env, jobject statusListenerObj, const BTDeviceRef& _deviceMatchRef) + : iname(iname_next.fetch_add(1)), deviceMatchRef(_deviceMatchRef) { - adapterObjRef = adapter->getJavaObject(); - jau::JavaGlobalObj::check(adapterObjRef, E_FILE_LINE); + jclass listenerClazz = jau::search_class(env, statusListenerObj); // adapterSettingsClazzRef, adapterSettingsClazzCtor { @@ -214,6 +209,10 @@ class JNIAdapterStatusListener : public AdapterStatusListener { mDeviceDisconnected = jau::search_method(env, listenerClazz, "deviceDisconnected", _deviceDisconnectedMethodArgs.c_str(), false); } + void setDeviceMatchRef(const BTDeviceRef& _deviceMatchRef) { + deviceMatchRef = _deviceMatchRef; + } + bool matchDevice(const BTDevice & device) override { if( nullptr == deviceMatchRef ) { return true; @@ -225,6 +224,12 @@ class JNIAdapterStatusListener : public AdapterStatusListener { const AdapterSetting changedmask, const uint64_t timestamp) override { JNIEnv *env = *jau::jni_env; (void)a; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); + + jau::JavaAnonRef adapter_java = a.getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(adapter_java, E_FILE_LINE); + jobject adapterSettingOld = env->NewObject(adapterSettingsClazzRef.getClass(), adapterSettingsClazzCtor, (jint)oldmask); jau::java_exception_check_and_throw(env, E_FILE_LINE); jau::JNIGlobalRef::check(adapterSettingOld, E_FILE_LINE); @@ -237,8 +242,8 @@ class JNIAdapterStatusListener : public AdapterStatusListener { jau::java_exception_check_and_throw(env, E_FILE_LINE); jau::JNIGlobalRef::check(adapterSettingChanged, E_FILE_LINE); - env->CallVoidMethod(listenerObjRef.getObject(), mAdapterSettingsChanged, - jau::JavaGlobalObj::GetObject(adapterObjRef), adapterSettingOld, adapterSettingNew, adapterSettingChanged, (jlong)timestamp); + env->CallVoidMethod(jau::JavaGlobalObj::GetObject(asl_java), mAdapterSettingsChanged, + jau::JavaGlobalObj::GetObject(adapter_java), adapterSettingOld, adapterSettingNew, adapterSettingChanged, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); env->DeleteLocalRef(adapterSettingOld); env->DeleteLocalRef(adapterSettingNew); @@ -248,6 +253,11 @@ class JNIAdapterStatusListener : public AdapterStatusListener { void discoveringChanged(BTAdapter &a, const ScanType currentMeta, const ScanType changedType, const bool changedEnabled, const DiscoveryPolicy policy, const uint64_t timestamp) override { JNIEnv *env = *jau::jni_env; (void)a; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); + + jau::JavaAnonRef adapter_java = a.getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(adapter_java, E_FILE_LINE); jobject jcurrentMeta = env->CallStaticObjectMethod(scanTypeClazzRef.getClass(), scanTypeClazzGet, (jbyte)number(currentMeta)); jau::java_exception_check_and_throw(env, E_FILE_LINE); @@ -261,7 +271,8 @@ class JNIAdapterStatusListener : public AdapterStatusListener { jau::java_exception_check_and_throw(env, E_FILE_LINE); jau::JNIGlobalRef::check(jdiscoveryPolicy, E_FILE_LINE); - env->CallVoidMethod(listenerObjRef.getObject(), mDiscoveringChanged, jau::JavaGlobalObj::GetObject(adapterObjRef), + env->CallVoidMethod(jau::JavaGlobalObj::GetObject(asl_java), mDiscoveringChanged, + jau::JavaGlobalObj::GetObject(adapter_java), jcurrentMeta, jchangedType, (jboolean)changedEnabled, jdiscoveryPolicy, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); } @@ -271,18 +282,22 @@ class JNIAdapterStatusListener : public AdapterStatusListener { jobject newJavaBTDevice(JNIEnv *env, BTDeviceRef device, const uint64_t timestamp) { // DBTDevice(final long nativeInstance, final DBTAdapter adptr, final byte byteAddress[/*6*/], final byte byteAddressType, // final long ts_creation, final String name) + jau::JavaAnonRef adapter_java = device->getAdapter().getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(adapter_java, E_FILE_LINE); + const EUI48 & addr = device->getAddressAndType().address; jbyteArray jaddr = env->NewByteArray(sizeof(addr)); env->SetByteArrayRegion(jaddr, 0, sizeof(addr), (const jbyte*)(addr.b)); jau::java_exception_check_and_throw(env, E_FILE_LINE); const jstring name = jau::from_string_to_jstring(env, device->getName()); jau::java_exception_check_and_throw(env, E_FILE_LINE); + jau::shared_ptr_ref<BTDevice> device_sref(device); // new instance to be released into new jobject jobject tmp_jdevice = env->NewObject(deviceClazzRef.getClass(), deviceClazzCtor, - (jlong)device.get(), jau::JavaGlobalObj::GetObject(adapterObjRef), + device_sref.release_to_jlong(), jau::JavaGlobalObj::GetObject(adapter_java), jaddr, device->getAddressAndType().type, (jlong)timestamp, name); jau::java_exception_check_and_throw(env, E_FILE_LINE); jau::JNIGlobalRef::check(tmp_jdevice, E_FILE_LINE); - std::shared_ptr<jau::JavaAnon> jDeviceRef1 = device->getJavaObject(); + jau::JavaAnonRef jDeviceRef1 = device->getJavaObject(); jau::JavaGlobalObj::check(jDeviceRef1, E_FILE_LINE); jobject jdevice = jau::JavaGlobalObj::GetObject(jDeviceRef1); env->DeleteLocalRef(jaddr); @@ -295,47 +310,55 @@ class JNIAdapterStatusListener : public AdapterStatusListener { bool deviceFound(BTDeviceRef device, const uint64_t timestamp) override { JNIEnv *env = *jau::jni_env; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); + jobject jdevice; - std::shared_ptr<jau::JavaAnon> jDeviceRef0 = device->getJavaObject(); - if( jau::JavaGlobalObj::isValid(jDeviceRef0) ) { + jau::JavaAnonRef device_java = device->getJavaObject(); + if( jau::JavaGlobalObj::isValid(device_java) ) { // Reuse Java instance - jdevice = jau::JavaGlobalObj::GetObject(jDeviceRef0); + jdevice = jau::JavaGlobalObj::GetObject(device_java); } else { jdevice = newJavaBTDevice(env, device, timestamp); } env->SetLongField(jdevice, deviceClazzTSLastDiscoveryField, (jlong)device->getLastDiscoveryTimestamp()); jau::java_exception_check_and_throw(env, E_FILE_LINE); - jboolean res = env->CallBooleanMethod(listenerObjRef.getObject(), mDeviceFound, jdevice, (jlong)timestamp); + jboolean res = env->CallBooleanMethod(jau::JavaGlobalObj::GetObject(asl_java), mDeviceFound, jdevice, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); return JNI_TRUE == res; } void deviceUpdated(BTDeviceRef device, const EIRDataType updateMask, const uint64_t timestamp) override { - std::shared_ptr<jau::JavaAnon> jDeviceRef = device->getJavaObject(); - if( !jau::JavaGlobalObj::isValid(jDeviceRef) ) { + JNIEnv *env = *jau::jni_env; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); + + jau::JavaAnonRef device_java = device->getJavaObject(); + if( !jau::JavaGlobalObj::isValid(device_java) ) { return; // java device has been pulled } - JNIEnv *env = *jau::jni_env; - env->SetLongField(jau::JavaGlobalObj::GetObject(jDeviceRef), deviceClazzTSLastUpdateField, (jlong)timestamp); + env->SetLongField(jau::JavaGlobalObj::GetObject(device_java), deviceClazzTSLastUpdateField, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); jobject eirDataTypeSet = env->NewObject(eirDataTypeSetClazzRef.getClass(), eirDataTypeSetClazzCtor, (jint)updateMask); jau::java_exception_check_and_throw(env, E_FILE_LINE); jau::JNIGlobalRef::check(eirDataTypeSet, E_FILE_LINE); - env->CallVoidMethod(listenerObjRef.getObject(), mDeviceUpdated, jau::JavaGlobalObj::GetObject(jDeviceRef), eirDataTypeSet, (jlong)timestamp); + env->CallVoidMethod(jau::JavaGlobalObj::GetObject(asl_java), mDeviceUpdated, jau::JavaGlobalObj::GetObject(device_java), eirDataTypeSet, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); env->DeleteLocalRef(eirDataTypeSet); } void deviceConnected(BTDeviceRef device, const bool discovered, const uint64_t timestamp) override { JNIEnv *env = *jau::jni_env; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); jobject jdevice; - std::shared_ptr<jau::JavaAnon> jDeviceRef0 = device->getJavaObject(); - if( jau::JavaGlobalObj::isValid(jDeviceRef0) ) { + jau::JavaAnonRef device_java = device->getJavaObject(); + if( jau::JavaGlobalObj::isValid(device_java) ) { // Reuse Java instance - jdevice = jau::JavaGlobalObj::GetObject(jDeviceRef0); + jdevice = jau::JavaGlobalObj::GetObject(device_java); } else { jdevice = newJavaBTDevice(env, device, timestamp); } @@ -346,17 +369,20 @@ class JNIAdapterStatusListener : public AdapterStatusListener { env->SetLongField(jdevice, deviceClazzTSLastUpdateField, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); - env->CallVoidMethod(listenerObjRef.getObject(), mDeviceConnected, jdevice, (jboolean)discovered, (jlong)timestamp); + env->CallVoidMethod(jau::JavaGlobalObj::GetObject(asl_java), mDeviceConnected, jdevice, (jboolean)discovered, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); } void devicePairingState(BTDeviceRef device, const SMPPairingState state, const PairingMode mode, const uint64_t timestamp) override { - std::shared_ptr<jau::JavaAnon> jDeviceRef = device->getJavaObject(); - if( !jau::JavaGlobalObj::isValid(jDeviceRef) ) { + JNIEnv *env = *jau::jni_env; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); + + jau::JavaAnonRef device_java = device->getJavaObject(); + if( !jau::JavaGlobalObj::isValid(device_java) ) { return; // java device has been pulled } - JNIEnv *env = *jau::jni_env; - jobject jdevice = jau::JavaGlobalObj::GetObject(jDeviceRef); + jobject jdevice = jau::JavaGlobalObj::GetObject(device_java); env->SetLongField(jdevice, deviceClazzTSLastUpdateField, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); @@ -368,31 +394,37 @@ class JNIAdapterStatusListener : public AdapterStatusListener { jau::java_exception_check_and_throw(env, E_FILE_LINE); jau::JNIGlobalRef::check(jmode, E_FILE_LINE); - env->CallVoidMethod(listenerObjRef.getObject(), mDevicePairingState, jdevice, jstate, jmode, (jlong)timestamp); + env->CallVoidMethod(jau::JavaGlobalObj::GetObject(asl_java), mDevicePairingState, jdevice, jstate, jmode, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); } void deviceReady(BTDeviceRef device, const uint64_t timestamp) override { - std::shared_ptr<jau::JavaAnon> jDeviceRef = device->getJavaObject(); - if( !jau::JavaGlobalObj::isValid(jDeviceRef) ) { + JNIEnv *env = *jau::jni_env; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); + + jau::JavaAnonRef device_java = device->getJavaObject(); + if( !jau::JavaGlobalObj::isValid(device_java) ) { return; // java device has been pulled } - JNIEnv *env = *jau::jni_env; - jobject jdevice = jau::JavaGlobalObj::GetObject(jDeviceRef); + jobject jdevice = jau::JavaGlobalObj::GetObject(device_java); env->SetLongField(jdevice, deviceClazzTSLastUpdateField, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); - env->CallVoidMethod(listenerObjRef.getObject(), mDeviceReady, jdevice, (jlong)timestamp); + env->CallVoidMethod(jau::JavaGlobalObj::GetObject(asl_java), mDeviceReady, jdevice, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); } void deviceDisconnected(BTDeviceRef device, const HCIStatusCode reason, const uint16_t handle, const uint64_t timestamp) override { - std::shared_ptr<jau::JavaAnon> jDeviceRef = device->getJavaObject(); - if( !jau::JavaGlobalObj::isValid(jDeviceRef) ) { + JNIEnv *env = *jau::jni_env; + jau::JavaAnonRef asl_java = getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); + + jau::JavaAnonRef device_java = device->getJavaObject(); + if( !jau::JavaGlobalObj::isValid(device_java) ) { return; // java device has been pulled } - JNIEnv *env = *jau::jni_env; - jobject jdevice = jau::JavaGlobalObj::GetObject(jDeviceRef); + jobject jdevice = jau::JavaGlobalObj::GetObject(device_java); env->SetLongField(jdevice, deviceClazzTSLastUpdateField, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); @@ -403,89 +435,117 @@ class JNIAdapterStatusListener : public AdapterStatusListener { env->SetShortField(jdevice, deviceClazzConnectionHandleField, (jshort)0); // zero out, disconnected jau::java_exception_check_and_throw(env, E_FILE_LINE); - env->CallVoidMethod(listenerObjRef.getObject(), mDeviceDisconnected, jdevice, hciErrorCode, (jshort)handle, (jlong)timestamp); + env->CallVoidMethod(jau::JavaGlobalObj::GetObject(asl_java), mDeviceDisconnected, jdevice, hciErrorCode, (jshort)handle, (jlong)timestamp); jau::java_exception_check_and_throw(env, E_FILE_LINE); } }; std::atomic<int> JNIAdapterStatusListener::iname_next(0); -jboolean Java_jau_direct_1bt_DBTAdapter_addStatusListenerImpl(JNIEnv *env, jobject obj, jobject jdeviceOwnerAndMatch, jobject statusListener) -{ +/* + * Class: org_direct_bt_AdapterStatusListener + * Method: ctorImpl + * Signature: ()J + */ +jlong Java_org_direct_1bt_AdapterStatusListener_ctorImpl(JNIEnv *env, jobject obj) { try { - if( nullptr == statusListener ) { - throw jau::IllegalArgumentException("JNIAdapterStatusListener::addStatusListener: statusListener is null", E_FILE_LINE); - } - { - JNIAdapterStatusListener * pre = jau::getInstanceUnchecked<JNIAdapterStatusListener>(env, statusListener); - if( nullptr != pre ) { - throw jau::IllegalStateException("JNIAdapterStatusListener::addStatusListener: statusListener's nativeInstance not null, already in use", E_FILE_LINE); - return false; - } + // new instance + jau::shared_ptr_ref<JNIAdapterStatusListener> ref( new JNIAdapterStatusListener(env, obj, nullptr) ); + + return ref.release_to_jlong(); + } catch(...) { + rethrow_and_raise_java_exception(env); + } + return (jlong) (intptr_t) nullptr; +} + + +/* + * Class: org_direct_bt_AdapterStatusListener + * Method: deleteImpl + * Signature: (J)V + */ +void Java_org_direct_1bt_AdapterStatusListener_deleteImpl(JNIEnv *env, jobject obj, jlong nativeInstance) { + (void)obj; + try { + jau::shared_ptr_ref<JNIAdapterStatusListener> sref(nativeInstance, false /* throw_on_nullptr */); // hold copy until done + if( nullptr != sref.pointer() ) { + std::shared_ptr<JNIAdapterStatusListener>* sref_ptr = jau::castInstance<JNIAdapterStatusListener>(nativeInstance); + delete sref_ptr; } - BTAdapter *adapter = jau::getJavaUplinkObject<BTAdapter>(env, obj); - jau::JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} - BTDevice * deviceOwnerAndMatchRef = nullptr; - if( nullptr != jdeviceOwnerAndMatch ) { - deviceOwnerAndMatchRef = jau::getJavaUplinkObject<BTDevice>(env, jdeviceOwnerAndMatch); - jau::JavaGlobalObj::check(deviceOwnerAndMatchRef->getJavaObject(), E_FILE_LINE); +void Java_jau_direct_1bt_DBTAdapter_deleteImpl(JNIEnv *env, jobject obj, jlong nativeInstance) +{ + (void)obj; + try { + jau::shared_ptr_ref<BTAdapter> adapter(nativeInstance, false /* throw_on_nullptr */); // hold copy until done + if( nullptr != adapter.pointer() ) { + if( !adapter.is_null() ) { + DBG_PRINT("Java_jau_direct_1bt_DBTAdapter_deleteImpl (w/ close) %s", adapter->toString().c_str()); + adapter->close(); + } else { + DBG_PRINT("Java_jau_direct_1bt_DBTAdapter_deleteImpl null reference"); + } + std::shared_ptr<BTAdapter>* ref_ptr = jau::castInstance<BTAdapter>(nativeInstance); + delete ref_ptr; + } else { + DBG_PRINT("Java_jau_direct_1bt_DBTAdapter_deleteImpl null reference store"); } + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} - jclass listenerClazz = jau::search_class(env, statusListener); - jmethodID mStatusListenerNotifyDeleted = jau::search_method(env, listenerClazz, "notifyDeleted", "()V", false); +jboolean Java_jau_direct_1bt_DBTAdapter_addStatusListenerImpl(JNIEnv *env, jobject obj, jobject jdeviceOwnerAndMatch, jobject jstatusListener) +{ + try { + jau::shared_ptr_ref<BTAdapter> adapter(env, obj); // hold until done + jau::JavaAnonRef adapter_java = adapter->getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(adapter_java, E_FILE_LINE); - std::shared_ptr<AdapterStatusListener> l = - std::shared_ptr<AdapterStatusListener>( new JNIAdapterStatusListener(env, adapter, - listenerClazz, statusListener, mStatusListenerNotifyDeleted, deviceOwnerAndMatchRef) ); + jau::shared_ptr_ref<JNIAdapterStatusListener> asl(env, jstatusListener); // hold until done + jau::JavaAnonRef asl_java = asl->getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); - env->DeleteLocalRef(listenerClazz); + jau::shared_ptr_ref<BTDevice> deviceOwnerAndMatchRef(env, jdeviceOwnerAndMatch, false /* throw_on_nullptr */); + if( !deviceOwnerAndMatchRef.is_null() ) { + jau::JavaGlobalObj::check(deviceOwnerAndMatchRef->getJavaObject(), E_FILE_LINE); + } - jau::setInstance(env, statusListener, l.get()); bool addRes; - if( nullptr != deviceOwnerAndMatchRef ) { - addRes = adapter->addStatusListener( *deviceOwnerAndMatchRef, l ); + if( !deviceOwnerAndMatchRef.is_null() ) { + addRes = adapter->addStatusListener( deviceOwnerAndMatchRef.shared_ptr(), asl.shared_ptr() ); } else { - addRes = adapter->addStatusListener( l ); + addRes = adapter->addStatusListener( asl.shared_ptr() ); } if( addRes ) { return JNI_TRUE; } - jau::clearInstance(env, statusListener); - ERR_PRINT("JNIAdapterStatusListener::addStatusListener: FAILED: %s", l->toString().c_str()); + ERR_PRINT("JNIAdapterStatusListener::addStatusListener: FAILED: %s", asl->toString().c_str()); } catch(...) { - jau::clearInstance(env, statusListener); rethrow_and_raise_java_exception(env); } return JNI_FALSE; } -jboolean Java_jau_direct_1bt_DBTAdapter_removeStatusListenerImpl(JNIEnv *env, jobject obj, jobject statusListener) +jboolean Java_jau_direct_1bt_DBTAdapter_removeStatusListenerImpl(JNIEnv *env, jobject obj, jobject jstatusListener) { try { - if( nullptr == statusListener ) { - throw jau::IllegalArgumentException("statusListener is null", E_FILE_LINE); - } - JNIAdapterStatusListener * pre = jau::getInstanceUnchecked<JNIAdapterStatusListener>(env, statusListener); - if( nullptr == pre ) { - DBG_PRINT("JNIAdapterStatusListener::removeStatusListener: statusListener's nativeInstance is null, not in use"); - return false; - } - jau::clearInstance(env, statusListener); + jau::shared_ptr_ref<BTAdapter> adapter(env, obj); // hold until done + jau::JavaAnonRef adapter_java = adapter->getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(adapter_java, E_FILE_LINE); - BTAdapter *adapter = jau::getJavaUplinkObject<BTAdapter>(env, obj); - jau::JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + jau::shared_ptr_ref<JNIAdapterStatusListener> asl(env, jstatusListener); // hold until done + jau::JavaAnonRef asl_java = asl->getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(asl_java, E_FILE_LINE); - if( ! adapter->removeStatusListener( pre ) ) { - WARN_PRINT("Failed to remove statusListener with nativeInstance: %p at %s", pre, adapter->toString().c_str()); + if( ! adapter->removeStatusListener( asl.shared_ptr() ) ) { + WARN_PRINT("Failed to remove statusListener with nativeInstance: %p at %s", asl.shared_ptr().get(), adapter->toString().c_str()); return false; } - { - JNIAdapterStatusListener * post = jau::getInstanceUnchecked<JNIAdapterStatusListener>(env, statusListener); - if( nullptr != post ) { - ERR_PRINT("JNIAdapterStatusListener::removeStatusListener: statusListener's nativeInstance not null post native removal"); - return false; - } - } return true; } catch(...) { rethrow_and_raise_java_exception(env); @@ -495,9 +555,9 @@ jboolean Java_jau_direct_1bt_DBTAdapter_removeStatusListenerImpl(JNIEnv *env, jo jint Java_jau_direct_1bt_DBTAdapter_removeAllStatusListenerImpl(JNIEnv *env, jobject obj) { try { - BTAdapter *adapter = jau::getJavaUplinkObject<BTAdapter>(env, obj); - jau::JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); - + jau::shared_ptr_ref<BTAdapter> adapter(env, obj); // hold until done + jau::JavaAnonRef adapter_java = adapter->getJavaObject(); // hold until done! + jau::JavaGlobalObj::check(adapter_java, E_FILE_LINE); return adapter->removeAllStatusListener(); } catch(...) { rethrow_and_raise_java_exception(env); diff --git a/java/org/direct_bt/AdapterStatusListener.java b/java/org/direct_bt/AdapterStatusListener.java index c9d33001..fa6c55df 100644 --- a/java/org/direct_bt/AdapterStatusListener.java +++ b/java/org/direct_bt/AdapterStatusListener.java @@ -25,6 +25,8 @@ package org.direct_bt; +import jau.direct_bt.DBTNativeDownlink; + /** * {@link BTAdapter} status listener for remote {@link BTDevice} discovery events: Added, updated and removed; * as well as for certain {@link BTAdapter} events. @@ -53,18 +55,15 @@ package org.direct_bt; * </p> * @since 2.0.0 */ -public abstract class AdapterStatusListener { - @SuppressWarnings("unused") - private volatile long nativeInstance; - - /** - * Called from native JNIAdapterStatusListener dtor - * i.e. native instance destructed in native land. - */ - @SuppressWarnings("unused") - private final void notifyDeleted() { - nativeInstance = 0; +public abstract class AdapterStatusListener extends DBTNativeDownlink { + protected AdapterStatusListener() { + super(); // pending native ctor + initDownlink(ctorImpl()); } + private native long ctorImpl(); + + @Override + protected native void deleteImpl(long nativeInstance); /** * {@link BTAdapter} setting(s) changed. diff --git a/src/direct_bt/BTAdapter.cpp b/src/direct_bt/BTAdapter.cpp index 1de057ab..8725c684 100644 --- a/src/direct_bt/BTAdapter.cpp +++ b/src/direct_bt/BTAdapter.cpp @@ -917,7 +917,7 @@ bool BTAdapter::removeDeviceFromWhitelist(const BDAddressAndType & addressAndTyp static jau::cow_darray<impl::StatusListenerPair>::equal_comparator _adapterStatusListenerRefEqComparator = [](const impl::StatusListenerPair &a, const impl::StatusListenerPair &b) -> bool { return *a.listener == *b.listener; }; -bool BTAdapter::addStatusListener(std::shared_ptr<AdapterStatusListener> l) { +bool BTAdapter::addStatusListener(const AdapterStatusListenerRef& l) { if( nullptr == l ) { throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE); } @@ -933,16 +933,14 @@ bool BTAdapter::addStatusListener(std::shared_ptr<AdapterStatusListener> l) { return added; } -bool BTAdapter::addStatusListener(const BTDevice& d, std::shared_ptr<AdapterStatusListener> l) { +bool BTAdapter::addStatusListener(const BTDeviceRef& d, const AdapterStatusListenerRef& l) { if( nullptr == l ) { throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE); } - - BTDeviceRef sd = getSharedDevice(d); - if( nullptr == sd ) { - throw jau::IllegalArgumentException("Device not shared: "+d.toString(), E_FILE_LINE); + if( nullptr == d ) { + throw jau::IllegalArgumentException("Device is null", E_FILE_LINE); } - const bool added = statusListenerList.push_back_unique(impl::StatusListenerPair{l, sd}, + const bool added = statusListenerList.push_back_unique(impl::StatusListenerPair{l, d}, _adapterStatusListenerRefEqComparator); if( added ) { sendAdapterSettingsInitial(*l, jau::getCurrentMilliseconds()); @@ -954,7 +952,11 @@ bool BTAdapter::addStatusListener(const BTDevice& d, std::shared_ptr<AdapterStat return added; } -bool BTAdapter::removeStatusListener(std::shared_ptr<AdapterStatusListener> l) { +bool BTAdapter::addStatusListener(const BTDevice& d, const AdapterStatusListenerRef& l) { + return addStatusListener(getSharedDevice(d), l); +} + +bool BTAdapter::removeStatusListener(const AdapterStatusListenerRef& l) { if( nullptr == l ) { throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE); } diff --git a/trial/direct_bt/dbt_client00.hpp b/trial/direct_bt/dbt_client00.hpp index e0fe8145..36508f8a 100644 --- a/trial/direct_bt/dbt_client00.hpp +++ b/trial/direct_bt/dbt_client00.hpp @@ -222,7 +222,7 @@ class DBTClient00 : public DBTClientTest { dc.detach(); } - std::string toString() const override { + std::string toString() const noexcept override { return "Client MyAdapterStatusListener[this "+to_hexstring(this)+"]"; } diff --git a/trial/direct_bt/dbt_client01.hpp b/trial/direct_bt/dbt_client01.hpp index 2f86976d..99e93b44 100644 --- a/trial/direct_bt/dbt_client01.hpp +++ b/trial/direct_bt/dbt_client01.hpp @@ -239,7 +239,7 @@ class DBTClient01 : public DBTClientTest { dc.detach(); } - std::string toString() const override { + std::string toString() const noexcept override { return "Client MyAdapterStatusListener[this "+to_hexstring(this)+"]"; } diff --git a/trial/direct_bt/dbt_client_server1x.hpp b/trial/direct_bt/dbt_client_server1x.hpp index 9952042a..012d5234 100644 --- a/trial/direct_bt/dbt_client_server1x.hpp +++ b/trial/direct_bt/dbt_client_server1x.hpp @@ -131,7 +131,7 @@ class DBTClientServer1x { fprintf_td(stderr, "XXXXXX Client Ready: %s\n", device->toString(true).c_str()); } - std::string toString() const override { return "DBTClientServer1x::Client"; } + std::string toString() const noexcept override { return "DBTClientServer1x::Client"; } }; std::shared_ptr<AdapterStatusListener> clientAdapterStatusListener = std::make_shared<MyAdapterStatusListener>(*this); REQUIRE( true == client->getAdapter()->addStatusListener(clientAdapterStatusListener) ); diff --git a/trial/direct_bt/dbt_server00.hpp b/trial/direct_bt/dbt_server00.hpp index 67ce53f2..4bd990f7 100644 --- a/trial/direct_bt/dbt_server00.hpp +++ b/trial/direct_bt/dbt_server00.hpp @@ -297,7 +297,7 @@ class DBTServer00 : public DBTServerTest { (void)timestamp; } - std::string toString() const override { + std::string toString() const noexcept override { return "Server MyAdapterStatusListener[this "+to_hexstring(this)+"]"; } diff --git a/trial/direct_bt/dbt_server01.hpp b/trial/direct_bt/dbt_server01.hpp index 7dfe07ed..1b652c50 100644 --- a/trial/direct_bt/dbt_server01.hpp +++ b/trial/direct_bt/dbt_server01.hpp @@ -301,7 +301,7 @@ class DBTServer01 : public DBTServerTest { (void)timestamp; } - std::string toString() const override { + std::string toString() const noexcept override { return "Server MyAdapterStatusListener[this "+to_hexstring(this)+"]"; } |