summaryrefslogtreecommitdiffstats
path: root/java/jni
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-05-17 09:58:12 +0200
committerSven Gothel <[email protected]>2020-05-17 09:58:12 +0200
commit525cb093d2ead1be30cf860d096129a8ec7146bf (patch)
tree0bf0e063fc9dfbe7b97cf45301dfe1b859b1e5cf /java/jni
parentb12a3e3adf8159a0c252cee67beae35e1b5b879f (diff)
Working GATT Java Side; GATT Types made fully functional for user to avoid 'technical' GATTHandler
GATT Types made fully functional for user to avoid 'technical' GATTHandler (C++) > GATTService, GATTCharacteristic, GATTDescriptor -- Reside in their own respective files -- Added semantic methods (readValue(), ..) implemented using DBTDevice -> GATTHandler -- GATTDescriptor owns its value -- GATTHandler setSendIndicationConfirmation(..) defaults to true -- Allow user to cirvumvent using GATTHandler manually completely, device 1--*> services 1--*> characteristics 1--*> descriptor -- C++ GATT types aligned 1:1 to TinyB (Java) > Merged GATTIndicationListener + GATTNotificationListener -> GATTCharacteristicListener -- Simplifying usage, unifying notification and indication -- Now using a list of shared_ptr<GATTCharacteristicListener> allowing multiple actors instead of just a one shot entry. Similar to AdapterStatusListener, we utilize this also on the Java side to implement the TinyB notifications. See dbt_scanner00.cpp: Simplified high-level usage. See dbt_scanner01.cpp: Lower-level technical usage w/ GATTHandler. +++ > Simplified more names > Removed redundancy in listener callbacks, -- don't pass adapter when device is already given. device <*--1> adapter -- don't pass GATT handle explicitly when characteristic is passed > Comparison of all GATT types are done by their respective unique handle Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle) > GATTHandler: Own L2CAPComm instance directly, instead of shared_ptr. > JNIMem: Added JNICriticalArray class for RAII style release ++++ ++++ Working GATT Java Side > All toString() methods return the C++ toString() via JNI for better representation. > DBTDevice.java/JNI: Resolved the odd 'adapter' reference issue: -- Was not passing the jobject of DBTAdapter, but its shared container refeference ;-) > All GATT types receive their GATT handler for equal test and identity @ ctor > GATT read/write Value update the cached value as well as issue the notifyValue on change, including GATTCharacteristic notification/indication listener
Diffstat (limited to 'java/jni')
-rw-r--r--java/jni/JNIMem.hpp85
-rw-r--r--java/jni/direct_bt/DBTAdapter.cxx171
-rw-r--r--java/jni/direct_bt/DBTDevice.cxx220
-rw-r--r--java/jni/direct_bt/DBTGattCharacteristic.cxx142
-rw-r--r--java/jni/direct_bt/DBTGattDescriptor.cxx81
-rw-r--r--java/jni/direct_bt/DBTGattService.cxx36
-rw-r--r--java/jni/direct_bt/DBTManager.cxx8
-rw-r--r--java/jni/direct_bt/DBTNativeDownlink.cxx2
8 files changed, 628 insertions, 117 deletions
diff --git a/java/jni/JNIMem.hpp b/java/jni/JNIMem.hpp
index fee3348b..a6a1dbb4 100644
--- a/java/jni/JNIMem.hpp
+++ b/java/jni/JNIMem.hpp
@@ -67,8 +67,10 @@ public:
extern thread_local JNIEnvContainer jni_env;
/*
- * This class provides a lifetime-managed GlobalRef variable, which is automatically
- * deleted when it goes out of scope.
+ * This class provides a lifetime-managed GlobalRef variable,
+ * which is automatically deleted when it goes out of scope.
+ *
+ * RAII-style acquire and relinquish via destructor
*/
class JNIGlobalRef {
private:
@@ -110,5 +112,84 @@ public:
{ return !( *this == rhs ); }
};
+/*
+ * This class provides a lifetime-managed 'PrimitiveArrayCritical' pinned heap,
+ * which is automatically released when it goes out of scope.
+ *
+ * RAII-style acquire and relinquish via destructor
+ */
+template <typename T>
+class JNICriticalArray {
+public:
+ enum Mode : jint {
+ /** Like default 0: If 'isCopy': Update the java array data with the copy and free the copy. */
+ UPDATE_AND_RELEASE = 0,
+
+ /** Like JNI_COMMIT: If 'isCopy': Update the java array data with the copy, but do not free the copy. */
+ UPDATE_NO_RELEASE = JNI_COMMIT,
+
+ /** Like default JNI_ABORT: If 'isCopy': Do not update the java array data with the copy, but free the copy. */
+ NO_UPDATE_AND_RELEASE = JNI_ABORT,
+ };
+
+private:
+ JNIEnv *env;
+ Mode mode = UPDATE_AND_RELEASE;
+ jbyteArray jarray = nullptr;
+ T* narray = nullptr;
+ jboolean isCopy = false;
+
+public:
+ JNICriticalArray(JNIEnv *env) : env(env) {}
+
+ JNICriticalArray(const JNICriticalArray &o) = delete;
+ JNICriticalArray(JNICriticalArray &&o) = delete;
+ JNICriticalArray& operator=(const JNICriticalArray &o) = delete;
+ JNICriticalArray& operator=(JNICriticalArray &&o) = delete;
+
+ /**
+ * Release the acquired primitive array, RAII style.
+ */
+ ~JNICriticalArray() {
+ release();
+ }
+
+ /**
+ * Manual release of the acquired primitive array,
+ * usually one likes to simply do this via the destructor, RAII style.
+ */
+ void release() {
+ if( nullptr != narray ) {
+ env->ReleasePrimitiveArrayCritical(jarray, narray, mode);
+ this->jarray = nullptr;
+ this->narray = nullptr;
+ this->env = nullptr;
+ }
+ }
+
+ /**
+ * Acquired the primitive array.
+ */
+ T* get(jbyteArray jarray, Mode mode=UPDATE_AND_RELEASE) {
+ if( nullptr == jarray ) {
+ return nullptr;
+ }
+ T* narray = static_cast<T*>( env->GetPrimitiveArrayCritical(jarray, &isCopy) );
+ if( nullptr != narray ) {
+ this->mode = mode;
+ this->jarray = jarray;
+ this->narray = narray;
+ return narray;
+ }
+ return nullptr;
+ }
+
+ /**
+ * Returns true if the primitive array had been acquired
+ * and the JVM utilizes a copy of the underlying java array.
+ */
+ bool getIsCopy() const { return isCopy; }
+};
+
#endif /* JNIMEM__HPP_ */
diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx
index 581ea15d..168a407e 100644
--- a/java/jni/direct_bt/DBTAdapter.cxx
+++ b/java/jni/direct_bt/DBTAdapter.cxx
@@ -25,7 +25,7 @@
#include "direct_bt_tinyb_DBTAdapter.h"
-#define VERBOSE_ON 1
+// #define VERBOSE_ON 1
#include <dbt_debug.hpp>
#include "JNIMem.hpp"
@@ -42,10 +42,10 @@ static const std::string _eirDataTypeSetClassName("org/tinyb/EIRDataTypeSet");
static const std::string _eirDataTypeSetClazzCtorArgs("(I)V");
static const std::string _deviceClazzCtorArgs("(JLdirect_bt/tinyb/DBTAdapter;Ljava/lang/String;Ljava/lang/String;J)V");
static const std::string _adapterSettingsChangedMethodArgs("(Lorg/tinyb/BluetoothAdapter;Lorg/tinyb/AdapterSettings;Lorg/tinyb/AdapterSettings;Lorg/tinyb/AdapterSettings;J)V");
-static const std::string _deviceStatusMethodArgs("(Lorg/tinyb/BluetoothAdapter;Lorg/tinyb/BluetoothDevice;J)V");
-static const std::string _deviceStatusUpdateMethodArgs("(Lorg/tinyb/BluetoothAdapter;Lorg/tinyb/BluetoothDevice;JLorg/tinyb/EIRDataTypeSet;)V");
+static const std::string _deviceStatusMethodArgs("(Lorg/tinyb/BluetoothDevice;J)V");
+static const std::string _deviceStatusUpdateMethodArgs("(Lorg/tinyb/BluetoothDevice;JLorg/tinyb/EIRDataTypeSet;)V");
-class AdapterStatusCallbackListener : public DBTAdapterStatusListener {
+class JNIAdapterStatusListener : public AdapterStatusListener {
private:
/**
package org.tinyb;
@@ -54,12 +54,13 @@ class AdapterStatusCallbackListener : public DBTAdapterStatusListener {
public void adapterSettingsChanged(final BluetoothAdapter adapter,
final AdapterSetting oldmask, final AdapterSetting newmask,
final AdapterSetting changedmask, final long timestamp);
- public void deviceFound(final BluetoothAdapter adapter, final BluetoothDevice device, final long timestamp);
- public void deviceUpdated(final BluetoothAdapter adapter, final BluetoothDevice device, final long timestamp, final EIRDataType updateMask);
- public void deviceConnected(final BluetoothAdapter adapter, final BluetoothDevice device, final long timestamp);
- public void deviceDisconnected(final BluetoothAdapter adapter, final BluetoothDevice device, final long timestamp);
+ public void deviceFound(final BluetoothDevice device, final long timestamp);
+ public void deviceUpdated(final BluetoothDevice device, final long timestamp, final EIRDataType updateMask);
+ public void deviceConnected(final BluetoothDevice device, final long timestamp);
+ public void deviceDisconnected(final BluetoothDevice device, final long timestamp);
};
*/
+ const DBTDevice * deviceMatchRef;
std::shared_ptr<JavaAnonObj> adapterObjRef;
std::unique_ptr<JNIGlobalRef> adapterSettingsClazzRef;
jmethodID adapterSettingsClazzCtor;
@@ -69,7 +70,6 @@ class AdapterStatusCallbackListener : public DBTAdapterStatusListener {
jmethodID deviceClazzCtor;
jfieldID deviceClazzTSUpdateField;
std::unique_ptr<JNIGlobalRef> listenerObjRef;
- std::unique_ptr<JNIGlobalRef> listenerClazzRef;
jmethodID mAdapterSettingsChanged = nullptr;
jmethodID mDeviceFound = nullptr;
jmethodID mDeviceUpdated = nullptr;
@@ -78,10 +78,19 @@ class AdapterStatusCallbackListener : public DBTAdapterStatusListener {
public:
- AdapterStatusCallbackListener(JNIEnv *env, DBTAdapter *adapter, jobject statusListener) {
+ JNIAdapterStatusListener(JNIEnv *env, DBTAdapter *adapter, jobject statusListener, const DBTDevice * deviceMatchRef) {
adapterObjRef = adapter->getJavaObject();
JavaGlobalObj::check(adapterObjRef, E_FILE_LINE);
+ listenerObjRef = std::unique_ptr<JNIGlobalRef>(new JNIGlobalRef(statusListener));
+ jclass listenerClazz = search_class(env, listenerObjRef->getObject());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ if( nullptr == listenerClazz ) {
+ throw InternalError("AdapterStatusListener not found", E_FILE_LINE);
+ }
+
+ this->deviceMatchRef = deviceMatchRef;
+
// adapterSettingsClazzRef, adapterSettingsClazzCtor
{
jclass adapterSettingsClazz = search_class(env, _adapterSettingsClassName.c_str());
@@ -135,45 +144,40 @@ class AdapterStatusCallbackListener : public DBTAdapterStatusListener {
throw InternalError("DBTDevice::java_class field not found: "+DBTDevice::java_class()+".ts_update", E_FILE_LINE);
}
- listenerObjRef = std::unique_ptr<JNIGlobalRef>(new JNIGlobalRef(statusListener));
- {
- jclass listenerClazz = search_class(env, listenerObjRef->getObject());
- java_exception_check_and_throw(env, E_FILE_LINE);
- if( nullptr == listenerClazz ) {
- throw InternalError("AdapterStatusListener not found", E_FILE_LINE);
- }
- listenerClazzRef = std::unique_ptr<JNIGlobalRef>(new JNIGlobalRef(listenerClazz));
- env->DeleteLocalRef(listenerClazz);
- }
-
-
- mAdapterSettingsChanged = search_method(env, listenerClazzRef->getClass(), "adapterSettingsChanged", _adapterSettingsChangedMethodArgs.c_str(), false);
+ mAdapterSettingsChanged = search_method(env, listenerClazz, "adapterSettingsChanged", _adapterSettingsChangedMethodArgs.c_str(), false);
java_exception_check_and_throw(env, E_FILE_LINE);
if( nullptr == mAdapterSettingsChanged ) {
throw InternalError("AdapterStatusListener has no adapterSettingsChanged"+_adapterSettingsChangedMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE);
}
- mDeviceFound = search_method(env, listenerClazzRef->getClass(), "deviceFound", _deviceStatusMethodArgs.c_str(), false);
+ mDeviceFound = search_method(env, listenerClazz, "deviceFound", _deviceStatusMethodArgs.c_str(), false);
java_exception_check_and_throw(env, E_FILE_LINE);
if( nullptr == mDeviceFound ) {
throw InternalError("AdapterStatusListener has no deviceFound"+_deviceStatusMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE);
}
- mDeviceUpdated = search_method(env, listenerClazzRef->getClass(), "deviceUpdated", _deviceStatusUpdateMethodArgs.c_str(), false);
+ mDeviceUpdated = search_method(env, listenerClazz, "deviceUpdated", _deviceStatusUpdateMethodArgs.c_str(), false);
java_exception_check_and_throw(env, E_FILE_LINE);
if( nullptr == mDeviceUpdated ) {
throw InternalError("AdapterStatusListener has no deviceUpdated"+_deviceStatusMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE);
}
- mDeviceConnected = search_method(env, listenerClazzRef->getClass(), "deviceConnected", _deviceStatusMethodArgs.c_str(), false);
+ mDeviceConnected = search_method(env, listenerClazz, "deviceConnected", _deviceStatusMethodArgs.c_str(), false);
java_exception_check_and_throw(env, E_FILE_LINE);
if( nullptr == mDeviceConnected ) {
throw InternalError("AdapterStatusListener has no deviceConnected"+_deviceStatusMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE);
}
- mDeviceDisconnected = search_method(env, listenerClazzRef->getClass(), "deviceDisconnected", _deviceStatusMethodArgs.c_str(), false);
+ mDeviceDisconnected = search_method(env, listenerClazz, "deviceDisconnected", _deviceStatusMethodArgs.c_str(), false);
java_exception_check_and_throw(env, E_FILE_LINE);
if( nullptr == mDeviceDisconnected ) {
throw InternalError("AdapterStatusListener has no deviceDisconnected"+_deviceStatusMethodArgs+" method, for "+adapter->toString(), E_FILE_LINE);
}
}
+ bool matchDevice(const DBTDevice & device) override {
+ if( nullptr == deviceMatchRef ) {
+ return true;
+ }
+ return device == *deviceMatchRef;
+ }
+
void adapterSettingsChanged(DBTAdapter const &a, const AdapterSetting oldmask, const AdapterSetting newmask,
const AdapterSetting changedmask, const uint64_t timestamp) override {
JNIEnv *env = *jni_env;
@@ -188,34 +192,33 @@ class AdapterStatusCallbackListener : public DBTAdapterStatusListener {
#endif
(void)a;
jobject adapterSettingOld = env->NewObject(adapterSettingsClazzRef->getClass(), adapterSettingsClazzCtor, (jint)oldmask);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(adapterSettingOld, E_FILE_LINE);
jobject adapterSettingNew = env->NewObject(adapterSettingsClazzRef->getClass(), adapterSettingsClazzCtor, (jint)newmask);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(adapterSettingNew, E_FILE_LINE);
jobject adapterSettingChanged = env->NewObject(adapterSettingsClazzRef->getClass(), adapterSettingsClazzCtor, (jint)changedmask);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(adapterSettingChanged, E_FILE_LINE);
env->CallVoidMethod(listenerObjRef->getObject(), mAdapterSettingsChanged,
JavaGlobalObj::GetObject(adapterObjRef), adapterSettingOld, adapterSettingNew, adapterSettingChanged, (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
} catch(...) {
rethrow_and_raise_java_exception(env);
}
}
- void deviceFound(DBTAdapter const &a, std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
+ void deviceFound(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
JNIEnv *env = *jni_env;
try {
#ifdef VERBOSE_ON
fprintf(stderr, "****** Native Adapter Device FOUND__: %s\n", device->toString().c_str());
fprintf(stderr, "Status DBTAdapter:\n");
- fprintf(stderr, "%s\n", a.toString().c_str());
+ fprintf(stderr, "%s\n", device->getAdapter().toString().c_str());
#endif
- (void)a;
jobject jdevice;
std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject();
if( JavaGlobalObj::isValid(jDeviceRef) ) {
@@ -226,98 +229,91 @@ class AdapterStatusCallbackListener : public DBTAdapterStatusListener {
// Device(final long nativeInstance, final Adapter adptr, final String address, final String name)
const jstring addr = from_string_to_jstring(env, device->getAddressString());
const jstring name = from_string_to_jstring(env, device->getName());
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
jobject jDevice = env->NewObject(deviceClazzRef->getClass(), deviceClazzCtor,
- (jlong)device.get(), adapterObjRef, addr, name, (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ (jlong)device.get(), JavaGlobalObj::GetObject(adapterObjRef), addr, name, (jlong)timestamp);
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(jDevice, E_FILE_LINE);
std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject();
JavaGlobalObj::check(jDeviceRef, E_FILE_LINE);
jdevice = JavaGlobalObj::GetObject(jDeviceRef);
}
- env->CallVoidMethod(listenerObjRef->getObject(), mDeviceFound,
- JavaGlobalObj::GetObject(adapterObjRef), jdevice, (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ env->CallVoidMethod(listenerObjRef->getObject(), mDeviceFound, jdevice, (jlong)timestamp);
+ java_exception_check_and_throw(env, E_FILE_LINE);
} catch(...) {
rethrow_and_raise_java_exception(env);
}
}
- void deviceUpdated(DBTAdapter const &a, std::shared_ptr<DBTDevice> device, const uint64_t timestamp, const EIRDataType updateMask) override {
+ void deviceUpdated(std::shared_ptr<DBTDevice> device, const uint64_t timestamp, const EIRDataType updateMask) override {
JNIEnv *env = *jni_env;
try {
#ifdef VERBOSE_ON
fprintf(stderr, "****** Native Adapter Device UPDATED: %s of %s\n", direct_bt::eirDataMaskToString(updateMask).c_str(), device->toString().c_str());
fprintf(stderr, "Status DBTAdapter:\n");
- fprintf(stderr, "%s\n", a.toString().c_str());
+ fprintf(stderr, "%s\n", device->getAdapter().toString().c_str());
#endif
- (void)a;
std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject();
JavaGlobalObj::check(jDeviceRef, E_FILE_LINE);
env->SetLongField(JavaGlobalObj::GetObject(jDeviceRef), deviceClazzTSUpdateField, (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
jobject eirDataTypeSet = env->NewObject(eirDataTypeSetClazzRef->getClass(), eirDataTypeSetClazzCtor, (jint)updateMask);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(eirDataTypeSet, E_FILE_LINE);
- env->CallVoidMethod(listenerObjRef->getObject(), mDeviceUpdated,
- JavaGlobalObj::GetObject(adapterObjRef), JavaGlobalObj::GetObject(jDeviceRef), (jlong)timestamp, eirDataTypeSet);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ env->CallVoidMethod(listenerObjRef->getObject(), mDeviceUpdated, JavaGlobalObj::GetObject(jDeviceRef), (jlong)timestamp, eirDataTypeSet);
+ java_exception_check_and_throw(env, E_FILE_LINE);
} catch(...) {
rethrow_and_raise_java_exception(env);
}
}
- void deviceConnected(DBTAdapter const &a, std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
+ void deviceConnected(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
JNIEnv *env = *jni_env;
try {
#ifdef VERBOSE_ON
fprintf(stderr, "****** DBTAdapter Device CONNECTED: %s\n", device->toString().c_str());
fprintf(stderr, "Status DBTAdapter:\n");
- fprintf(stderr, "%s\n", a.toString().c_str());
+ fprintf(stderr, "%s\n", device->getAdapter().toString().c_str());
#endif
- (void)a;
std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject();
JavaGlobalObj::check(jDeviceRef, E_FILE_LINE);
env->SetLongField(JavaGlobalObj::GetObject(jDeviceRef), deviceClazzTSUpdateField, (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
- env->CallVoidMethod(listenerObjRef->getObject(), mDeviceConnected,
- JavaGlobalObj::GetObject(adapterObjRef), JavaGlobalObj::GetObject(jDeviceRef), (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ env->CallVoidMethod(listenerObjRef->getObject(), mDeviceConnected, JavaGlobalObj::GetObject(jDeviceRef), (jlong)timestamp);
+ java_exception_check_and_throw(env, E_FILE_LINE);
} catch(...) {
rethrow_and_raise_java_exception(env);
}
}
- void deviceDisconnected(DBTAdapter const &a, std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
+ void deviceDisconnected(std::shared_ptr<DBTDevice> device, const uint64_t timestamp) override {
JNIEnv *env = *jni_env;
try {
#ifdef VERBOSE_ON
fprintf(stderr, "****** DBTAdapter Device DISCONNECTED: %s\n", device->toString().c_str());
fprintf(stderr, "Status DBTAdapter:\n");
- fprintf(stderr, "%s\n", a.toString().c_str());
+ fprintf(stderr, "%s\n", device->getAdapter().toString().c_str());
#endif
- (void)a;
std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject();
JavaGlobalObj::check(jDeviceRef, E_FILE_LINE);
env->SetLongField(JavaGlobalObj::GetObject(jDeviceRef), deviceClazzTSUpdateField, (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
- env->CallVoidMethod(listenerObjRef->getObject(), mDeviceDisconnected,
- JavaGlobalObj::GetObject(adapterObjRef), JavaGlobalObj::GetObject(jDeviceRef), (jlong)timestamp);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ env->CallVoidMethod(listenerObjRef->getObject(), mDeviceDisconnected, JavaGlobalObj::GetObject(jDeviceRef), (jlong)timestamp);
+ java_exception_check_and_throw(env, E_FILE_LINE);
} catch(...) {
rethrow_and_raise_java_exception(env);
}
}
};
-jboolean Java_direct_1bt_tinyb_DBTAdapter_addStatusListener(JNIEnv *env, jobject obj, jobject statusListener)
+jboolean Java_direct_1bt_tinyb_DBTAdapter_addStatusListener(JNIEnv *env, jobject obj, jobject statusListener, jobject jdeviceMatch)
{
try {
if( nullptr == statusListener ) {
throw IllegalArgumentException("statusListener is null", E_FILE_LINE);
}
{
- AdapterStatusCallbackListener * pre =
- getObjectRef<AdapterStatusCallbackListener>(env, statusListener, "nativeInstance");
+ JNIAdapterStatusListener * pre =
+ getObjectRef<JNIAdapterStatusListener>(env, statusListener, "nativeInstance");
if( nullptr != pre ) {
WARN_PRINT("statusListener's nativeInstance not null, already in use");
return false;
@@ -326,8 +322,14 @@ jboolean Java_direct_1bt_tinyb_DBTAdapter_addStatusListener(JNIEnv *env, jobject
DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj);
JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE);
- std::shared_ptr<DBTAdapterStatusListener> l =
- std::shared_ptr<DBTAdapterStatusListener>( new AdapterStatusCallbackListener(env, adapter, statusListener) );
+ DBTDevice * deviceMatchRef = nullptr;
+ if( nullptr != jdeviceMatch ) {
+ deviceMatchRef = getInstance<DBTDevice>(env, jdeviceMatch);
+ JavaGlobalObj::check(deviceMatchRef->getJavaObject(), E_FILE_LINE);
+ }
+
+ std::shared_ptr<AdapterStatusListener> l =
+ std::shared_ptr<AdapterStatusListener>( new JNIAdapterStatusListener(env, adapter, statusListener, deviceMatchRef) );
if( adapter->addStatusListener( l ) ) {
setInstance(env, statusListener, l.get());
@@ -345,20 +347,20 @@ jboolean Java_direct_1bt_tinyb_DBTAdapter_removeStatusListener(JNIEnv *env, jobj
if( nullptr == statusListener ) {
throw IllegalArgumentException("statusListener is null", E_FILE_LINE);
}
- const AdapterStatusCallbackListener * pre =
- getObjectRef<AdapterStatusCallbackListener>(env, statusListener, "nativeInstance");
+ const JNIAdapterStatusListener * pre =
+ getObjectRef<JNIAdapterStatusListener>(env, statusListener, "nativeInstance");
if( nullptr == pre ) {
WARN_PRINT("statusListener's nativeInstance is null, not in use");
return false;
}
- setObjectRef<AdapterStatusCallbackListener>(env, statusListener, nullptr, "nativeInstance");
+ setObjectRef<JNIAdapterStatusListener>(env, statusListener, nullptr, "nativeInstance");
DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj);
JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE);
- // set our callback discovery listener.
if( ! adapter->removeStatusListener( pre ) ) {
- throw InternalError("Failed to remove statusListener with nativeInstance", E_FILE_LINE);
+ WARN_PRINT("Failed to remove statusListener with nativeInstance: %p at %s", pre, adapter->toString().c_str());
+ return false;
}
return true;
} catch(...) {
@@ -367,6 +369,29 @@ jboolean Java_direct_1bt_tinyb_DBTAdapter_removeStatusListener(JNIEnv *env, jobj
return JNI_FALSE;
}
+jint Java_direct_1bt_tinyb_DBTAdapter_removeAllStatusListener(JNIEnv *env, jobject obj) {
+ try {
+ DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj);
+ JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE);
+
+ return adapter->removeAllStatusListener();
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return 0;
+}
+
+jstring Java_direct_1bt_tinyb_DBTAdapter_toStringImpl(JNIEnv *env, jobject obj) {
+ try {
+ DBTAdapter *nativePtr = getInstance<DBTAdapter>(env, obj);
+ JavaGlobalObj::check(nativePtr->getJavaObject(), E_FILE_LINE);
+ return from_string_to_jstring(env, nativePtr->toString());
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
jboolean Java_direct_1bt_tinyb_DBTAdapter_openImpl(JNIEnv *env, jobject obj) {
try {
DBTAdapter *adapter = getInstance<DBTAdapter>(env, obj);
@@ -572,7 +597,7 @@ jobject Java_direct_1bt_tinyb_DBTAdapter_connectDevice(JNIEnv *env, jobject obj,
std::shared_ptr<JavaAnonObj> jDeviceRef = device->getJavaObject();
JavaGlobalObj::check(jDeviceRef, E_FILE_LINE);
- device->defaultConnect();
+ device->connectHCIDefault();
return JavaGlobalObj::GetObject(jDeviceRef);
}
} catch(...) {
diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx
index 9d5d9e1f..cb249d4b 100644
--- a/java/jni/direct_bt/DBTDevice.cxx
+++ b/java/jni/direct_bt/DBTDevice.cxx
@@ -24,9 +24,8 @@
*/
#include "direct_bt_tinyb_DBTDevice.h"
-#include "direct_bt_tinyb_DBTAdapter.h"
-#define VERBOSE_ON 1
+// #define VERBOSE_ON 1
#include <dbt_debug.hpp>
#include "JNIMem.hpp"
@@ -38,6 +37,113 @@
using namespace direct_bt;
+static const std::string _notificationReceivedMethodArgs("(Lorg/tinyb/BluetoothGattCharacteristic;[BJ)V");
+static const std::string _indicationReceivedMethodArgs("(Lorg/tinyb/BluetoothGattCharacteristic;[BJZ)V");
+
+class JNICharacteristicListener : public GATTCharacteristicListener {
+ private:
+ /**
+ package org.tinyb;
+
+ public abstract class GATTCharacteristicListener {
+ long nativeInstance;
+
+ public void notificationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp) {
+ }
+
+ public void indicationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp,
+ final boolean confirmationSent) {
+ }
+
+ };
+ */
+ const GATTCharacteristic * characteristicMatchRef;
+ std::shared_ptr<JavaAnonObj> deviceObjRef;
+ std::unique_ptr<JNIGlobalRef> listenerObjRef;
+ jmethodID mNotificationReceived = nullptr;
+ jmethodID mIndicationReceived = nullptr;
+
+ public:
+
+ JNICharacteristicListener(JNIEnv *env, DBTDevice *device, jobject listener, const GATTCharacteristic * characteristicMatchRef) {
+ deviceObjRef = device->getJavaObject();
+ JavaGlobalObj::check(deviceObjRef, E_FILE_LINE);
+
+ listenerObjRef = std::unique_ptr<JNIGlobalRef>(new JNIGlobalRef(listener));
+ jclass listenerClazz = search_class(env, listenerObjRef->getObject());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ if( nullptr == listenerClazz ) {
+ throw InternalError("CharacteristicListener not found", E_FILE_LINE);
+ }
+
+ this->characteristicMatchRef = characteristicMatchRef;
+
+ mNotificationReceived = search_method(env, listenerClazz, "notificationReceived", _notificationReceivedMethodArgs.c_str(), false);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ if( nullptr == mNotificationReceived ) {
+ throw InternalError("GATTCharacteristicListener 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("GATTCharacteristicListener has no indicationReceived"+_indicationReceivedMethodArgs+" method, for "+device->toString(), E_FILE_LINE);
+ }
+ }
+
+ bool match(const GATTCharacteristic & characteristic) override {
+ if( nullptr == characteristicMatchRef ) {
+ return true;
+ }
+ return characteristic == *characteristicMatchRef;
+ }
+
+ void notificationReceived(GATTCharacteristicRef charDecl,
+ std::shared_ptr<TROOctets> charValue, const uint64_t timestamp) override {
+ JNIEnv *env = *jni_env;
+ try {
+ JavaGlobalObj::check(charDecl->getJavaObject(), E_FILE_LINE);
+ jobject jCharDecl = JavaGlobalObj::GetObject(charDecl->getJavaObject());
+
+ const size_t value_size = charValue->getSize();
+ jbyteArray jvalue = env->NewByteArray((jsize)value_size);
+ env->SetByteArrayRegion(jvalue, 0, (jsize)value_size, (const jbyte *)charValue->get_ptr());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+
+
+ env->CallVoidMethod(listenerObjRef->getObject(), mNotificationReceived,
+ jCharDecl, jvalue, (jlong)timestamp);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ }
+
+ void indicationReceived(GATTCharacteristicRef charDecl,
+ std::shared_ptr<TROOctets> charValue, const uint64_t timestamp,
+ const bool confirmationSent) override {
+ JNIEnv *env = *jni_env;
+ try {
+ JavaGlobalObj::check(charDecl->getJavaObject(), E_FILE_LINE);
+ jobject jCharDecl = JavaGlobalObj::GetObject(charDecl->getJavaObject());
+
+ const size_t value_size = charValue->getSize();
+ jbyteArray jvalue = env->NewByteArray((jsize)value_size);
+ env->SetByteArrayRegion(jvalue, 0, (jsize)value_size, (const jbyte *)charValue->get_ptr());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+
+
+ env->CallVoidMethod(listenerObjRef->getObject(), mIndicationReceived,
+ jCharDecl, jvalue, (jlong)timestamp, (jboolean)confirmationSent);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ }
+};
+
+
void Java_direct_1bt_tinyb_DBTDevice_initImpl(JNIEnv *env, jobject obj)
{
try {
@@ -48,47 +154,107 @@ void Java_direct_1bt_tinyb_DBTDevice_initImpl(JNIEnv *env, jobject obj)
}
}
-jboolean Java_direct_1bt_tinyb_DBTDevice_addStatusListener(JNIEnv *env, jobject obj, jobject statusListener)
-{
+jstring Java_direct_1bt_tinyb_DBTDevice_toStringImpl(JNIEnv *env, jobject obj) {
try {
+ DBTDevice *nativePtr = getInstance<DBTDevice>(env, obj);
+ JavaGlobalObj::check(nativePtr->getJavaObject(), E_FILE_LINE);
+ return from_string_to_jstring(env, nativePtr->toString());
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
+jboolean Java_direct_1bt_tinyb_DBTDevice_addCharacteristicListener(JNIEnv *env, jobject obj, jobject listener, jobject jcharacteristicMatch) {
+ try {
+ if( nullptr == listener ) {
+ throw IllegalArgumentException("characteristicListener is null", E_FILE_LINE);
+ }
+ {
+ JNICharacteristicListener * pre =
+ getObjectRef<JNICharacteristicListener>(env, listener, "nativeInstance");
+ if( nullptr != pre ) {
+ WARN_PRINT("characteristicListener's nativeInstance not null, already in use");
+ return false;
+ }
+ }
DBTDevice *device = getInstance<DBTDevice>(env, obj);
JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
+ std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
+ if( nullptr == gatt ) {
+ throw IllegalStateException("Characteristic's device GATTHandle not connected: "+ device->toString(), E_FILE_LINE);
+ }
- DBTAdapter * adapter = const_cast<DBTAdapter *>( &device->getAdapter() );
- std::shared_ptr<JavaAnonObj> adapterObjRef = adapter->getJavaObject();
- JavaGlobalObj::check(adapterObjRef, E_FILE_LINE);
- jobject jadapter = JavaGlobalObj::GetObject(adapterObjRef);
+ GATTCharacteristic * characteristicMatchRef = nullptr;
+ if( nullptr != jcharacteristicMatch ) {
+ characteristicMatchRef = getInstance<GATTCharacteristic>(env, jcharacteristicMatch);
+ JavaGlobalObj::check(characteristicMatchRef->getJavaObject(), E_FILE_LINE);
+ }
- return Java_direct_1bt_tinyb_DBTAdapter_addStatusListener(env, jadapter, statusListener);
+ std::shared_ptr<GATTCharacteristicListener> l =
+ std::shared_ptr<GATTCharacteristicListener>( new JNICharacteristicListener(env, device, listener, characteristicMatchRef) );
+
+ if( gatt->addCharacteristicListener(l) ) {
+ setInstance(env, listener, l.get());
+ return JNI_TRUE;
+ }
} catch(...) {
rethrow_and_raise_java_exception(env);
}
return JNI_FALSE;
}
-jboolean Java_direct_1bt_tinyb_DBTDevice_removeStatusListener(JNIEnv *env, jobject obj, jobject statusListener)
-{
+jboolean Java_direct_1bt_tinyb_DBTDevice_removeCharacteristicListener(JNIEnv *env, jobject obj, jobject statusListener) {
try {
+ if( nullptr == statusListener ) {
+ throw IllegalArgumentException("characteristicListener is null", E_FILE_LINE);
+ }
+ JNICharacteristicListener * pre =
+ getObjectRef<JNICharacteristicListener>(env, statusListener, "nativeInstance");
+ if( nullptr == pre ) {
+ WARN_PRINT("characteristicListener's nativeInstance is null, not in use");
+ return false;
+ }
+ setObjectRef<JNICharacteristicListener>(env, statusListener, nullptr, "nativeInstance");
+
DBTDevice *device = getInstance<DBTDevice>(env, obj);
JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
+ std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
+ if( nullptr == gatt ) {
+ throw IllegalStateException("Characteristic's device GATTHandle not connected: "+ device->toString(), E_FILE_LINE);
+ }
- DBTAdapter * adapter = const_cast<DBTAdapter *>( &device->getAdapter() );
- std::shared_ptr<JavaAnonObj> adapterObjRef = adapter->getJavaObject();
- JavaGlobalObj::check(adapterObjRef, E_FILE_LINE);
- jobject jadapter = JavaGlobalObj::GetObject(adapterObjRef);
-
- return Java_direct_1bt_tinyb_DBTAdapter_removeStatusListener(env, jadapter, statusListener);
+ if( ! gatt->removeCharacteristicListener(pre) ) {
+ WARN_PRINT("Failed to remove characteristicListener with nativeInstance: %p at %s", pre, device->toString().c_str());
+ return false;
+ }
+ return true;
} catch(...) {
rethrow_and_raise_java_exception(env);
}
return JNI_FALSE;
}
+jint Java_direct_1bt_tinyb_DBTDevice_removeAllCharacteristicListener(JNIEnv *env, jobject obj) {
+ try {
+ DBTDevice *device = getInstance<DBTDevice>(env, obj);
+ JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
+ std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
+ if( nullptr == gatt ) {
+ throw IllegalStateException("Characteristic's device GATTHandle not connected: "+ device->toString(), E_FILE_LINE);
+ }
+
+ return gatt->removeAllCharacteristicListener();
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return 0;
+}
+
void Java_direct_1bt_tinyb_DBTDevice_deleteImpl(JNIEnv *env, jobject obj)
{
try {
DBTDevice *device = getInstance<DBTDevice>(env, obj);
- JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
device->remove();
// No delete: DBTDevice instance owned by DBTAdapter
} catch(...) {
@@ -125,8 +291,8 @@ jboolean Java_direct_1bt_tinyb_DBTDevice_connectImpl(JNIEnv *env, jobject obj)
try {
DBTDevice *device = getInstance<DBTDevice>(env, obj);
JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
- uint16_t chandle = device->defaultConnect();
- if( 0 == chandle ) {
+ uint16_t hciHandle = device->connectHCIDefault();
+ if( 0 == hciHandle ) {
return JNI_FALSE;
}
std::shared_ptr<GATTHandler> gatt = device->connectGATT();
@@ -158,7 +324,7 @@ jboolean Java_direct_1bt_tinyb_DBTDevice_connectImpl(JNIEnv *env, jobject obj)
// getter
//
-static const std::string _serviceClazzCtorArgs("(JLorg/tinyb/BluetoothDevice;ZLjava/lang/String;)V");
+static const std::string _serviceClazzCtorArgs("(JLdirect_bt/tinyb/DBTDevice;ZLjava/lang/String;SS)V");
jobject Java_direct_1bt_tinyb_DBTDevice_getServices(JNIEnv *env, jobject obj) {
try {
@@ -168,7 +334,8 @@ jobject Java_direct_1bt_tinyb_DBTDevice_getServices(JNIEnv *env, jobject obj) {
std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
if( nullptr != gatt ) {
std::vector<std::shared_ptr<GATTService>> & services = gatt->getServices();
- // DBTGattService(final long nativeInstance, final BluetoothDevice device, final boolean isPrimary, final String uuid)
+ // DBTGattService(final long nativeInstance, final DBTDevice device, final boolean isPrimary,
+ // final String type_uuid, final short handleStart, final short handleEnd)
std::function<jobject(JNIEnv*, jclass, jmethodID, GATTService*)> ctor_service =
[](JNIEnv *env, jclass clazz, jmethodID clazz_ctor, GATTService *service)->jobject {
@@ -177,10 +344,11 @@ jobject Java_direct_1bt_tinyb_DBTDevice_getServices(JNIEnv *env, jobject obj) {
jobject jdevice = JavaGlobalObj::GetObject(service->device->getJavaObject());
const jboolean isPrimary = service->isPrimary;
const jstring uuid = from_string_to_jstring(env, service->type->toString());
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
- jobject jservice = env->NewObject(clazz, clazz_ctor, (jlong)service, jdevice, isPrimary, uuid);
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ jobject jservice = env->NewObject(clazz, clazz_ctor, (jlong)service, jdevice, isPrimary,
+ uuid, service->startHandle, service->endHandle);
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(jservice, E_FILE_LINE);
std::shared_ptr<JavaAnonObj> jServiceRef = service->getJavaObject();
JavaGlobalObj::check(jServiceRef, E_FILE_LINE);
@@ -188,6 +356,8 @@ jobject Java_direct_1bt_tinyb_DBTDevice_getServices(JNIEnv *env, jobject obj) {
return JavaGlobalObj::GetObject(jServiceRef);
};
return convert_vector_sharedptr_to_jarraylist<GATTService>(env, services, _serviceClazzCtorArgs.c_str(), ctor_service);
+ } else {
+ WARN_PRINT("Device GATTHandle not connected: %s", device->toString().c_str());
}
} catch(...) {
rethrow_and_raise_java_exception(env);
diff --git a/java/jni/direct_bt/DBTGattCharacteristic.cxx b/java/jni/direct_bt/DBTGattCharacteristic.cxx
index c19701a3..2222e435 100644
--- a/java/jni/direct_bt/DBTGattCharacteristic.cxx
+++ b/java/jni/direct_bt/DBTGattCharacteristic.cxx
@@ -25,10 +25,150 @@
#include "direct_bt_tinyb_DBTGattCharacteristic.h"
+// #define VERBOSE_ON 1
+#include <dbt_debug.hpp>
+
#include "JNIMem.hpp"
#include "helper_base.hpp"
+#include "helper_dbt.hpp"
-#include "direct_bt/DBTTypes.hpp"
+#include "direct_bt/DBTDevice.hpp"
+#include "direct_bt/DBTAdapter.hpp"
using namespace direct_bt;
+jstring Java_direct_1bt_tinyb_DBTGattCharacteristic_toStringImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTCharacteristic *nativePtr = getInstance<GATTCharacteristic>(env, obj);
+ JavaGlobalObj::check(nativePtr->getJavaObject(), E_FILE_LINE);
+ return from_string_to_jstring(env, nativePtr->toString());
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
+void Java_direct_1bt_tinyb_DBTGattCharacteristic_deleteImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTCharacteristic *characteristic = getInstance<GATTCharacteristic>(env, obj);
+ (void)characteristic;
+ // No delete: Service instance owned by GATTService -> DBTDevice
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+}
+
+static const std::string _descriptorClazzCtorArgs("(JLdirect_bt/tinyb/DBTGattCharacteristic;Ljava/lang/String;S[B)V");
+
+jobject Java_direct_1bt_tinyb_DBTGattCharacteristic_getDescriptorsImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTCharacteristic *characteristic = getInstance<GATTCharacteristic>(env, obj);
+ JavaGlobalObj::check(characteristic->getJavaObject(), E_FILE_LINE);
+
+ std::vector<GATTDescriptorRef> & descriptorList = characteristic->descriptorList;
+
+ // DBTGattDescriptor(final long nativeInstance, final DBTGattCharacteristic characteristic,
+ // final String type_uuid, final short handle, final byte[] value)
+
+ // DBTGattDescriptor(final long nativeInstance, final DBTGattCharacteristic characteristic,
+ // final String type_uuid, final short handle, final byte[] value)
+
+ std::function<jobject(JNIEnv*, jclass, jmethodID, GATTDescriptor *)> ctor_desc =
+ [](JNIEnv *env, jclass clazz, jmethodID clazz_ctor, GATTDescriptor *descriptor)->jobject {
+ // prepare adapter ctor
+ JavaGlobalObj::check(descriptor->characteristic->getJavaObject(), E_FILE_LINE);
+ jobject jcharacteristic = JavaGlobalObj::GetObject(descriptor->characteristic->getJavaObject());
+
+ const jstring uuid = from_string_to_jstring(env, descriptor->type->toString());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+
+ const size_t value_size = descriptor->value.getSize();
+ jbyteArray jvalue = env->NewByteArray((jsize)value_size);
+ env->SetByteArrayRegion(jvalue, 0, (jsize)value_size, (const jbyte *)descriptor->value.get_ptr());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+
+ jobject jchar = env->NewObject(clazz, clazz_ctor, (jlong)descriptor, jcharacteristic,
+ uuid, (jshort)descriptor->handle, jvalue);
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ JNIGlobalRef::check(jchar, E_FILE_LINE);
+ std::shared_ptr<JavaAnonObj> jCharRef = descriptor->getJavaObject();
+ JavaGlobalObj::check(jCharRef, E_FILE_LINE);
+
+ return JavaGlobalObj::GetObject(jCharRef);
+ };
+ return convert_vector_sharedptr_to_jarraylist<GATTDescriptor>(env, descriptorList, _descriptorClazzCtorArgs.c_str(), ctor_desc);
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
+jbyteArray Java_direct_1bt_tinyb_DBTGattCharacteristic_readValueImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTCharacteristic *characteristic = getInstance<GATTCharacteristic>(env, obj);
+ JavaGlobalObj::check(characteristic->getJavaObject(), E_FILE_LINE);
+
+ POctets res(GATTHandler::ClientMaxMTU, 0);
+ if( !characteristic->readValue(res) ) {
+ ERR_PRINT("Characteristic readValue failed: %s", characteristic->toString().c_str());
+ return JNI_FALSE;
+ }
+
+ const size_t value_size = res.getSize();
+ jbyteArray jres = env->NewByteArray((jsize)value_size);
+ env->SetByteArrayRegion(jres, 0, (jsize)value_size, (const jbyte *)res.get_ptr());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ return jres;
+
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
+jboolean Java_direct_1bt_tinyb_DBTGattCharacteristic_writeValueImpl(JNIEnv *env, jobject obj, jbyteArray jvalue) {
+ try {
+ if( nullptr == jvalue ) {
+ throw IllegalArgumentException("byte array null", E_FILE_LINE);
+ }
+ const int value_size = env->GetArrayLength(jvalue);
+ if( 0 == value_size ) {
+ return JNI_TRUE;
+ }
+ GATTCharacteristic *characteristic = getInstance<GATTCharacteristic>(env, obj);
+ JavaGlobalObj::check(characteristic->getJavaObject(), E_FILE_LINE);
+
+ JNICriticalArray<uint8_t> criticalArray(env); // RAII - release
+ uint8_t * value_ptr = criticalArray.get(jvalue, criticalArray.Mode::NO_UPDATE_AND_RELEASE);
+ if( NULL == value_ptr ) {
+ throw InternalError("GetPrimitiveArrayCritical(byte array) is null", E_FILE_LINE);
+ }
+ TROOctets value(value_ptr, value_size);
+ if( !characteristic->writeValue(value) ) {
+ ERR_PRINT("Characteristic writeValue failed: %s", characteristic->toString().c_str());
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_direct_1bt_tinyb_DBTGattCharacteristic_enableValueNotificationsImpl(JNIEnv *env, jobject obj, jboolean enable) {
+ try {
+ GATTCharacteristic *characteristic = getInstance<GATTCharacteristic>(env, obj);
+ JavaGlobalObj::check(characteristic->getJavaObject(), E_FILE_LINE);
+
+ bool cccdEnableResult[2];
+ bool res = characteristic->configIndicationNotification(enable, enable, cccdEnableResult);
+ DBG_PRINT("DBTGattCharacteristic::configIndicationNotification Config Notification(%d), Indication(%d): Result %d",
+ cccdEnableResult[0], cccdEnableResult[1], res);
+ (void) cccdEnableResult;
+ return res;
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return JNI_FALSE;
+}
+
diff --git a/java/jni/direct_bt/DBTGattDescriptor.cxx b/java/jni/direct_bt/DBTGattDescriptor.cxx
index d90c559b..2a3443fc 100644
--- a/java/jni/direct_bt/DBTGattDescriptor.cxx
+++ b/java/jni/direct_bt/DBTGattDescriptor.cxx
@@ -25,10 +25,89 @@
#include "direct_bt_tinyb_DBTGattDescriptor.h"
+// #define VERBOSE_ON 1
+#include <dbt_debug.hpp>
+
#include "JNIMem.hpp"
#include "helper_base.hpp"
+#include "helper_dbt.hpp"
-#include "direct_bt/DBTTypes.hpp"
+#include "direct_bt/DBTDevice.hpp"
+#include "direct_bt/DBTAdapter.hpp"
using namespace direct_bt;
+void Java_direct_1bt_tinyb_DBTGattDescriptor_deleteImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTDescriptor *descriptor = getInstance<GATTDescriptor>(env, obj);
+ (void)descriptor;
+ // No delete: Service instance owned by GATTService -> DBTDevice
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+}
+
+jstring Java_direct_1bt_tinyb_DBTGattDescriptor_toStringImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTDescriptor *nativePtr = getInstance<GATTDescriptor>(env, obj);
+ JavaGlobalObj::check(nativePtr->getJavaObject(), E_FILE_LINE);
+ return from_string_to_jstring(env, nativePtr->toString());
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
+jbyteArray Java_direct_1bt_tinyb_DBTGattDescriptor_readValueImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTDescriptor *descriptor = getInstance<GATTDescriptor>(env, obj);
+ JavaGlobalObj::check(descriptor->getJavaObject(), E_FILE_LINE);
+
+ if( !descriptor->readValue() ) {
+ ERR_PRINT("Characteristic readValue failed: %s", descriptor->toString().c_str());
+ return JNI_FALSE;
+ }
+ const size_t value_size = descriptor->value.getSize();
+ jbyteArray jres = env->NewByteArray((jsize)value_size);
+ env->SetByteArrayRegion(jres, 0, (jsize)value_size, (const jbyte *)descriptor->value.get_ptr());
+ java_exception_check_and_throw(env, E_FILE_LINE);
+ return jres;
+
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
+jboolean Java_direct_1bt_tinyb_DBTGattDescriptor_writeValueImpl(JNIEnv *env, jobject obj, jbyteArray jvalue) {
+ try {
+ if( nullptr == jvalue ) {
+ throw IllegalArgumentException("byte array null", E_FILE_LINE);
+ }
+ const int value_size = env->GetArrayLength(jvalue);
+ if( 0 == value_size ) {
+ return JNI_TRUE;
+ }
+ GATTDescriptor *descriptor = getInstance<GATTDescriptor>(env, obj);
+ JavaGlobalObj::check(descriptor->getJavaObject(), E_FILE_LINE);
+
+ JNICriticalArray<uint8_t> criticalArray(env); // RAII - release
+ uint8_t * value_ptr = criticalArray.get(jvalue, criticalArray.Mode::NO_UPDATE_AND_RELEASE);
+ if( NULL == value_ptr ) {
+ throw InternalError("GetPrimitiveArrayCritical(byte array) is null", E_FILE_LINE);
+ }
+ TROOctets value(value_ptr, value_size);
+ descriptor->value = value; // copy data
+
+ if( !descriptor->writeValue() ) {
+ ERR_PRINT("Descriptor writeValue failed: %s", descriptor->toString().c_str());
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return JNI_FALSE;
+}
+
+
diff --git a/java/jni/direct_bt/DBTGattService.cxx b/java/jni/direct_bt/DBTGattService.cxx
index 3361a44c..6b3668e1 100644
--- a/java/jni/direct_bt/DBTGattService.cxx
+++ b/java/jni/direct_bt/DBTGattService.cxx
@@ -25,7 +25,7 @@
#include "direct_bt_tinyb_DBTGattService.h"
-#define VERBOSE_ON 1
+// #define VERBOSE_ON 1
#include <dbt_debug.hpp>
#include "JNIMem.hpp"
@@ -37,10 +37,21 @@
using namespace direct_bt;
+jstring Java_direct_1bt_tinyb_DBTGattService_toStringImpl(JNIEnv *env, jobject obj) {
+ try {
+ GATTService *nativePtr = getInstance<GATTService>(env, obj);
+ JavaGlobalObj::check(nativePtr->getJavaObject(), E_FILE_LINE);
+ return from_string_to_jstring(env, nativePtr->toString());
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return nullptr;
+}
+
+
void Java_direct_1bt_tinyb_DBTGattService_deleteImpl(JNIEnv *env, jobject obj) {
try {
GATTService *service = getInstance<GATTService>(env, obj);
- JavaGlobalObj::check(service->getJavaObject(), E_FILE_LINE);
(void)service;
// No delete: Service instance owned by DBTDevice
} catch(...) {
@@ -48,16 +59,19 @@ void Java_direct_1bt_tinyb_DBTGattService_deleteImpl(JNIEnv *env, jobject obj) {
}
}
-static const std::string _characteristicClazzCtorArgs("(JLorg/tinyb/BluetoothGattService;[Ljava/lang/String;Ljava/lang/String;)V");
+static const std::string _characteristicClazzCtorArgs("(JLdirect_bt/tinyb/DBTGattService;S[Ljava/lang/String;Ljava/lang/String;SI)V");
-jobject Java_direct_1bt_tinyb_DBTGattService_getCharacteristics(JNIEnv *env, jobject obj) {
+jobject Java_direct_1bt_tinyb_DBTGattService_getCharacteristicsImpl(JNIEnv *env, jobject obj) {
try {
GATTService *service = getInstance<GATTService>(env, obj);
JavaGlobalObj::check(service->getJavaObject(), E_FILE_LINE);
std::vector<std::shared_ptr<GATTCharacteristic>> & characteristics = service->characteristicList;
- // DBTGattCharacteristic(final long nativeInstance, final BluetoothGattService service, final String[] properties, final String uuid)
+ // DBTGattCharacteristic(final long nativeInstance, final DBTGattService service,
+ // final short handle, final String[] properties,
+ // final String value_type_uuid, final short value_handle,
+ // final int clientCharacteristicsConfigIndex)
std::function<jobject(JNIEnv*, jclass, jmethodID, GATTCharacteristic *)> ctor_char =
[](JNIEnv *env, jclass clazz, jmethodID clazz_ctor, GATTCharacteristic *characteristic)->jobject {
@@ -70,19 +84,21 @@ jobject Java_direct_1bt_tinyb_DBTGattService_getCharacteristics(JNIEnv *env, job
jclass string_class = search_class(env, "Ljava/lang/String;");
jobjectArray jproperties = env->NewObjectArray(props_size, string_class, 0);
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
for (unsigned int i = 0; i < props_size; ++i) {
jobject elem = from_string_to_jstring(env, *props[i].get());
env->SetObjectArrayElement(jproperties, i, elem);
}
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
const jstring uuid = from_string_to_jstring(env, characteristic->value_type->toString());
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
- jobject jchar = env->NewObject(clazz, clazz_ctor, (jlong)characteristic, jservice, jproperties, uuid);
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ jobject jchar = env->NewObject(clazz, clazz_ctor, (jlong)characteristic, jservice,
+ characteristic->handle, jproperties,
+ uuid, characteristic->value_handle, characteristic->clientCharacteristicsConfigIndex);
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(jchar, E_FILE_LINE);
std::shared_ptr<JavaAnonObj> jCharRef = characteristic->getJavaObject();
JavaGlobalObj::check(jCharRef, E_FILE_LINE);
diff --git a/java/jni/direct_bt/DBTManager.cxx b/java/jni/direct_bt/DBTManager.cxx
index 6e59b1fb..d9fc55bf 100644
--- a/java/jni/direct_bt/DBTManager.cxx
+++ b/java/jni/direct_bt/DBTManager.cxx
@@ -25,7 +25,7 @@
#include "direct_bt_tinyb_DBTManager.h"
-#define VERBOSE_ON 1
+// #define VERBOSE_ON 1
#include <dbt_debug.hpp>
#include "JNIMem.hpp"
@@ -43,7 +43,7 @@ void Java_direct_1bt_tinyb_DBTManager_initImpl(JNIEnv *env, jobject obj)
try {
DBTManager *manager = &DBTManager::get(BTMode::BT_MODE_LE); // special: static singleton
setInstance<DBTManager>(env, obj, manager);
- if( java_exception_check(env, E_FILE_LINE) ) { return; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
manager->setJavaObject( std::shared_ptr<JavaAnonObj>( new JavaGlobalObj(obj) ) );
JavaGlobalObj::check(manager->getJavaObject(), E_FILE_LINE);
DBG_PRINT("Java_direct_1bt_tinyb_DBTManager_init: Manager %s", manager->toString().c_str());
@@ -93,9 +93,9 @@ jobject Java_direct_1bt_tinyb_DBTManager_getAdapterListImpl(JNIEnv *env, jobject
// prepare adapter ctor
const jstring addr = from_string_to_jstring(env, adapter->getAddressString());
const jstring name = from_string_to_jstring(env, adapter->getName());
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
jobject jAdapter = env->NewObject(clazz, clazz_ctor, (jlong)adapter, addr, name);
- if( java_exception_check(env, E_FILE_LINE) ) { return nullptr; }
+ java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(jAdapter, E_FILE_LINE);
std::shared_ptr<JavaAnonObj> jAdapterRef = adapter->getJavaObject();
JavaGlobalObj::check(jAdapterRef, E_FILE_LINE);
diff --git a/java/jni/direct_bt/DBTNativeDownlink.cxx b/java/jni/direct_bt/DBTNativeDownlink.cxx
index 78270664..fdfacf5e 100644
--- a/java/jni/direct_bt/DBTNativeDownlink.cxx
+++ b/java/jni/direct_bt/DBTNativeDownlink.cxx
@@ -25,7 +25,7 @@
#include "direct_bt_tinyb_DBTNativeDownlink.h"
-#define VERBOSE_ON 1
+// #define VERBOSE_ON 1
#include <dbt_debug.hpp>
#include "JNIMem.hpp"