summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/direct_bt/DBTDevice.hpp38
-rw-r--r--api/direct_bt/GATTCharacteristic.hpp116
-rw-r--r--api/direct_bt/GATTHandler.hpp21
-rw-r--r--examples/direct_bt_scanner00/dbt_scanner00.cpp6
-rw-r--r--examples/direct_bt_scanner01/dbt_scanner01.cpp2
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp12
-rw-r--r--examples/java/ScannerTinyB01.java47
-rw-r--r--examples/java/ScannerTinyB02.java31
-rw-r--r--examples/java/ScannerTinyB10.java39
-rw-r--r--java/direct_bt/tinyb/DBTDevice.java11
-rw-r--r--java/direct_bt/tinyb/DBTGattCharacteristic.java181
-rw-r--r--java/jni/BluetoothUtils.cxx2
-rw-r--r--java/jni/JNIMem.hpp6
-rw-r--r--java/jni/direct_bt/DBTDevice.cxx79
-rw-r--r--java/jni/direct_bt/DBTGattCharacteristic.cxx25
-rw-r--r--java/jni/direct_bt/DBTGattDescriptor.cxx2
-rw-r--r--java/jni/direct_bt/DBTGattService.cxx8
-rw-r--r--java/jni/direct_bt/helper_dbt.hpp4
-rw-r--r--java/org/tinyb/BluetoothDevice.java23
-rw-r--r--java/org/tinyb/BluetoothGattCharacteristic.java97
-rw-r--r--java/org/tinyb/BluetoothGattService.java35
-rw-r--r--java/org/tinyb/GATTCharacteristicListener.java47
-rw-r--r--java/tinyb/dbus/DBusDevice.java7
-rw-r--r--java/tinyb/dbus/DBusGattCharacteristic.java18
-rw-r--r--src/direct_bt/DBTDevice.cpp39
-rw-r--r--src/direct_bt/GATTCharacteristic.cpp69
-rw-r--r--src/direct_bt/GATTHandler.cpp59
27 files changed, 697 insertions, 327 deletions
diff --git a/api/direct_bt/DBTDevice.hpp b/api/direct_bt/DBTDevice.hpp
index 1ff1a2a4..4b8eb806 100644
--- a/api/direct_bt/DBTDevice.hpp
+++ b/api/direct_bt/DBTDevice.hpp
@@ -390,6 +390,44 @@ namespace direct_bt {
* </p>
*/
void disconnectGATT();
+
+ /**
+ * Add the given {@link GATTCharacteristicListener} to the listener list if not already present.
+ * <p>
+ * Convenience delegation call to GATTHandler
+ * </p>
+ * @param listener A {@link GATTCharacteristicListener} instance, listening to all {@link BluetoothGattCharacteristic} 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 GATTHandler is null, i.e. not connected
+ */
+ bool addCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l);
+
+ /**
+ * Remove the given {@link GATTCharacteristicListener} from the listener list.
+ * <p>
+ * If the GATTHandler is null, i.e. not connected, {@code false} is being returned.
+ * </p>
+ * @param listener A {@link GATTCharacteristicListener} instance
+ * @return true if the given listener is an element of the list and has been removed, otherwise false.
+ */
+ bool removeCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l);
+
+ /**
+ * Remove all {@link GATTCharacteristicListener} from the list, which are associated to the given {@link GATTCharacteristic}.
+ * <p>
+ * Implementation tests all listener's GATTCharacteristicListener::match(const GATTCharacteristic & characteristic)
+ * to match with the given associated characteristic.
+ * </p>
+ * @param associatedCharacteristic the match criteria to remove any GATTCharacteristicListener from the list
+ * @return number of removed listener.
+ */
+ int removeAllAssociatedCharacteristicListener(std::shared_ptr<GATTCharacteristic> associatedCharacteristic);
+
+ /**
+ * Remove all {@link GATTCharacteristicListener} from the list.
+ * @return number of removed listener.
+ */
+ int removeAllCharacteristicListener();
};
inline bool operator<(const DBTDevice& lhs, const DBTDevice& rhs)
diff --git a/api/direct_bt/GATTCharacteristic.hpp b/api/direct_bt/GATTCharacteristic.hpp
index 39f132fc..7734c35f 100644
--- a/api/direct_bt/GATTCharacteristic.hpp
+++ b/api/direct_bt/GATTCharacteristic.hpp
@@ -173,25 +173,32 @@ namespace direct_bt {
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
* <p>
- * Convenience delegation call to GATTHandler via DBTDevice
+ * Method enables notification and/or indication for this characteristic at BLE level.
* </p>
* <p>
- * If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown.
+ * Convenience delegation call to GATTHandler via DBTDevice
* </p>
* <p>
* Implementation masks this Characteristic properties PropertyBitVal::Notify and PropertyBitVal::Indicate
- * with the respective user request parameters, hence removes unsupported requests.<br>
- * If the resulting combination for both requests is false, method returns false.
- * </p>
- * <p>
- * Returns false if there is no GATTDescriptor of type ClientCharacteristicConfiguration,
- * or if the operation has failed.
- * </p>
+ * with the respective user request parameters, hence removes unsupported requests.
+ * </p>
+ * @param enableNotification
+ * @param enableIndication
+ * @param enabledState array of size 2, holding the resulting enabled state for notification and indication.
+ * @return false if this characteristic has no PropertyBitVal::Notify or PropertyBitVal::Indication present,
+ * or there is no GATTDescriptor of type ClientCharacteristicConfiguration, or if the operation has failed.
+ * Otherwise returns true.
+ * @throws IllegalStateException if notification or indication is set to be enabled
+ * and the {@link DBTDevice's}'s {@link GATTHandler} is null, i.e. not connected
*/
- bool configIndicationNotification(const bool enableNotification, const bool enableIndication, bool enableResult[2]);
+ bool configNotificationIndication(const bool enableNotification, const bool enableIndication, bool enabledState[2]);
/**
- * Add the given listener to the list if not already present.
+ * Add the given GATTCharacteristicListener to the listener list if not already present.
+ * <p>
+ * Occurring notifications and indications, if enabled via {@link #configNotificationIndication(bool, bool, bool[])}},
+ * will call the respective GATTCharacteristicListener callback method.
+ * </p>
* <p>
* Returns true if the given listener is not element of the list and has been newly added,
* otherwise false.
@@ -200,61 +207,88 @@ namespace direct_bt {
* Convenience delegation call to GATTHandler via DBTDevice
* </p>
* <p>
- * If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown.
- * </p>
- * <p>
* To restrict the listener to listen only to this GATTCharacteristic instance,
* user has to implement GATTCharacteristicListener::match(GATTCharacteristicRef) accordingly.
* <br>
- * For this purpose, use may derive from SpecificGATTCharacteristicListener,
+ * For this purpose, use may derive from AssociatedGATTCharacteristicListener,
* which provides these simple matching filter facilities.
* </p>
+ * @throws IllegalStateException if the {@link DBTDevice's}'s {@link GATTHandler} is null, i.e. not connected
*/
bool addCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l);
/**
- * Remove the given listener from the list.
+ * Add the given GATTCharacteristicListener to the listener list if not already present
+ * and if enabling the notification and/or indication for this characteristic at BLE level was successful.
* <p>
- * Returns true if the given listener is an element of the list and has been removed,
+ * Occurring notifications and indications will call the respective {@link GATTCharacteristicListener}
+ * callback method.
+ * </p>
+ * <p>
+ * 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.
* </p>
* <p>
* Convenience delegation call to GATTHandler via DBTDevice
+ * performing both, configNotificationIndication(..) and addCharacteristicListener(..).
* </p>
* <p>
- * If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown.
+ * To restrict the listener to listen only to this GATTCharacteristic instance,
+ * user has to implement GATTCharacteristicListener::match(GATTCharacteristicRef) accordingly.
+ * <br>
+ * For this purpose, use may derive from AssociatedGATTCharacteristicListener,
+ * which provides these simple matching filter facilities.
* </p>
+ * @param enabledState array of size 2, holding the resulting enabled state for notification and indication
+ * using {@link #configNotificationIndication(bool, bool, bool[])}}
+ * @throws IllegalStateException if the {@link DBTDevice's}'s {@link GATTHandler} is null, i.e. not connected
*/
- bool removeCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l);
+ bool addCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l, bool enabledState[2]);
/**
- * Remove the given listener from the list.
+ * Disables the notification and/or indication for this characteristic at BLE level
+ * if {@code disableIndicationNotification == true}
+ * and removes the given {@link GATTCharacteristicListener} from the listener list.
* <p>
* Returns true if the given listener is an element of the list and has been removed,
* otherwise false.
* </p>
* <p>
* Convenience delegation call to GATTHandler via DBTDevice
+ * performing addCharacteristicListener(..)
+ * and {@link #configNotificationIndication(bool, bool, bool[]) if {@code disableIndicationNotification == true}.
* </p>
* <p>
- * If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown.
+ * If the DBTDevice's GATTHandler is null, i.e. not connected, {@code false} is being returned.
* </p>
+ * @param l
+ * @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic
+ * using {@link #configNotificationIndication(bool, bool, bool[])
+ * @return
*/
- bool removeCharacteristicListener(const GATTCharacteristicListener * l);
+ bool removeCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l, bool disableIndicationNotification);
/**
- * Remove all event listener from the list.
+ * Disables the notification and/or indication for this characteristic at BLE level
+ * if {@code disableIndicationNotification == true}
+ * and removes all {@link GATTCharacteristicListener} from the listener list.
* <p>
* Returns the number of removed event listener.
* </p>
* <p>
* Convenience delegation call to GATTHandler via DBTDevice
+ * performing addCharacteristicListener(..)
+ * and configNotificationIndication(..) if {@code disableIndicationNotification == true}.
* </p>
* <p>
- * If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown.
+ * If the DBTDevice's GATTHandler is null, i.e. not connected, {@code zero} is being returned.
* </p>
+ * @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic
+ * using {@link #configNotificationIndication(bool, bool, bool[])
+ * @return
*/
- int removeAllCharacteristicListener();
+ int removeAllCharacteristicListener(bool disableIndicationNotification);
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value
@@ -319,6 +353,9 @@ namespace direct_bt {
* see method's API doc for {@link GATTCharacteristic} filtering.
* </p>
* <p>
+ * User may utilize {@link AssociatedGATTCharacteristicListener} to listen to only one {@link GATTCharacteristic}.
+ * </p>
+ * <p>
* A listener instance may be attached to a {@link GATTHandler} via
* {@link GATTHandler::addCharacteristicListener(std::shared_ptr<GATTCharacteristicListener>)}
* to listen to all events of the device or the matching filtered events.
@@ -345,9 +382,24 @@ namespace direct_bt {
return true;
}
+ /**
+ * Called from native BLE stack, initiated by a received notification associated
+ * with the given {@link GATTCharacteristic}.
+ * @param charDecl {@link GATTCharacteristic} related to this notification
+ * @param charValue the notification value
+ * @param timestamp the indication monotonic timestamp, see getCurrentMilliseconds()
+ */
virtual void notificationReceived(GATTCharacteristicRef charDecl,
std::shared_ptr<TROOctets> charValue, const uint64_t timestamp) = 0;
+ /**
+ * Called from native BLE stack, initiated by a received indication associated
+ * with the given {@link GATTCharacteristic}.
+ * @param charDecl {@link GATTCharacteristic} related to this indication
+ * @param charValue the indication value
+ * @param timestamp the indication monotonic timestamp, see {@link BluetoothUtils#getCurrentMilliseconds()}
+ * @param confirmationSent if true, the native stack has sent the confirmation, otherwise user is required to do so.
+ */
virtual void indicationReceived(GATTCharacteristicRef charDecl,
std::shared_ptr<TROOctets> charValue, const uint64_t timestamp,
const bool confirmationSent) = 0;
@@ -367,22 +419,22 @@ namespace direct_bt {
{ return !(*this == rhs); }
};
- class SpecificGATTCharacteristicListener : public GATTCharacteristicListener{
+ class AssociatedGATTCharacteristicListener : public GATTCharacteristicListener{
private:
- const GATTCharacteristic * characteristicMatch;
+ const GATTCharacteristic * associatedCharacteristic;
public:
/**
- * Passing the specific GATTCharacteristic to filter out non matching events.
+ * Passing the associated GATTCharacteristic to filter out non matching events.
*/
- SpecificGATTCharacteristicListener(const GATTCharacteristic * characteristicMatch)
- : characteristicMatch(characteristicMatch) { }
+ AssociatedGATTCharacteristicListener(const GATTCharacteristic * characteristicMatch)
+ : associatedCharacteristic(characteristicMatch) { }
bool match(const GATTCharacteristic & characteristic) override {
- if( nullptr == characteristicMatch ) {
+ if( nullptr == associatedCharacteristic ) {
return true;
}
- return *characteristicMatch == characteristic;
+ return *associatedCharacteristic == characteristic;
}
};
diff --git a/api/direct_bt/GATTHandler.hpp b/api/direct_bt/GATTHandler.hpp
index 99d5d7ad..40162184 100644
--- a/api/direct_bt/GATTHandler.hpp
+++ b/api/direct_bt/GATTHandler.hpp
@@ -106,7 +106,7 @@ namespace direct_bt {
/** send immediate confirmation of indication events from device, defaults to true. */
bool sendIndicationConfirmation = true;
- std::vector<std::shared_ptr<GATTCharacteristicListener>> eventListenerList;
+ std::vector<std::shared_ptr<GATTCharacteristicListener>> characteristicListenerList;
std::recursive_mutex mtx_eventListenerList;
uint16_t serverMTU;
@@ -310,10 +310,13 @@ namespace direct_bt {
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
* <p>
+ * Method enables notification and/or indication for the corresponding characteristic at BLE level.
+ * </p>
+ * <p>
* Throws an IllegalArgumentException if the given GATTDescriptor is not a ClientCharacteristicConfiguration.
* </p>
*/
- bool configIndicationNotification(GATTDescriptor & cd, const bool enableNotification, const bool enableIndication);
+ bool configNotificationIndication(GATTDescriptor & cd, const bool enableNotification, const bool enableIndication);
/**
* Add the given listener to the list if not already present.
@@ -341,8 +344,22 @@ namespace direct_bt {
* </p>
*/
bool removeCharacteristicListener(const GATTCharacteristicListener * l);
+
/**
+ * Remove all {@link GATTCharacteristicListener} from the list, which are associated to the given {@link GATTCharacteristic}.
+ * <p>
+ * Implementation tests all listener's GATTCharacteristicListener::match(const GATTCharacteristic & characteristic)
+ * to match with the given associated characteristic.
+ * </p>
+ * @param associatedCharacteristic the match criteria to remove any GATTCharacteristicListener from the list
+ * @return number of removed listener.
+ */
+ int removeAllAssociatedCharacteristicListener(std::shared_ptr<GATTCharacteristic> associatedCharacteristic);
+
+ int removeAllAssociatedCharacteristicListener(const GATTCharacteristic * associatedCharacteristic);
+
+ /**
* Remove all event listener from the list.
* <p>
* Returns the number of removed event listener.
diff --git a/examples/direct_bt_scanner00/dbt_scanner00.cpp b/examples/direct_bt_scanner00/dbt_scanner00.cpp
index 617b52b8..e08998c9 100644
--- a/examples/direct_bt_scanner00/dbt_scanner00.cpp
+++ b/examples/direct_bt_scanner00/dbt_scanner00.cpp
@@ -95,11 +95,11 @@ class MyAdapterStatusListener : public AdapterStatusListener {
static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT);
-class MyGATTEventListener : public SpecificGATTCharacteristicListener {
+class MyGATTEventListener : public AssociatedGATTCharacteristicListener {
public:
MyGATTEventListener(const GATTCharacteristic * characteristicMatch)
- : SpecificGATTCharacteristicListener(characteristicMatch) {}
+ : AssociatedGATTCharacteristicListener(characteristicMatch) {}
void notificationReceived(GATTCharacteristicRef charDecl, std::shared_ptr<TROOctets> charValue, const uint64_t timestamp) override {
const std::shared_ptr<DBTDevice> dev = charDecl->getDevice();
@@ -278,7 +278,7 @@ int main(int argc, char *argv[])
}
}
bool cccdEnableResult[2];
- bool cccdRet = serviceChar.configIndicationNotification(true /* enableNotification */, true /* enableIndication */, cccdEnableResult);
+ bool cccdRet = serviceChar.configNotificationIndication(true /* enableNotification */, true /* enableIndication */, cccdEnableResult);
fprintf(stderr, " [%2.2d.%2.2d] Config Notification(%d), Indication(%d): Result %d\n",
(int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], cccdRet);
if( cccdRet ) {
diff --git a/examples/direct_bt_scanner01/dbt_scanner01.cpp b/examples/direct_bt_scanner01/dbt_scanner01.cpp
index 10eca512..2fead468 100644
--- a/examples/direct_bt_scanner01/dbt_scanner01.cpp
+++ b/examples/direct_bt_scanner01/dbt_scanner01.cpp
@@ -290,7 +290,7 @@ int main(int argc, char *argv[])
const bool enableNotification = serviceChar.hasProperties(GATTCharacteristic::PropertyBitVal::Notify);
const bool enableIndication = serviceChar.hasProperties(GATTCharacteristic::PropertyBitVal::Indicate);
if( enableNotification || enableIndication ) {
- bool res = gatt->configIndicationNotification(*cccd, enableNotification, enableIndication);
+ bool res = gatt->configNotificationIndication(*cccd, enableNotification, enableIndication);
fprintf(stderr, " [%2.2d.%2.2d] Config Notification(%d), Indication(%d): Result %d\n",
(int)i, (int)j, enableNotification, enableIndication, res);
}
diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp
index bde839d8..4a4e7bd8 100644
--- a/examples/direct_bt_scanner10/dbt_scanner10.cpp
+++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp
@@ -193,11 +193,11 @@ class MyAdapterStatusListener : public AdapterStatusListener {
static const uuid16_t _TEMPERATURE_MEASUREMENT(GattCharacteristicType::TEMPERATURE_MEASUREMENT);
-class MyGATTEventListener : public SpecificGATTCharacteristicListener {
+class MyGATTEventListener : public AssociatedGATTCharacteristicListener {
public:
MyGATTEventListener(const GATTCharacteristic * characteristicMatch)
- : SpecificGATTCharacteristicListener(characteristicMatch) {}
+ : AssociatedGATTCharacteristicListener(characteristicMatch) {}
void notificationReceived(GATTCharacteristicRef charDecl, std::shared_ptr<TROOctets> charValue, const uint64_t timestamp) override {
const std::shared_ptr<DBTDevice> dev = charDecl->getDevice();
@@ -306,12 +306,10 @@ static void processConnectedDevice(std::shared_ptr<DBTDevice> device) {
}
}
bool cccdEnableResult[2];
- bool cccdRet = serviceChar.configIndicationNotification(true /* enableNotification */, true /* enableIndication */, cccdEnableResult);
- fprintf(stderr, " [%2.2d.%2.2d] Config Notification(%d), Indication(%d): Result %d\n",
+ bool cccdRet = serviceChar.addCharacteristicListener( std::shared_ptr<GATTCharacteristicListener>( new MyGATTEventListener(&serviceChar) ),
+ cccdEnableResult );
+ fprintf(stderr, " [%2.2d.%2.2d] addCharacteristicListener Notification(%d), Indication(%d): Result %d\n",
(int)i, (int)j, cccdEnableResult[0], cccdEnableResult[1], cccdRet);
- if( cccdRet ) {
- serviceChar.addCharacteristicListener( std::shared_ptr<GATTCharacteristicListener>( new MyGATTEventListener(&serviceChar) ) );
- }
}
}
// FIXME sleep 1s for potential callbacks ..
diff --git a/examples/java/ScannerTinyB01.java b/examples/java/ScannerTinyB01.java
index c2f7a739..d6385167 100644
--- a/examples/java/ScannerTinyB01.java
+++ b/examples/java/ScannerTinyB01.java
@@ -203,22 +203,6 @@ public class ScannerTinyB01 {
adapter.enablePoweredNotifications(new BooleanNotification("Powered", timestamp_t0));
- final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener() {
- @Override
- public void notificationReceived(final BluetoothGattCharacteristic charDecl,
- final byte[] value, final long timestamp) {
- System.err.println("****** GATT notificationReceived: "+charDecl+
- ", value "+BluetoothUtils.bytesHexString(value, true, true));
- }
-
- @Override
- public void indicationReceived(final BluetoothGattCharacteristic charDecl,
- final byte[] value, final long timestamp, final boolean confirmationSent) {
- System.err.println("****** GATT indicationReceived: "+charDecl+
- ", value "+BluetoothUtils.bytesHexString(value, true, true));
- }
- };
-
int loop = 0;
try {
while( forever || loop < max_loops ) {
@@ -339,13 +323,23 @@ public class ScannerTinyB01 {
}
}
- final boolean addedCharacteristicListenerRes;
- if( isDirectBT ) {
- addedCharacteristicListenerRes =
- BluetoothGattService.addCharacteristicListenerToAll(sensor, primServices, myCharacteristicListener);
- } else {
- addedCharacteristicListenerRes = false;
- }
+ final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener(null) {
+ @Override
+ public void notificationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp) {
+ System.err.println("****** GATT notificationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+
+ @Override
+ public void indicationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp, final boolean confirmationSent) {
+ System.err.println("****** GATT indicationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+ };
+ final boolean addedCharacteristicListenerRes =
+ BluetoothGattService.addCharacteristicListenerToAll(sensor, primServices, myCharacteristicListener);
System.err.println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes);
int i=0, j=0;
@@ -373,12 +367,7 @@ public class ScannerTinyB01 {
}
Thread.sleep(1000); // FIXME: Wait for notifications
- final boolean remRes;
- if( isDirectBT ) {
- remRes = BluetoothGattService.removeCharacteristicListenerFromAll(sensor, primServices, myCharacteristicListener);
- } else {
- remRes = false;
- }
+ final boolean remRes = BluetoothGattService.removeCharacteristicListenerFromAll(sensor, primServices, myCharacteristicListener);
System.err.println("Removed GATTCharacteristicListener: "+remRes);
}
sensor.disconnect();
diff --git a/examples/java/ScannerTinyB02.java b/examples/java/ScannerTinyB02.java
index 678e88a0..d02ba9e7 100644
--- a/examples/java/ScannerTinyB02.java
+++ b/examples/java/ScannerTinyB02.java
@@ -193,22 +193,6 @@ public class ScannerTinyB02 {
adapter.enablePoweredNotifications(new BooleanNotification("Powered", timestamp_t0));
- final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener() {
- @Override
- public void notificationReceived(final BluetoothGattCharacteristic charDecl,
- final byte[] value, final long timestamp) {
- System.err.println("****** GATT notificationReceived: "+charDecl+
- ", value "+BluetoothUtils.bytesHexString(value, true, true));
- }
-
- @Override
- public void indicationReceived(final BluetoothGattCharacteristic charDecl,
- final byte[] value, final long timestamp, final boolean confirmationSent) {
- System.err.println("****** GATT indicationReceived: "+charDecl+
- ", value "+BluetoothUtils.bytesHexString(value, true, true));
- }
- };
-
int loop = 0;
try {
while( forever || loop < max_loops ) {
@@ -346,6 +330,21 @@ public class ScannerTinyB02 {
if ( null == primServices || primServices.isEmpty() ) {
System.err.println("No BluetoothGattService found!");
} else {
+ final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener(null) {
+ @Override
+ public void notificationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp) {
+ System.err.println("****** GATT notificationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+
+ @Override
+ public void indicationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp, final boolean confirmationSent) {
+ System.err.println("****** GATT indicationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+ };
final boolean addedCharacteristicListenerRes =
BluetoothGattService.addCharacteristicListenerToAll(sensor, primServices, myCharacteristicListener);
System.err.println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes);
diff --git a/examples/java/ScannerTinyB10.java b/examples/java/ScannerTinyB10.java
index 959bba5b..78235138 100644
--- a/examples/java/ScannerTinyB10.java
+++ b/examples/java/ScannerTinyB10.java
@@ -164,22 +164,6 @@ public class ScannerTinyB10 {
}
};
- final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener() {
- @Override
- public void notificationReceived(final BluetoothGattCharacteristic charDecl,
- final byte[] value, final long timestamp) {
- System.err.println("****** GATT notificationReceived: "+charDecl+
- ", value "+BluetoothUtils.bytesHexString(value, true, true));
- }
-
- @Override
- public void indicationReceived(final BluetoothGattCharacteristic charDecl,
- final byte[] value, final long timestamp, final boolean confirmationSent) {
- System.err.println("****** GATT indicationReceived: "+charDecl+
- ", value "+BluetoothUtils.bytesHexString(value, true, true));
- }
- };
-
private void connectDiscoveredDevice(final BluetoothDevice device) {
System.err.println("****** Connecting Device: Start " + device.toString());
device.getAdapter().stopDiscovery();
@@ -237,9 +221,26 @@ public class ScannerTinyB10 {
System.err.println(" over device : "+char2);
}
}
- final boolean addedCharacteristicListenerRes =
- BluetoothGattService.addCharacteristicListenerToAll(device, primServices, myCharacteristicListener);
- System.err.println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes);
+ {
+ final GATTCharacteristicListener myCharacteristicListener = new GATTCharacteristicListener(null) {
+ @Override
+ public void notificationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp) {
+ System.err.println("****** GATT notificationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+
+ @Override
+ public void indicationReceived(final BluetoothGattCharacteristic charDecl,
+ final byte[] value, final long timestamp, final boolean confirmationSent) {
+ System.err.println("****** GATT indicationReceived: "+charDecl+
+ ", value "+BluetoothUtils.bytesHexString(value, true, true));
+ }
+ };
+ final boolean addedCharacteristicListenerRes =
+ BluetoothGattService.addCharacteristicListenerToAll(device, primServices, myCharacteristicListener);
+ System.err.println("Added GATTCharacteristicListener: "+addedCharacteristicListenerRes);
+ }
int i=0, j=0;
for(final Iterator<BluetoothGattService> srvIter = primServices.iterator(); srvIter.hasNext(); i++) {
diff --git a/java/direct_bt/tinyb/DBTDevice.java b/java/direct_bt/tinyb/DBTDevice.java
index 1f9ec3be..fa47a6ff 100644
--- a/java/direct_bt/tinyb/DBTDevice.java
+++ b/java/direct_bt/tinyb/DBTDevice.java
@@ -241,7 +241,8 @@ public class DBTDevice extends DBTObject implements BluetoothDevice
return;
}
if( !isShutdown ) { // avoid all interaction @ JVM shutdown, native dtor (deleteImpl) cleans up.
- disconnect();
+ // implicit via disconnect, gatt.disconnect(): GATTHandler::removeAllCharacteristicListener();
+ disconnectImpl(); // make sure, regardless of isConnected state
disableConnectedNotifications();
disableRSSINotifications();
@@ -651,12 +652,18 @@ public class DBTDevice extends DBTObject implements BluetoothDevice
protected native void deleteImpl(long nativeInstance);
@Override
- public native boolean addCharacteristicListener(final GATTCharacteristicListener listener, final BluetoothGattCharacteristic characteristicMatch);
+ public boolean addCharacteristicListener(final GATTCharacteristicListener listener) {
+ return addCharacteristicListener(listener, (DBTGattCharacteristic)listener.getAssociatedCharacteristic());
+ }
+ private native boolean addCharacteristicListener(final GATTCharacteristicListener listener, final DBTGattCharacteristic associatedCharacteristic);
@Override
public native boolean removeCharacteristicListener(final GATTCharacteristicListener l);
@Override
+ public native int removeAllAssociatedCharacteristicListener(final BluetoothGattCharacteristic associatedCharacteristic);
+
+ @Override
public native int removeAllCharacteristicListener();
/* local functionality */
diff --git a/java/direct_bt/tinyb/DBTGattCharacteristic.java b/java/direct_bt/tinyb/DBTGattCharacteristic.java
index 7a0973f1..a5b07f2a 100644
--- a/java/direct_bt/tinyb/DBTGattCharacteristic.java
+++ b/java/direct_bt/tinyb/DBTGattCharacteristic.java
@@ -33,7 +33,6 @@ import org.tinyb.BluetoothException;
import org.tinyb.BluetoothGattCharacteristic;
import org.tinyb.BluetoothGattDescriptor;
import org.tinyb.BluetoothGattService;
-import org.tinyb.BluetoothManager;
import org.tinyb.BluetoothNotification;
import org.tinyb.BluetoothObject;
import org.tinyb.BluetoothType;
@@ -57,8 +56,8 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha
/* Characteristics Property */
private final String[] properties;
- // private final boolean hasNotify;
- // private final boolean hasIndicate;
+ private final boolean hasNotify;
+ private final boolean hasIndicate;
/* Characteristics Value Type UUID */
private final String value_type_uuid;
@@ -96,41 +95,9 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha
return valueChanged;
}
- private final GATTCharacteristicListener characteristicListener = new GATTCharacteristicListener() {
-
- @Override
- public void notificationReceived(final BluetoothGattCharacteristic charDecl, final byte[] value, final long timestamp) {
- final DBTGattCharacteristic cd = (DBTGattCharacteristic)charDecl;
- if( !cd.equals(DBTGattCharacteristic.this) ) {
- throw new InternalError("Filtered GATTCharacteristicListener.notificationReceived: Wrong Characteristic: Got "+charDecl+
- ", expected "+DBTGattCharacteristic.this.toString());
- }
- final boolean valueChanged = updateCachedValue(value, true);
- if( DEBUG ) {
- System.err.println("GATTCharacteristicListener.notificationReceived: "+charDecl+
- ", value[changed "+valueChanged+", data "+BluetoothUtils.bytesHexString(value, true, true)+"]");
- }
- }
-
- @Override
- public void indicationReceived(final BluetoothGattCharacteristic charDecl, final byte[] value, final long timestamp,
- final boolean confirmationSent) {
- final DBTGattCharacteristic cd = (DBTGattCharacteristic)charDecl;
- if( !cd.equals(DBTGattCharacteristic.this) ) {
- throw new InternalError("Filtered GATTCharacteristicListener.indicationReceived: Wrong Characteristic: Got "+charDecl+
- ", expected "+DBTGattCharacteristic.this.toString());
- }
- final boolean valueChanged = updateCachedValue(value, true);
- if( DEBUG ) {
- System.err.println("GATTCharacteristicListener.indicationReceived: "+charDecl+
- ", value[changed "+valueChanged+", data "+BluetoothUtils.bytesHexString(value, true, true)+"]");
- }
- }
-
- };
-
/* pp */ DBTGattCharacteristic(final long nativeInstance, final DBTGattService service,
final short handle, final String[] properties,
+ final boolean hasNotify, final boolean hasIndicate,
final String value_type_uuid, final short value_handle,
final int clientCharacteristicsConfigIndex)
{
@@ -139,26 +106,47 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha
this.handle = handle;
this.properties = properties;
- /** {
- boolean hasNotify = false;
- boolean hasIndicate = false;
- for(int i=0; !hasNotify && !hasIndicate && i<properties.length; i++) {
- if( "notify".equals(properties[i]) ) {
- hasNotify = true;
- }
- if( "indicate".equals(properties[i]) ) {
- hasIndicate = true;
- }
- }
- this.hasNotify = hasNotify;
- this.hasIndicate = hasIndicate;
- } */
-
+ this.hasNotify = hasNotify;
+ this.hasIndicate = hasIndicate;
this.value_type_uuid = value_type_uuid;
this.value_handle = value_handle;
this.clientCharacteristicsConfigIndex = clientCharacteristicsConfigIndex;
this.descriptorList = getDescriptorsImpl();
- this.addCharacteristicListener(characteristicListener, false); // silent, don't enable native GATT ourselves
+
+ if( hasNotify || hasIndicate ) {
+ // This characteristicListener serves TinyB 'enableValueNotification(..)' and 'getValue()' (cached value)
+ // backwards compatibility only!
+ final GATTCharacteristicListener characteristicListener = new GATTCharacteristicListener(this) {
+ @Override
+ public void notificationReceived(final BluetoothGattCharacteristic charDecl, final byte[] value, final long timestamp) {
+ final DBTGattCharacteristic cd = (DBTGattCharacteristic)charDecl;
+ if( !cd.equals(DBTGattCharacteristic.this) ) {
+ throw new InternalError("Filtered GATTCharacteristicListener.notificationReceived: Wrong Characteristic: Got "+charDecl+
+ ", expected "+DBTGattCharacteristic.this.toString());
+ }
+ final boolean valueChanged = updateCachedValue(value, true);
+ if( DEBUG ) {
+ System.err.println("GATTCharacteristicListener.notificationReceived: "+charDecl+
+ ", value[changed "+valueChanged+", data "+BluetoothUtils.bytesHexString(value, true, true)+"]");
+ }
+ }
+ @Override
+ public void indicationReceived(final BluetoothGattCharacteristic charDecl, final byte[] value, final long timestamp,
+ final boolean confirmationSent) {
+ final DBTGattCharacteristic cd = (DBTGattCharacteristic)charDecl;
+ if( !cd.equals(DBTGattCharacteristic.this) ) {
+ throw new InternalError("Filtered GATTCharacteristicListener.indicationReceived: Wrong Characteristic: Got "+charDecl+
+ ", expected "+DBTGattCharacteristic.this.toString());
+ }
+ final boolean valueChanged = updateCachedValue(value, true);
+ if( DEBUG ) {
+ System.err.println("GATTCharacteristicListener.indicationReceived: "+charDecl+
+ ", value[changed "+valueChanged+", data "+BluetoothUtils.bytesHexString(value, true, true)+"]");
+ }
+ }
+ };
+ this.addCharacteristicListener(characteristicListener); // silent, don't enable native GATT ourselves
+ }
}
@Override
@@ -166,8 +154,7 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha
if( !isValid() ) {
return;
}
- removeAllCharacteristicListener();
- disableValueNotifications();
+ removeAllAssociatedCharacteristicListener(true);
super.close();
}
@@ -235,57 +222,74 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha
public final List<BluetoothGattDescriptor> getDescriptors() { return descriptorList; }
@Override
- public final synchronized void enableValueNotifications(final BluetoothNotification<byte[]> callback) {
- final boolean res = enableValueNotificationsImpl(true);
- if( DEBUG ) {
- System.err.println("GATTCharacteristicListener.enableValueNotifications: GATT Native: "+res);
- }
- valueNotificationCB = callback;
- }
-
- @Override
- public final synchronized void disableValueNotifications() {
- try {
- final boolean res = enableValueNotificationsImpl(false);
+ public final synchronized boolean configNotificationIndication(final boolean enableNotification, final boolean enableIndication, final boolean enabledState[/*2*/])
+ throws IllegalStateException
+ {
+ if( hasNotify || hasIndicate ) {
+ final boolean res = configNotificationIndicationImpl(enableNotification, enableIndication, enabledState);
if( DEBUG ) {
- System.err.println("GATTCharacteristicListener.disableValueNotifications: GATT Native: "+res);
+ System.err.println("GATTCharacteristicListener.configNotificationIndication: "+res+", enableResult "+Arrays.toString(enabledState));
}
- } catch (final Throwable t) {
+ return res;
+ } else {
+ enabledState[0] = false;
+ enabledState[1] = false;
if( DEBUG ) {
- System.err.println("Caught "+t.getMessage());
- t.printStackTrace();
+ System.err.println("GATTCharacteristicListener.configNotificationIndication: FALSE*");
}
+ return false;
}
- valueNotificationCB = null;
}
+ private native boolean configNotificationIndicationImpl(boolean enableNotification, boolean enableIndication, final boolean enabledState[/*2*/])
+ throws IllegalStateException;
@Override
- public final boolean getNotifying() {
- return null != valueNotificationCB;
+ public final boolean addCharacteristicListener(final GATTCharacteristicListener listener) {
+ return getService().getDevice().addCharacteristicListener(listener);
}
@Override
- public final boolean addCharacteristicListener(final GATTCharacteristicListener listener) {
- return addCharacteristicListener(listener, true);
- }
- private final boolean addCharacteristicListener(final GATTCharacteristicListener listener, final boolean nativeEnable) {
- if( nativeEnable ) {
- final boolean res = enableValueNotificationsImpl(true);
- if( DEBUG ) {
- System.err.println("GATTCharacteristicListener.addCharacteristicListener: GATT Native: "+res);
- }
+ public final boolean addCharacteristicListener(final GATTCharacteristicListener listener, final boolean enabledState[/*2*/]) {
+ if( !configNotificationIndication(true /* enableNotification */, true /* enableIndication */, enabledState) ) {
+ return false;
}
- return getService().getDevice().addCharacteristicListener(listener, this);
+ return getService().getDevice().addCharacteristicListener(listener);
}
@Override
- public final boolean removeCharacteristicListener(final GATTCharacteristicListener l) {
+ public final boolean removeCharacteristicListener(final GATTCharacteristicListener l, final boolean disableIndicationNotification) {
+ if( disableIndicationNotification ) {
+ configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]);
+ }
return getService().getDevice().removeCharacteristicListener(l);
}
@Override
- public final int removeAllCharacteristicListener() {
- return getService().getDevice().removeAllCharacteristicListener();
+ public final int removeAllAssociatedCharacteristicListener(final boolean disableIndicationNotification) {
+ if( disableIndicationNotification ) {
+ configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]);
+ }
+ return getService().getDevice().removeAllAssociatedCharacteristicListener(this);
+ }
+
+ @Override
+ public final synchronized void enableValueNotifications(final BluetoothNotification<byte[]> callback) {
+ if( !configNotificationIndication(true /* enableNotification */, true /* enableIndication */, null) ) {
+ valueNotificationCB = null;
+ } else {
+ valueNotificationCB = callback;
+ }
+ }
+
+ @Override
+ public final synchronized void disableValueNotifications() {
+ configNotificationIndication(false /* enableNotification */, false /* enableIndication */, null);
+ valueNotificationCB = null;
+ }
+
+ @Override
+ public final boolean getNotifying() {
+ return null != valueNotificationCB;
}
/**
@@ -325,11 +329,6 @@ public class DBTGattCharacteristic extends DBTObject implements BluetoothGattCha
private native List<BluetoothGattDescriptor> getDescriptorsImpl();
- /**
- * Enables disables GATT notification and/or indication.
- */
- private native boolean enableValueNotificationsImpl(boolean v);
-
@Override
protected native void deleteImpl(long nativeInstance);
diff --git a/java/jni/BluetoothUtils.cxx b/java/jni/BluetoothUtils.cxx
index b05ec66f..8278b8a7 100644
--- a/java/jni/BluetoothUtils.cxx
+++ b/java/jni/BluetoothUtils.cxx
@@ -70,7 +70,7 @@ jstring Java_org_tinyb_BluetoothUtils_decodeUTF8String(JNIEnv *env, jclass clazz
throw std::invalid_argument(msg.c_str());
}
- JNICriticalArray<uint8_t> criticalArray(env); // RAII - release
+ JNICriticalArray<uint8_t, jbyteArray> criticalArray(env); // RAII - release
uint8_t * buffer_ptr = criticalArray.get(jbuffer, criticalArray.Mode::NO_UPDATE_AND_RELEASE);
if( NULL == buffer_ptr ) {
throw std::invalid_argument("GetPrimitiveArrayCritical(byte array) is null");
diff --git a/java/jni/JNIMem.hpp b/java/jni/JNIMem.hpp
index 6cdc832a..03ec4948 100644
--- a/java/jni/JNIMem.hpp
+++ b/java/jni/JNIMem.hpp
@@ -121,7 +121,7 @@ public:
*
* RAII-style acquire and relinquish via destructor
*/
-template <typename T>
+template <typename T, typename U>
class JNICriticalArray {
public:
enum Mode : jint {
@@ -138,7 +138,7 @@ public:
private:
JNIEnv *env;
Mode mode = UPDATE_AND_RELEASE;
- jbyteArray jarray = nullptr;
+ U jarray = nullptr;
T* narray = nullptr;
jboolean isCopy = false;
@@ -173,7 +173,7 @@ public:
/**
* Acquired the primitive array.
*/
- T* get(jbyteArray jarray, Mode mode=UPDATE_AND_RELEASE) {
+ T* get(U jarray, Mode mode=UPDATE_AND_RELEASE) {
if( nullptr == jarray ) {
return nullptr;
}
diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx
index cd073cb9..ae57644a 100644
--- a/java/jni/direct_bt/DBTDevice.cxx
+++ b/java/jni/direct_bt/DBTDevice.cxx
@@ -59,26 +59,28 @@ class JNICharacteristicListener : public GATTCharacteristicListener {
};
*/
- const GATTCharacteristic * characteristicMatchRef;
- std::shared_ptr<JavaAnonObj> deviceObjRef;
- std::unique_ptr<JNIGlobalRef> listenerObjRef;
+ const GATTCharacteristic * associatedCharacteristicRef;
+ JNIGlobalRef listenerObj; // keep listener instance alive
+ JNIGlobalRef associatedCharacteristicObj; // keeps associated characteristic alive, if not null
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());
+ JNICharacteristicListener(JNIEnv *env, DBTDevice *device, jobject listener, GATTCharacteristic * associatedCharacteristicRef)
+ : associatedCharacteristicRef(associatedCharacteristicRef),
+ listenerObj(listener)
+ {
+ jclass listenerClazz = search_class(env, listenerObj.getObject());
java_exception_check_and_throw(env, E_FILE_LINE);
if( nullptr == listenerClazz ) {
throw InternalError("CharacteristicListener not found", E_FILE_LINE);
}
- this->characteristicMatchRef = characteristicMatchRef;
+ 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);
@@ -93,10 +95,10 @@ class JNICharacteristicListener : public GATTCharacteristicListener {
}
bool match(const GATTCharacteristic & characteristic) override {
- if( nullptr == characteristicMatchRef ) {
+ if( nullptr == associatedCharacteristicRef ) {
return true;
}
- return characteristic == *characteristicMatchRef;
+ return characteristic == *associatedCharacteristicRef;
}
void notificationReceived(GATTCharacteristicRef charDecl,
@@ -111,7 +113,7 @@ class JNICharacteristicListener : public GATTCharacteristicListener {
java_exception_check_and_throw(env, E_FILE_LINE);
- env->CallVoidMethod(listenerObjRef->getObject(), mNotificationReceived,
+ env->CallVoidMethod(listenerObj.getObject(), mNotificationReceived,
jCharDecl, jvalue, (jlong)timestamp);
java_exception_check_and_throw(env, E_FILE_LINE);
}
@@ -129,7 +131,7 @@ class JNICharacteristicListener : public GATTCharacteristicListener {
java_exception_check_and_throw(env, E_FILE_LINE);
- env->CallVoidMethod(listenerObjRef->getObject(), mIndicationReceived,
+ env->CallVoidMethod(listenerObj.getObject(), mIndicationReceived,
jCharDecl, jvalue, (jlong)timestamp, (jboolean)confirmationSent);
java_exception_check_and_throw(env, E_FILE_LINE);
}
@@ -168,16 +170,16 @@ jstring Java_direct_1bt_tinyb_DBTDevice_toStringImpl(JNIEnv *env, jobject obj) {
return nullptr;
}
-jboolean Java_direct_1bt_tinyb_DBTDevice_addCharacteristicListener(JNIEnv *env, jobject obj, jobject listener, jobject jcharacteristicMatch) {
+jboolean Java_direct_1bt_tinyb_DBTDevice_addCharacteristicListener(JNIEnv *env, jobject obj, jobject listener, jobject jAssociatedCharacteristic) {
try {
if( nullptr == listener ) {
- throw IllegalArgumentException("characteristicListener is null", E_FILE_LINE);
+ throw IllegalArgumentException("characteristicListener argument 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");
+ throw IllegalStateException("CharacteristicListener's nativeInstance not null, already in use", E_FILE_LINE);
return false;
}
}
@@ -188,14 +190,13 @@ jboolean Java_direct_1bt_tinyb_DBTDevice_addCharacteristicListener(JNIEnv *env,
throw IllegalStateException("Characteristic's device GATTHandle not connected: "+ device->toString(), E_FILE_LINE);
}
- GATTCharacteristic * characteristicMatchRef = nullptr;
- if( nullptr != jcharacteristicMatch ) {
- characteristicMatchRef = getInstance<GATTCharacteristic>(env, jcharacteristicMatch);
- JavaGlobalObj::check(characteristicMatchRef->getJavaObject(), E_FILE_LINE);
+ GATTCharacteristic * associatedCharacteristicRef = nullptr;
+ if( nullptr != jAssociatedCharacteristic ) {
+ associatedCharacteristicRef = getInstance<GATTCharacteristic>(env, jAssociatedCharacteristic);
}
std::shared_ptr<GATTCharacteristicListener> l =
- std::shared_ptr<GATTCharacteristicListener>( new JNICharacteristicListener(env, device, listener, characteristicMatchRef) );
+ std::shared_ptr<GATTCharacteristicListener>( new JNICharacteristicListener(env, device, listener, associatedCharacteristicRef) );
if( gatt->addCharacteristicListener(l) ) {
setInstance(env, listener, l.get());
@@ -207,18 +208,18 @@ jboolean Java_direct_1bt_tinyb_DBTDevice_addCharacteristicListener(JNIEnv *env,
return JNI_FALSE;
}
-jboolean Java_direct_1bt_tinyb_DBTDevice_removeCharacteristicListener(JNIEnv *env, jobject obj, jobject statusListener) {
+jboolean Java_direct_1bt_tinyb_DBTDevice_removeCharacteristicListener(JNIEnv *env, jobject obj, jobject jlistener) {
try {
- if( nullptr == statusListener ) {
- throw IllegalArgumentException("characteristicListener is null", E_FILE_LINE);
+ if( nullptr == jlistener ) {
+ throw IllegalArgumentException("characteristicListener argument is null", E_FILE_LINE);
}
JNICharacteristicListener * pre =
- getObjectRef<JNICharacteristicListener>(env, statusListener, "nativeInstance");
+ getObjectRef<JNICharacteristicListener>(env, jlistener, "nativeInstance");
if( nullptr == pre ) {
WARN_PRINT("characteristicListener's nativeInstance is null, not in use");
return false;
}
- setObjectRef<JNICharacteristicListener>(env, statusListener, nullptr, "nativeInstance");
+ setObjectRef<JNICharacteristicListener>(env, jlistener, nullptr, "nativeInstance");
DBTDevice *device = getInstance<DBTDevice>(env, obj);
JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
@@ -240,6 +241,30 @@ jboolean Java_direct_1bt_tinyb_DBTDevice_removeCharacteristicListener(JNIEnv *en
return JNI_FALSE;
}
+jint Java_direct_1bt_tinyb_DBTDevice_removeAllAssociatedCharacteristicListener(JNIEnv *env, jobject obj, jobject jAssociatedCharacteristic) {
+ try {
+ if( nullptr == jAssociatedCharacteristic ) {
+ throw IllegalArgumentException("associatedCharacteristic argument is null", E_FILE_LINE);
+ }
+ DBTDevice *device = getInstance<DBTDevice>(env, obj);
+ JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
+ std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
+ if( nullptr == gatt ) {
+ // OK to have GATTHandler being shutdown @ disable
+ DBG_PRINT("Characteristic's device GATTHandle not connected: %s", device->toString().c_str());
+ return 0;
+ }
+
+ GATTCharacteristic * associatedCharacteristicRef = getInstance<GATTCharacteristic>(env, jAssociatedCharacteristic);
+ JavaGlobalObj::check(associatedCharacteristicRef->getJavaObject(), E_FILE_LINE);
+
+ return gatt->removeAllAssociatedCharacteristicListener(associatedCharacteristicRef);
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return 0;
+}
+
jint Java_direct_1bt_tinyb_DBTDevice_removeAllCharacteristicListener(JNIEnv *env, jobject obj) {
try {
DBTDevice *device = getInstance<DBTDevice>(env, obj);
diff --git a/java/jni/direct_bt/DBTGattCharacteristic.cxx b/java/jni/direct_bt/DBTGattCharacteristic.cxx
index d7541e2e..f376618e 100644
--- a/java/jni/direct_bt/DBTGattCharacteristic.cxx
+++ b/java/jni/direct_bt/DBTGattCharacteristic.cxx
@@ -145,7 +145,7 @@ jboolean Java_direct_1bt_tinyb_DBTGattCharacteristic_writeValueImpl(JNIEnv *env,
GATTCharacteristic *characteristic = getInstance<GATTCharacteristic>(env, obj);
JavaGlobalObj::check(characteristic->getJavaObject(), E_FILE_LINE);
- JNICriticalArray<uint8_t> criticalArray(env); // RAII - release
+ JNICriticalArray<uint8_t, jbyteArray> 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);
@@ -162,16 +162,31 @@ jboolean Java_direct_1bt_tinyb_DBTGattCharacteristic_writeValueImpl(JNIEnv *env,
return JNI_FALSE;
}
-jboolean Java_direct_1bt_tinyb_DBTGattCharacteristic_enableValueNotificationsImpl(JNIEnv *env, jobject obj, jboolean enable) {
+jboolean Java_direct_1bt_tinyb_DBTGattCharacteristic_configNotificationIndicationImpl(JNIEnv *env, jobject obj,
+ jboolean enableNotification, jboolean enableIndication, jbooleanArray jEnabledState) {
try {
GATTCharacteristic *characteristic = getInstance<GATTCharacteristic>(env, obj);
JavaGlobalObj::check(characteristic->getJavaObject(), E_FILE_LINE);
+ if( nullptr == jEnabledState ) {
+ throw IllegalArgumentException("boolean array null", E_FILE_LINE);
+ }
+ const int state_size = env->GetArrayLength(jEnabledState);
+ if( 2 > state_size ) {
+ throw IllegalArgumentException("boolean array smaller than 2, length "+std::to_string(state_size), E_FILE_LINE);
+ }
+ JNICriticalArray<jboolean, jbooleanArray> criticalArray(env); // RAII - release
+ jboolean * state_ptr = criticalArray.get(jEnabledState, criticalArray.Mode::UPDATE_AND_RELEASE);
+ if( NULL == state_ptr ) {
+ throw InternalError("GetPrimitiveArrayCritical(boolean array) is null", E_FILE_LINE);
+ }
+
bool cccdEnableResult[2];
- bool res = characteristic->configIndicationNotification(enable, enable, cccdEnableResult);
- DBG_PRINT("DBTGattCharacteristic::configIndicationNotification Config Notification(%d), Indication(%d): Result %d",
+ bool res = characteristic->configNotificationIndication(enableNotification, enableIndication, cccdEnableResult);
+ DBG_PRINT("DBTGattCharacteristic::configNotificationIndication Config Notification(%d), Indication(%d): Result %d",
cccdEnableResult[0], cccdEnableResult[1], res);
- (void) cccdEnableResult;
+ state_ptr[0] = cccdEnableResult[0];
+ state_ptr[1] = cccdEnableResult[1];
return res;
} catch(...) {
rethrow_and_raise_java_exception(env);
diff --git a/java/jni/direct_bt/DBTGattDescriptor.cxx b/java/jni/direct_bt/DBTGattDescriptor.cxx
index 081fda33..066e9f5f 100644
--- a/java/jni/direct_bt/DBTGattDescriptor.cxx
+++ b/java/jni/direct_bt/DBTGattDescriptor.cxx
@@ -92,7 +92,7 @@ jboolean Java_direct_1bt_tinyb_DBTGattDescriptor_writeValueImpl(JNIEnv *env, job
GATTDescriptor *descriptor = getInstance<GATTDescriptor>(env, obj);
JavaGlobalObj::check(descriptor->getJavaObject(), E_FILE_LINE);
- JNICriticalArray<uint8_t> criticalArray(env); // RAII - release
+ JNICriticalArray<uint8_t, jbyteArray> 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);
diff --git a/java/jni/direct_bt/DBTGattService.cxx b/java/jni/direct_bt/DBTGattService.cxx
index fcb234e8..7023d755 100644
--- a/java/jni/direct_bt/DBTGattService.cxx
+++ b/java/jni/direct_bt/DBTGattService.cxx
@@ -60,7 +60,7 @@ void Java_direct_1bt_tinyb_DBTGattService_deleteImpl(JNIEnv *env, jobject obj, j
}
}
-static const std::string _characteristicClazzCtorArgs("(JLdirect_bt/tinyb/DBTGattService;S[Ljava/lang/String;Ljava/lang/String;SI)V");
+static const std::string _characteristicClazzCtorArgs("(JLdirect_bt/tinyb/DBTGattService;S[Ljava/lang/String;ZZLjava/lang/String;SI)V");
jobject Java_direct_1bt_tinyb_DBTGattService_getCharacteristicsImpl(JNIEnv *env, jobject obj) {
try {
@@ -71,6 +71,7 @@ jobject Java_direct_1bt_tinyb_DBTGattService_getCharacteristicsImpl(JNIEnv *env,
// DBTGattCharacteristic(final long nativeInstance, final DBTGattService service,
// final short handle, final String[] properties,
+ // final boolean hasNotify, final boolean hasIndicate,
// final String value_type_uuid, final short value_handle,
// final int clientCharacteristicsConfigIndex)
@@ -97,13 +98,16 @@ jobject Java_direct_1bt_tinyb_DBTGattService_getCharacteristicsImpl(JNIEnv *env,
}
java_exception_check_and_throw(env, E_FILE_LINE);
+ const bool hasNotify = characteristic->hasProperties(GATTCharacteristic::PropertyBitVal::Notify);
+ const bool hasIndicate = characteristic->hasProperties(GATTCharacteristic::PropertyBitVal::Indicate);
+
const jstring uuid = from_string_to_jstring(env,
directBTJNISettings.getUnifyUUID128Bit() ? characteristic->value_type->toUUID128String() :
characteristic->value_type->toString());
java_exception_check_and_throw(env, E_FILE_LINE);
jobject jchar = env->NewObject(clazz, clazz_ctor, (jlong)characteristic, jservice,
- characteristic->handle, jproperties,
+ characteristic->handle, jproperties, hasNotify, hasIndicate,
uuid, characteristic->value_handle, characteristic->clientCharacteristicsConfigIndex);
java_exception_check_and_throw(env, E_FILE_LINE);
JNIGlobalRef::check(jchar, E_FILE_LINE);
diff --git a/java/jni/direct_bt/helper_dbt.hpp b/java/jni/direct_bt/helper_dbt.hpp
index d71104e7..abd78b50 100644
--- a/java/jni/direct_bt/helper_dbt.hpp
+++ b/java/jni/direct_bt/helper_dbt.hpp
@@ -106,6 +106,10 @@ namespace direct_bt {
/* Provides access to the stored GlobalRef as a jclass. */
jclass getClass() const { return javaObjectRef.getClass(); }
+ /* Provides access to the stored GlobalRef as an getJavaObject. */
+ static JNIGlobalRef GetJavaObject(const std::shared_ptr<JavaAnonObj> & shref) {
+ return static_cast<JavaGlobalObj*>(shref.get())->getJavaObject();
+ }
/* Provides access to the stored GlobalRef as an jobject. */
static jobject GetObject(const std::shared_ptr<JavaAnonObj> & shref) {
return static_cast<JavaGlobalObj*>(shref.get())->getObject();
diff --git a/java/org/tinyb/BluetoothDevice.java b/java/org/tinyb/BluetoothDevice.java
index 0db213f0..d3b004f9 100644
--- a/java/org/tinyb/BluetoothDevice.java
+++ b/java/org/tinyb/BluetoothDevice.java
@@ -443,16 +443,20 @@ public interface BluetoothDevice extends BluetoothObject
/**
* Add the given {@link GATTCharacteristicListener} to the listener list if not already present.
* @param listener A {@link GATTCharacteristicListener} instance, listening to all {@link BluetoothGattCharacteristic} events of this device
- * @param characteristicMatch Optional {@link BluetoothGattCharacteristic} to be matched before calling any
- * {@link GATTCharacteristicListener} methods. Pass {@code null} for no filtering.
* @return true if the given listener is not element of the list and has been newly added, otherwise false.
+ * @throws IllegalStateException if the {@link BluetoothDevice}'s GATTHandler is null, i.e. not connected
+ * @throws IllegalStateException if the given {@link GATTCharacteristicListener} is already in use, i.e. added.
* @since 2.0.0
* @implNote not implemented in tinyb.dbus
*/
- public boolean addCharacteristicListener(final GATTCharacteristicListener listener, final BluetoothGattCharacteristic characteristicMatch);
+ public boolean addCharacteristicListener(final GATTCharacteristicListener listener)
+ throws IllegalStateException;
/**
* Remove the given {@link GATTCharacteristicListener} from the listener list.
+ * <p>
+ * If the {@link BluetoothDevice}'s GATTHandler is null, i.e. not connected, {@code false} is being returned.
+ * </p>
* @param listener A {@link GATTCharacteristicListener} instance
* @return true if the given listener is an element of the list and has been removed, otherwise false.
* @since 2.0.0
@@ -461,6 +465,19 @@ public interface BluetoothDevice extends BluetoothObject
public boolean removeCharacteristicListener(final GATTCharacteristicListener l);
/**
+ * Remove all {@link GATTCharacteristicListener} from the list, which are associated to the given {@link BluetoothGattCharacteristic}.
+ * <p>
+ * Implementation tests all listener's {@link GATTCharacteristicListener#getAssociatedCharacteristic()}
+ * to match with the given associated characteristic.
+ * </p>
+ * @param associatedCharacteristic the match criteria to remove any GATTCharacteristicListener from the list
+ * @return number of removed listener.
+ * @since 2.0.0
+ * @implNote not implemented in tinyb.dbus
+ */
+ public int removeAllAssociatedCharacteristicListener(final BluetoothGattCharacteristic associatedCharacteristic);
+
+ /**
* Remove all {@link GATTCharacteristicListener} from the list.
* @return number of removed listener.
* @since 2.0.0
diff --git a/java/org/tinyb/BluetoothGattCharacteristic.java b/java/org/tinyb/BluetoothGattCharacteristic.java
index 27c0bccc..586e7b2c 100644
--- a/java/org/tinyb/BluetoothGattCharacteristic.java
+++ b/java/org/tinyb/BluetoothGattCharacteristic.java
@@ -71,31 +71,112 @@ public interface BluetoothGattCharacteristic extends BluetoothObject
public byte[] readValue() throws BluetoothException;
/**
+ * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
+ * <p>
+ * Method enables notification and/or indication for this characteristic at BLE level.
+ * </p>
+ * <p>
+ * Implementation masks this Characteristic properties PropertyBitVal::Notify and PropertyBitVal::Indicate
+ * with the respective user request parameters, hence removes unsupported requests.
+ * </p>
+ * @param enableNotification
+ * @param enableIndication
+ * @param enabledState array of size 2, holding the resulting enabled state for notification and indication.
+ * @return false if this characteristic has no PropertyBitVal::Notify or PropertyBitVal::Indication present,
+ * or there is no GATTDescriptor of type ClientCharacteristicConfiguration, or if the operation has failed.
+ * Otherwise returns true.
+ * @throws IllegalStateException if notification or indication is set to be enabled
+ * and the {@link BluetoothDevice}'s GATTHandler is null, i.e. not connected
+ * @since 2.0.0
+ * @implNote not implemented in tinyb.dbus
+ */
+ public boolean configNotificationIndication(final boolean enableNotification, final boolean enableIndication, final boolean enabledState[/*2*/])
+ throws IllegalStateException;
+
+ /**
* Add the given {@link GATTCharacteristicListener} to the listener list if not already present.
+ * <p>
+ * Occurring notifications and indications, if enabled via {#link {@link #configNotificationIndication(boolean, boolean, boolean[])}},
+ * will call the respective GATTCharacteristicListener callback method.
+ * </p>
* @param listener A {@link GATTCharacteristicListener} instance, listening to this {@link BluetoothGattCharacteristic}'s events
* @return true if the given listener is not element of the list and has been newly added, otherwise false.
+ * @throws IllegalStateException if the DBTDevice's GATTHandler is null, i.e. not connected
+ * @throws IllegalStateException if the given {@link GATTCharacteristicListener} is already in use, i.e. added.
+ * @since 2.0.0
+ * @implNote not implemented in tinyb.dbus
+ */
+ public boolean addCharacteristicListener(final GATTCharacteristicListener listener)
+ throws IllegalStateException;
+
+ /**
+ * Add the given {@link GATTCharacteristicListener} to the listener list if not already present
+ * and if enabling the notification and/or indication for this characteristic at BLE level was successful.
+ * <p>
+ * Occurring notifications and indications will call the respective {@link GATTCharacteristicListener}
+ * callback method.
+ * </p>
+ * @param listener A {@link GATTCharacteristicListener} instance, listening to this {@link BluetoothGattCharacteristic}'s events
+ * @param enabledState array of size 2, holding the resulting enabled state for notification and indication
+ * using #configNotificationIndication(boolean, boolean, boolean[])
+ * @return true if enabling the notification and/or indication was successful
+ * and if the given listener is not element of the list and has been newly added, otherwise false.
+ * @throws IllegalStateException if the {@link BluetoothDevice}'s GATTHandler is null, i.e. not connected
+ * @throws IllegalStateException if the given {@link GATTCharacteristicListener} is already in use, i.e. added.
+ * @see #configNotificationIndication(boolean, boolean, boolean[])
* @since 2.0.0
+ * @implNote not implemented in tinyb.dbus
*/
- public boolean addCharacteristicListener(final GATTCharacteristicListener listener);
+ public boolean addCharacteristicListener(final GATTCharacteristicListener listener, final boolean enabledState[/*2*/])
+ throws IllegalStateException;
/**
- * Remove the given {@link GATTCharacteristicListener} from the listener list.
+ * Disables the notification and/or indication for this characteristic at BLE level
+ * if {@code disableIndicationNotification == true}
+ * and removes the given {@link GATTCharacteristicListener} from the listener list.
+ * <p>
+ * If the DBTDevice's GATTHandler is null, i.e. not connected, {@code false} is being returned.
+ * </p>
+ *
* @param listener A {@link GATTCharacteristicListener} instance
+ * @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic
+ * using {@link #configNotificationIndication(boolean, boolean, boolean[])
* @return true if the given listener is an element of the list and has been removed, otherwise false.
+ * @see #configNotificationIndication(boolean, boolean, boolean[])
* @since 2.0.0
+ * @implNote not implemented in tinyb.dbus
*/
- public boolean removeCharacteristicListener(final GATTCharacteristicListener l);
+ public boolean removeCharacteristicListener(final GATTCharacteristicListener l, final boolean disableIndicationNotification);
/**
- * Remove all {@link GATTCharacteristicListener} from the list.
+ * Disables the notification and/or indication for this characteristic BLE level
+ * if {@code disableIndicationNotification == true}
+ * and removes all {@link GATTCharacteristicListener} from the listener list,
+ * which are associated with this characteristic instance.
+ * <p>
+ * Implementation tests all listener's {@link GATTCharacteristicListener#getAssociatedCharacteristic()}
+ * to match with this characteristic instance.
+ * </p>
+ * <p>
+ * If the DBTDevice's GATTHandler is null, i.e. not connected, {@code false} is being returned.
+ * </p>
+ *
+ * @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic
+ * using {@link #configNotificationIndication(boolean, boolean, boolean[])
* @return number of removed listener.
+ * @see #configNotificationIndication(boolean, boolean, boolean[])
+ * @see BluetoothDevice#removeAllAssociatedCharacteristicListener(BluetoothGattCharacteristic)
* @since 2.0.0
+ * @implNote not implemented in tinyb.dbus
*/
- public int removeAllCharacteristicListener();
+ public int removeAllAssociatedCharacteristicListener(final boolean disableIndicationNotification);
/**
- * Enables notifications for the value and calls run function of the BluetoothNotification
- * object. It enables notifications for this characteristic at BLE level.
+ * Sets the given value BluetoothNotification to have its run function
+ * receive the enabled notification and/or indication sent.
+ * <p>
+ * Enables notification and/or indication for this characteristic at BLE level.
+ * </p.
* @param callback A BluetoothNotification<byte[]> object. Its run function will be called
* when a notification is issued. The run function will deliver the new value of the value
* property.
@@ -104,7 +185,7 @@ public interface BluetoothGattCharacteristic extends BluetoothObject
/**
* Disables notifications of the value and unregisters the callback object
- * passed through the corresponding enable method. It disables notications
+ * passed through the corresponding enable method. It disables notifcations
* at BLE level for this characteristic.
*/
public void disableValueNotifications();
diff --git a/java/org/tinyb/BluetoothGattService.java b/java/org/tinyb/BluetoothGattService.java
index 797a2e82..52f766ea 100644
--- a/java/org/tinyb/BluetoothGattService.java
+++ b/java/org/tinyb/BluetoothGattService.java
@@ -89,18 +89,30 @@ public interface BluetoothGattService extends BluetoothObject
/**
* Adds the given {@link GATTCharacteristicListener} to the {@link BluetoothDevice} for all {@link BluetoothGattCharacteristic}s.
* @param listener {@link GATTCharacteristicListener} to add to the {@link BluetoothDevice}.
+ * It is important to have hte listener's {@link GATTCharacteristicListener#getAssociatedCharacteristic() associated characteristic} == null,
+ * otherwise the listener can't be used for all characteristics.
* @return true if successful, otherwise false
+ * @throws IllegalArgumentException if listener's {@link GATTCharacteristicListener#getAssociatedCharacteristic() associated characteristic}
+ * is not null.
* @since 2.0.0
* @implNote not implemented in tinyb.dbus
+ * @see BluetoothGattCharacteristic#configNotificationIndication(boolean, boolean, boolean[])
+ * @see BluetoothDevice#addCharacteristicListener(GATTCharacteristicListener, BluetoothGattCharacteristic)
*/
- public static boolean addCharacteristicListenerToAll(final BluetoothDevice device, final List<BluetoothGattService> services, final GATTCharacteristicListener listener) {
- final boolean res = device.addCharacteristicListener(listener, null /* for all */);
+ public static boolean addCharacteristicListenerToAll(final BluetoothDevice device, final List<BluetoothGattService> services,
+ final GATTCharacteristicListener listener) {
+ if( null == listener ) {
+ throw new IllegalArgumentException("listener argument null");
+ }
+ if( null != listener.getAssociatedCharacteristic() ) {
+ throw new IllegalArgumentException("listener's associated characteristic is not null");
+ }
+ final boolean res = device.addCharacteristicListener(listener);
for(final Iterator<BluetoothGattService> is = services.iterator(); is.hasNext(); ) {
final BluetoothGattService s = is.next();
final List<BluetoothGattCharacteristic> characteristics = s.getCharacteristics();
for(final Iterator<BluetoothGattCharacteristic> ic = characteristics.iterator(); ic.hasNext(); ) {
- final BluetoothGattCharacteristic c = ic.next();
- c.enableValueNotifications(null);
+ ic.next().configNotificationIndication(true /* enableNotification */, true /* enableIndication */, new boolean[2]);
}
}
return res;
@@ -112,14 +124,16 @@ public interface BluetoothGattService extends BluetoothObject
* @return true if successful, otherwise false
* @since 2.0.0
* @implNote not implemented in tinyb.dbus
+ * @see BluetoothGattCharacteristic#configNotificationIndication(boolean, boolean, boolean[])
+ * @see BluetoothDevice#removeCharacteristicListener(GATTCharacteristicListener)
*/
- public static boolean removeCharacteristicListenerFromAll(final BluetoothDevice device, final List<BluetoothGattService> services, final GATTCharacteristicListener listener) {
+ public static boolean removeCharacteristicListenerFromAll(final BluetoothDevice device, final List<BluetoothGattService> services,
+ final GATTCharacteristicListener listener) {
for(final Iterator<BluetoothGattService> is = services.iterator(); is.hasNext(); ) {
final BluetoothGattService s = is.next();
final List<BluetoothGattCharacteristic> characteristics = s.getCharacteristics();
for(final Iterator<BluetoothGattCharacteristic> ic = characteristics.iterator(); ic.hasNext(); ) {
- final BluetoothGattCharacteristic c = ic.next();
- c.disableValueNotifications();
+ ic.next().configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]);
}
}
return device.removeCharacteristicListener(listener);
@@ -130,18 +144,17 @@ public interface BluetoothGattService extends BluetoothObject
* @return count of removed {@link GATTCharacteristicListener}
* @since 2.0.0
* @implNote not implemented in tinyb.dbus
+ * @see BluetoothGattCharacteristic#configNotificationIndication(boolean, boolean, boolean[])
+ * @see BluetoothDevice#removeAllCharacteristicListener()
*/
public static int removeAllCharacteristicListener(final BluetoothDevice device, final List<BluetoothGattService> services) {
for(final Iterator<BluetoothGattService> is = services.iterator(); is.hasNext(); ) {
final BluetoothGattService s = is.next();
final List<BluetoothGattCharacteristic> characteristics = s.getCharacteristics();
for(final Iterator<BluetoothGattCharacteristic> ic = characteristics.iterator(); ic.hasNext(); ) {
- final BluetoothGattCharacteristic c = ic.next();
- c.disableValueNotifications();
+ ic.next().configNotificationIndication(false /* enableNotification */, false /* enableIndication */, new boolean[2]);
}
}
return device.removeAllCharacteristicListener();
}
-
-
}
diff --git a/java/org/tinyb/GATTCharacteristicListener.java b/java/org/tinyb/GATTCharacteristicListener.java
index 693b5532..f47e17d3 100644
--- a/java/org/tinyb/GATTCharacteristicListener.java
+++ b/java/org/tinyb/GATTCharacteristicListener.java
@@ -25,6 +25,8 @@
package org.tinyb;
+import java.lang.ref.WeakReference;
+
/**
* {@link BluetoothGattCharacteristic} event listener for notification and indication events.
* <p>
@@ -55,14 +57,59 @@ package org.tinyb;
public abstract class GATTCharacteristicListener {
@SuppressWarnings("unused")
private long nativeInstance;
+ private final WeakReference<BluetoothGattCharacteristic> associatedCharacteristic;
+
+ /**
+ * Returns the weakly associated {@link BluetoothGattCharacteristic} to this listener instance.
+ * <p>
+ * Returns {@code null} if no association has been made
+ * or if the associated {@link BluetoothGattCharacteristic} has been garbage collected.
+ * </p>
+ */
+ public final BluetoothGattCharacteristic getAssociatedCharacteristic() {
+ return null != associatedCharacteristic ? associatedCharacteristic.get() : null;
+ }
+ /**
+ * @param associatedCharacteristic weakly associates this listener instance to one {@link BluetoothGattCharacteristic},
+ * may be {@code null} for no association.
+ * @see #getAssociatedCharacteristic()
+ */
+ public GATTCharacteristicListener(final BluetoothGattCharacteristic associatedCharacteristic) {
+ if( null != associatedCharacteristic ) {
+ this.associatedCharacteristic = new WeakReference<BluetoothGattCharacteristic>(associatedCharacteristic);
+ } else {
+ this.associatedCharacteristic = null;
+ }
+ }
+
+ /**
+ * Called from native BLE stack, initiated by a received notification associated
+ * with the given {@link BluetoothGattCharacteristic}.
+ * @param charDecl {@link BluetoothGattCharacteristic} related to this notification
+ * @param value the notification value
+ * @param timestamp the indication monotonic timestamp, see {@link BluetoothUtils#getCurrentMilliseconds()}
+ */
public void notificationReceived(final BluetoothGattCharacteristic charDecl,
final byte[] value, final long timestamp) {
}
+ /**
+ * Called from native BLE stack, initiated by a received indication associated
+ * with the given {@link BluetoothGattCharacteristic}.
+ * @param charDecl {@link BluetoothGattCharacteristic} related to this indication
+ * @param value the indication value
+ * @param timestamp the indication monotonic timestamp, see {@link BluetoothUtils#getCurrentMilliseconds()}
+ * @param confirmationSent if true, the native stack has sent the confirmation, otherwise user is required to do so.
+ */
public void indicationReceived(final BluetoothGattCharacteristic charDecl,
final byte[] value, final long timestamp,
final boolean confirmationSent) {
}
+ public String toString() {
+ final BluetoothGattCharacteristic c = getAssociatedCharacteristic();
+ final String cs = null != c ? c.toString() : "null";
+ return "GATTCharacteristicListener[associated "+cs+"]";
+ }
};
diff --git a/java/tinyb/dbus/DBusDevice.java b/java/tinyb/dbus/DBusDevice.java
index 023607ed..5bbb8f7d 100644
--- a/java/tinyb/dbus/DBusDevice.java
+++ b/java/tinyb/dbus/DBusDevice.java
@@ -233,7 +233,7 @@ public class DBusDevice extends DBusObject implements BluetoothDevice
public native void disableServicesResolvedNotifications();
@Override
- public boolean addCharacteristicListener(final GATTCharacteristicListener listener, final BluetoothGattCharacteristic characteristicMatch) {
+ public boolean addCharacteristicListener(final GATTCharacteristicListener listener) {
return false; // FIXME
}
@@ -243,6 +243,11 @@ public class DBusDevice extends DBusObject implements BluetoothDevice
}
@Override
+ public int removeAllAssociatedCharacteristicListener(final BluetoothGattCharacteristic associatedCharacteristic) {
+ return 0; // FIXME
+ }
+
+ @Override
public int removeAllCharacteristicListener() {
return 0; // FIXME
}
diff --git a/java/tinyb/dbus/DBusGattCharacteristic.java b/java/tinyb/dbus/DBusGattCharacteristic.java
index 719d4369..2cf874bc 100644
--- a/java/tinyb/dbus/DBusGattCharacteristic.java
+++ b/java/tinyb/dbus/DBusGattCharacteristic.java
@@ -102,16 +102,30 @@ public class DBusGattCharacteristic extends DBusObject implements BluetoothGattC
{
super(instance);
}
+
@Override
public boolean addCharacteristicListener(final GATTCharacteristicListener listener) {
return false; // FIXME
}
@Override
- public boolean removeCharacteristicListener(final GATTCharacteristicListener l) {
+ public boolean configNotificationIndication(final boolean enableNotification, final boolean enableIndication, final boolean[] enabledState)
+ throws IllegalStateException
+ {
+ return false; // FIXME
+ }
+
+ @Override
+ public boolean addCharacteristicListener(final GATTCharacteristicListener listener, final boolean[] enabledState)
+ throws IllegalStateException
+ {
+ return false; // FIXME
+ }
+ @Override
+ public boolean removeCharacteristicListener(final GATTCharacteristicListener l, final boolean disableIndicationNotification) {
return false; // FIXME
}
@Override
- public int removeAllCharacteristicListener() {
+ public int removeAllAssociatedCharacteristicListener(final boolean disableIndicationNotification) {
return 0; // FIXME
}
diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp
index 4a7fafee..bdcb4fd3 100644
--- a/src/direct_bt/DBTDevice.cpp
+++ b/src/direct_bt/DBTDevice.cpp
@@ -577,3 +577,42 @@ void DBTDevice::disconnectGATT() {
}
INFO_PRINT("DBTDevice::disconnectGATT: End");
}
+
+bool DBTDevice::addCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l) {
+ std::shared_ptr<GATTHandler> gatt = getGATTHandler();
+ if( nullptr == gatt ) {
+ throw IllegalStateException("Device's GATTHandle not connected: "+
+ toString() + ", " + toString(), E_FILE_LINE);
+ }
+ return gatt->addCharacteristicListener(l);
+}
+
+bool DBTDevice::removeCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l) {
+ std::shared_ptr<GATTHandler> gatt = getGATTHandler();
+ if( nullptr == gatt ) {
+ // OK to have GATTHandler being shutdown @ disable
+ DBG_PRINT("Device's GATTHandle not connected: %s, %s", toString().c_str(), device->toString().c_str());
+ return false;
+ }
+ return gatt->removeCharacteristicListener(l);
+}
+
+int DBTDevice::removeAllAssociatedCharacteristicListener(std::shared_ptr<GATTCharacteristic> associatedCharacteristic) {
+ std::shared_ptr<GATTHandler> gatt = getGATTHandler();
+ if( nullptr == gatt ) {
+ // OK to have GATTHandler being shutdown @ disable
+ DBG_PRINT("Device's GATTHandle not connected: %s, %s", toString().c_str(), device->toString().c_str());
+ return false;
+ }
+ return gatt->removeAllAssociatedCharacteristicListener( associatedCharacteristic );
+}
+
+int DBTDevice::removeAllCharacteristicListener() {
+ std::shared_ptr<GATTHandler> gatt = getGATTHandler();
+ if( nullptr == gatt ) {
+ // OK to have GATTHandler being shutdown @ disable
+ DBG_PRINT("Device's GATTHandle not connected: %s, %s", toString().c_str(), device->toString().c_str());
+ return 0;
+ }
+ return gatt->removeAllCharacteristicListener();
+}
diff --git a/src/direct_bt/GATTCharacteristic.cpp b/src/direct_bt/GATTCharacteristic.cpp
index c6de3092..b36091b4 100644
--- a/src/direct_bt/GATTCharacteristic.cpp
+++ b/src/direct_bt/GATTCharacteristic.cpp
@@ -144,11 +144,16 @@ std::string GATTCharacteristic::toString() const {
service_name+" ]";
}
-bool GATTCharacteristic::configIndicationNotification(const bool enableNotification, const bool enableIndication, bool enableResult[2]) {
+bool GATTCharacteristic::configNotificationIndication(const bool enableNotification, const bool enableIndication, bool enabledState[2]) {
+ enabledState[0] = false;
+ enabledState[1] = false;
+
const bool hasEnableNotification = hasProperties(GATTCharacteristic::PropertyBitVal::Notify);
const bool hasEnableIndication = hasProperties(GATTCharacteristic::PropertyBitVal::Indicate);
- const bool resEnableNotification = hasEnableNotification & enableNotification;
- const bool resEnableIndication = hasEnableIndication & enableIndication;
+ if( !hasEnableNotification && !hasEnableIndication ) {
+ DBG_PRINT("Characteristic has neither Notify nor Indicate property present: %s", toString().c_str());
+ return false;
+ }
std::shared_ptr<DBTDevice> device = getDevice();
std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
@@ -161,20 +166,20 @@ bool GATTCharacteristic::configIndicationNotification(const bool enableNotificat
throw IllegalStateException("Characteristic's device GATTHandle not connected: "+
toString() + ", " + device->toString(), E_FILE_LINE);
}
+ const bool resEnableNotification = hasEnableNotification & enableNotification;
+ const bool resEnableIndication = hasEnableIndication & enableIndication;
+
GATTDescriptorRef cccd = this->getClientCharacteristicConfig();
if( nullptr == cccd ) {
DBG_PRINT("Characteristic has no ClientCharacteristicConfig descriptor: %s", toString().c_str());
return false;
}
- bool res = gatt->configIndicationNotification(*cccd, resEnableNotification, resEnableIndication);
+ bool res = gatt->configNotificationIndication(*cccd, resEnableNotification, resEnableIndication);
if( res ) {
- enableResult[0] = resEnableNotification;
- enableResult[1] = resEnableIndication;
- } else {
- enableResult[0] = false;
- enableResult[1] = false;
+ enabledState[0] = resEnableNotification;
+ enabledState[1] = resEnableIndication;
}
- DBG_PRINT("GATTCharacteristic::configIndicationNotification: res %d, notification[shall %d, has %d = %d], indication[shall %s, has %d = %d]",
+ DBG_PRINT("GATTCharacteristic::configNotificationIndication: res %d, notification[shall %d, has %d = %d], indication[shall %s, has %d = %d]",
res,
enableNotification. hasEnableNotification, resEnableNotification,
enableIndication. hasEnableIndication, resEnableIndication);
@@ -182,46 +187,30 @@ bool GATTCharacteristic::configIndicationNotification(const bool enableNotificat
}
bool GATTCharacteristic::addCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l) {
- std::shared_ptr<DBTDevice> device = getDevice();
- std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
- if( nullptr == gatt ) {
- throw IllegalStateException("Characteristic's device GATTHandle not connected: "+
- toString() + ", " + device->toString(), E_FILE_LINE);
- }
- return gatt->addCharacteristicListener(l);
+ return getDevice()->addCharacteristicListener(l);
}
-bool GATTCharacteristic::removeCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l) {
- std::shared_ptr<DBTDevice> device = getDevice();
- std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
- if( nullptr == gatt ) {
- // OK to have GATTHandler being shutdown @ disable
- DBG_PRINT("Characteristic's device GATTHandle not connected: %s, %s", toString().c_str(), device->toString().c_str());
+bool GATTCharacteristic::addCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l, bool enabledState[2]) {
+ if( !configNotificationIndication(true, true, enabledState) ) {
return false;
}
- return gatt->removeCharacteristicListener(l);
+ return addCharacteristicListener(l);
}
-bool GATTCharacteristic::removeCharacteristicListener(const GATTCharacteristicListener * l) {
- std::shared_ptr<DBTDevice> device = getDevice();
- std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
- if( nullptr == gatt ) {
- // OK to have GATTHandler being shutdown @ disable
- DBG_PRINT("Characteristic's device GATTHandle not connected: %s, %s", toString().c_str(), device->toString().c_str());
- return false;
+bool GATTCharacteristic::removeCharacteristicListener(std::shared_ptr<GATTCharacteristicListener> l, bool disableIndicationNotification) {
+ if( disableIndicationNotification ) {
+ bool enabledState[2];
+ configNotificationIndication(false, false, enabledState);
}
- return gatt->removeCharacteristicListener(l);
+ return getDevice()->removeCharacteristicListener(l);
}
-int GATTCharacteristic::removeAllCharacteristicListener() {
- std::shared_ptr<DBTDevice> device = getDevice();
- std::shared_ptr<GATTHandler> gatt = device->getGATTHandler();
- if( nullptr == gatt ) {
- // OK to have GATTHandler being shutdown @ disable
- DBG_PRINT("Characteristic's device GATTHandle not connected: %s, %s", toString().c_str(), device->toString().c_str());
- return 0;
+int GATTCharacteristic::removeAllCharacteristicListener(bool disableIndicationNotification) {
+ if( disableIndicationNotification ) {
+ bool enabledState[2];
+ configNotificationIndication(false, false, enabledState);
}
- return gatt->removeAllCharacteristicListener();
+ return getDevice()->removeAllCharacteristicListener();
}
bool GATTCharacteristic::readValue(POctets & res, int expectedLength) {
diff --git a/src/direct_bt/GATTHandler.cpp b/src/direct_bt/GATTHandler.cpp
index c75408df..5ba74d1e 100644
--- a/src/direct_bt/GATTHandler.cpp
+++ b/src/direct_bt/GATTHandler.cpp
@@ -91,14 +91,14 @@ bool GATTHandler::addCharacteristicListener(std::shared_ptr<GATTCharacteristicLi
throw IllegalArgumentException("GATTEventListener ref is null", E_FILE_LINE);
}
const std::lock_guard<std::recursive_mutex> lock(mtx_eventListenerList); // RAII-style acquire and relinquish via destructor
- for(auto it = eventListenerList.begin(); it != eventListenerList.end(); ) {
+ for(auto it = characteristicListenerList.begin(); it != characteristicListenerList.end(); ) {
if ( **it == *l ) {
return false; // already included
} else {
++it;
}
}
- eventListenerList.push_back(l);
+ characteristicListenerList.push_back(l);
return true;
}
@@ -106,10 +106,17 @@ bool GATTHandler::removeCharacteristicListener(std::shared_ptr<GATTCharacteristi
if( nullptr == l ) {
throw IllegalArgumentException("GATTEventListener ref is null", E_FILE_LINE);
}
+ return removeCharacteristicListener( l.get() );
+}
+
+bool GATTHandler::removeCharacteristicListener(const GATTCharacteristicListener * l) {
+ if( nullptr == l ) {
+ throw IllegalArgumentException("GATTEventListener ref is null", E_FILE_LINE);
+ }
const std::lock_guard<std::recursive_mutex> lock(mtx_eventListenerList); // RAII-style acquire and relinquish via destructor
- for(auto it = eventListenerList.begin(); it != eventListenerList.end(); ) {
+ for(auto it = characteristicListenerList.begin(); it != characteristicListenerList.end(); ) {
if ( **it == *l ) {
- it = eventListenerList.erase(it);
+ it = characteristicListenerList.erase(it);
return true;
} else {
++it;
@@ -118,14 +125,21 @@ bool GATTHandler::removeCharacteristicListener(std::shared_ptr<GATTCharacteristi
return false;
}
-bool GATTHandler::removeCharacteristicListener(const GATTCharacteristicListener * l) {
- if( nullptr == l ) {
- throw IllegalArgumentException("GATTEventListener ref is null", E_FILE_LINE);
+int GATTHandler::removeAllAssociatedCharacteristicListener(std::shared_ptr<GATTCharacteristic> associatedCharacteristic) {
+ if( nullptr == associatedCharacteristic ) {
+ throw IllegalArgumentException("GATTCharacteristic ref is null", E_FILE_LINE);
+ }
+ return removeAllAssociatedCharacteristicListener( associatedCharacteristic.get() );
+}
+
+int GATTHandler::removeAllAssociatedCharacteristicListener(const GATTCharacteristic * associatedCharacteristic) {
+ if( nullptr == associatedCharacteristic ) {
+ throw IllegalArgumentException("GATTCharacteristic ref is null", E_FILE_LINE);
}
const std::lock_guard<std::recursive_mutex> lock(mtx_eventListenerList); // RAII-style acquire and relinquish via destructor
- for(auto it = eventListenerList.begin(); it != eventListenerList.end(); ) {
- if ( **it == *l ) {
- it = eventListenerList.erase(it);
+ for(auto it = characteristicListenerList.begin(); it != characteristicListenerList.end(); ) {
+ if ( (*it)->match(*associatedCharacteristic) ) {
+ it = characteristicListenerList.erase(it);
return true;
} else {
++it;
@@ -136,8 +150,8 @@ bool GATTHandler::removeCharacteristicListener(const GATTCharacteristicListener
int GATTHandler::removeAllCharacteristicListener() {
const std::lock_guard<std::recursive_mutex> lock(mtx_eventListenerList); // RAII-style acquire and relinquish via destructor
- int count = eventListenerList.size();
- eventListenerList.clear();
+ int count = characteristicListenerList.size();
+ characteristicListenerList.clear();
return count;
}
@@ -176,19 +190,19 @@ void GATTHandler::l2capReaderThreadImpl() {
if( AttPDUMsg::Opcode::ATT_HANDLE_VALUE_NTF == opc ) {
const AttHandleValueRcv * a = static_cast<const AttHandleValueRcv*>(attPDU);
- DBG_PRINT("GATTHandler: NTF: %s", a->toString().c_str());
+ DBG_PRINT("GATTHandler: NTF: %s, listener %zd", a->toString().c_str(), characteristicListenerList.size());
GATTCharacteristicRef decl = findCharacterisicsByValueHandle(a->getHandle());
const std::shared_ptr<TROOctets> data(new POctets(a->getValue()));
const uint64_t timestamp = a->ts_creation;
int i=0;
- for_each_idx_mtx(mtx_eventListenerList, eventListenerList, [&](std::shared_ptr<GATTCharacteristicListener> &l) {
+ for_each_idx_mtx(mtx_eventListenerList, characteristicListenerList, [&](std::shared_ptr<GATTCharacteristicListener> &l) {
try {
if( l->match(*decl) ) {
l->notificationReceived(decl, data, timestamp);
}
} catch (std::exception &e) {
ERR_PRINT("GATTHandler::notificationReceived-CBs %d/%zd: GATTCharacteristicListener %s: Caught exception %s",
- i+1, eventListenerList.size(),
+ i+1, characteristicListenerList.size(),
aptrHexString((void*)l.get()).c_str(), e.what());
}
i++;
@@ -196,7 +210,8 @@ void GATTHandler::l2capReaderThreadImpl() {
attPDU = nullptr;
} else if( AttPDUMsg::Opcode::ATT_HANDLE_VALUE_IND == opc ) {
const AttHandleValueRcv * a = static_cast<const AttHandleValueRcv*>(attPDU);
- DBG_PRINT("GATTHandler: IND: %s, sendIndicationConfirmation %d", a->toString().c_str(), sendIndicationConfirmation);
+ DBG_PRINT("GATTHandler: IND: %s, sendIndicationConfirmation %d, listener %zd", a->toString().c_str(),
+ sendIndicationConfirmation, characteristicListenerList.size());
bool cfmSent = false;
if( sendIndicationConfirmation ) {
AttHandleValueCfm cfm;
@@ -208,14 +223,14 @@ void GATTHandler::l2capReaderThreadImpl() {
const std::shared_ptr<TROOctets> data(new POctets(a->getValue()));
const uint64_t timestamp = a->ts_creation;
int i=0;
- for_each_idx_mtx(mtx_eventListenerList, eventListenerList, [&](std::shared_ptr<GATTCharacteristicListener> &l) {
+ for_each_idx_mtx(mtx_eventListenerList, characteristicListenerList, [&](std::shared_ptr<GATTCharacteristicListener> &l) {
try {
if( l->match(*decl) ) {
l->indicationReceived(decl, data, timestamp, cfmSent);
}
} catch (std::exception &e) {
ERR_PRINT("GATTHandler::indicationReceived-CBs %d/%zd: GATTCharacteristicListener %s, cfmSent %d: Caught exception %s",
- i+1, eventListenerList.size(),
+ i+1, characteristicListenerList.size(),
aptrHexString((void*)l.get()).c_str(), cfmSent, e.what());
}
i++;
@@ -254,7 +269,6 @@ GATTHandler::GATTHandler(const std::shared_ptr<DBTDevice> &device, const int rep
{ }
GATTHandler::~GATTHandler() {
- eventListenerList.clear();
disconnect(false /* disconnectDevice */, false /* ioErrorCause */);
services.clear();
}
@@ -313,6 +327,7 @@ bool GATTHandler::disconnect(const bool disconnectDevice, const bool ioErrorCaus
DBG_PRINT("GATTHandler::disconnect: Not connected: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
l2cap.disconnect(); // interrupt GATT's L2CAP ::connect(..), avoiding prolonged hang
+ characteristicListenerList.clear();
return false;
}
hasIOError = false;
@@ -336,6 +351,8 @@ bool GATTHandler::disconnect(const bool disconnectDevice, const bool ioErrorCaus
}
}
}
+ removeAllCharacteristicListener();
+
std::shared_ptr<DBTDevice> device = getDevice();
if( disconnectDevice && nullptr != device ) {
@@ -836,13 +853,13 @@ bool GATTHandler::writeValue(const uint16_t handle, const TROOctets & value, con
return res;
}
-bool GATTHandler::configIndicationNotification(GATTDescriptor & cccd, const bool enableNotification, const bool enableIndication) {
+bool GATTHandler::configNotificationIndication(GATTDescriptor & cccd, const bool enableNotification, const bool enableIndication) {
if( !cccd.isClientCharacteristicConfiguration() ) {
throw IllegalArgumentException("Not a ClientCharacteristicConfiguration: "+cccd.toString(), E_FILE_LINE);
}
/* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration */
const uint16_t ccc_value = enableNotification | ( enableIndication << 1 );
- DBG_PRINT("GATTHandler::configIndicationNotification decl %s, enableNotification %d, enableIndication %d",
+ DBG_PRINT("GATTHandler::configNotificationIndication decl %s, enableNotification %d, enableIndication %d",
cccd.toString().c_str(), enableNotification, enableIndication);
cccd.value.resize(2, 2);
cccd.value.put_uint16(0, ccc_value);