/*
* Author: Sven Gothel
* Copyright (c) 2020 Gothel Software e.K.
* Copyright (c) 2020 ZAFENA AB
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef BT_GATT_CHARACTERISTIC_HPP_
#define BT_GATT_CHARACTERISTIC_HPP_
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "BTTypes0.hpp"
#include "ATTPDUTypes.hpp"
#include "BTTypes1.hpp"
#include "BTGattDesc.hpp"
/**
* - - - - - - - - - - - - - - -
*
* Module BTGattChar:
*
* - BT Core Spec v5.2: Vol 3, Part G Generic Attribute Protocol (GATT)
* - BT Core Spec v5.2: Vol 3, Part G GATT: 2.6 GATT Profile Hierarchy
*/
namespace direct_bt {
class BTGattHandler; // forward
class BTGattService; // forward
typedef std::shared_ptr BTGattServiceRef;
/**
*
* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1 Characteristic Declaration Attribute Value
*
* handle -> CDAV value
*
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.6.1 Discover All Characteristics of a Service
*
* Here the handle is a service's characteristics-declaration
* and the value the Characteristics Property, Characteristics Value Handle _and_ Characteristics UUID.
*
*/
class BTGattChar : public BTObject {
private:
/** Characteristics's service weak back-reference */
std::weak_ptr wbr_service;
bool enabledNotifyState = false;
bool enabledIndicateState = false;
std::string toShortString() const noexcept;
public:
/** BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties */
enum PropertyBitVal : uint8_t {
NONE = 0,
Broadcast = (1 << 0),
Read = (1 << 1),
WriteNoAck = (1 << 2),
WriteWithAck = (1 << 3),
Notify = (1 << 4),
Indicate = (1 << 5),
AuthSignedWrite = (1 << 6),
ExtProps = (1 << 7)
};
/**
* Returns string values as defined in
*
* org.bluez.GattCharacteristic1 :: array{string} Flags [read-only]
*
*/
static std::string getPropertiesString(const PropertyBitVal properties) noexcept;
static jau::darray> getPropertiesStringList(const PropertyBitVal properties) noexcept;
/**
* {@link BTGattChar} event listener for notification and indication events.
*
* This listener instance is attached to a BTGattChar via
* {@link BTGattChar::addCharListener(std::shared_ptr)} or
* {@link BTGattChar::addCharListener(std::shared_ptr, bool[])}
* to listen to events associated with the BTGattChar instance.
*
*
* The listener manager maintains a unique set of listener instances without duplicates.
*
*
* Implementation will utilize a BTGattCharListener instance for the listener manager,
* delegating matching BTGattChar events to this instance.
*
*/
class Listener {
public:
/**
* Called from native BLE stack, initiated by a received notification associated
* with the given {@link BTGattChar}.
* @param charDecl {@link BTGattChar} related to this notification
* @param charValue the notification value
* @param timestamp the indication monotonic timestamp, see getCurrentMilliseconds()
*/
virtual void notificationReceived(BTGattCharRef charDecl,
const jau::TROOctets& charValue, const uint64_t timestamp) = 0;
/**
* Called from native BLE stack, initiated by a received indication associated
* with the given {@link BTGattChar}.
* @param charDecl {@link BTGattChar} related to this indication
* @param charValue the indication value
* @param timestamp 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(BTGattCharRef charDecl,
const jau::TROOctets& charValue, const uint64_t timestamp,
const bool confirmationSent) = 0;
virtual ~Listener() noexcept {}
/**
* Default comparison operator, merely testing for same memory reference.
*
* Specializations may override.
*
*/
virtual bool operator==(const Listener& rhs) const noexcept
{ return this == &rhs; }
bool operator!=(const Listener& rhs) const noexcept
{ return !(*this == rhs); }
};
/**
* Characteristics's Service Handle - key to service's handle range, retrieved from Characteristics data.
*
* Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle).
*
*/
const uint16_t service_handle;
/**
* Characteristic Handle of this instance.
*
* Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle).
*
*/
const uint16_t handle;
/* Characteristics Property */
const PropertyBitVal properties;
/**
* Characteristics Value Handle.
*
* Attribute handles are unique for each device (server) (BT Core Spec v5.2: Vol 3, Part F Protocol..: 3.2.2 Attribute Handle).
*
*/
const uint16_t value_handle;
/* Characteristics Value Type UUID */
std::unique_ptr value_type;
/** List of Characteristic Descriptions as shared reference */
jau::darray descriptorList;
/* Optional Client Characteristic Configuration index within descriptorList */
int clientCharConfigIndex = -1;
BTGattChar(const BTGattServiceRef & service_, const uint16_t service_handle_, const uint16_t handle_,
const PropertyBitVal properties_, const uint16_t value_handle_, std::unique_ptr && value_type_) noexcept
: wbr_service(service_), service_handle(service_handle_), handle(handle_),
properties(properties_), value_handle(value_handle_), value_type(std::move(value_type_)) {}
std::string get_java_class() const noexcept override {
return java_class();
}
static std::string java_class() noexcept {
return std::string(JAVA_DBT_PACKAGE "DBTGattChar");
}
std::shared_ptr getServiceUnchecked() const noexcept { return wbr_service.lock(); }
std::shared_ptr getServiceChecked() const;
std::shared_ptr getGattHandlerUnchecked() const noexcept;
std::shared_ptr getGattHandlerChecked() const;
std::shared_ptr getDeviceUnchecked() const noexcept;
std::shared_ptr getDeviceChecked() const;
bool hasProperties(const PropertyBitVal v) const noexcept { return v == ( properties & v ); }
/**
* Find a BTGattDesc by its desc_uuid.
*
* @parameter desc_uuid the UUID of the desired BTGattDesc
* @return The matching descriptor or null if not found
*/
std::shared_ptr findGattDesc(const jau::uuid_t& desc_uuid) noexcept;
std::string toString() const noexcept override;
void clearDescriptors() noexcept {
descriptorList.clear();
clientCharConfigIndex = -1;
}
BTGattDescRef getClientCharConfig() noexcept {
if( 0 > clientCharConfigIndex ) {
return nullptr;
}
return descriptorList.at(static_cast(clientCharConfigIndex)); // abort if out of bounds
}
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
*
* Method enables notification and/or indication for this characteristic at BLE level.
*
*
* Implementation masks this Characteristic properties PropertyBitVal::Notify and PropertyBitVal::Indicate
* with the respective user request parameters, hence removes unsupported requests.
*
*
* Notification and/or indication configuration is only performed per characteristic if changed.
*
*
* It is recommended to utilize notification over indication, as its link-layer handshake
* and higher potential bandwidth may deliver material higher performance.
*
* @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 BTGattDesc 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 BTDevice's}'s {@link BTGattHandler} is null, i.e. not connected
*/
bool configNotificationIndication(const bool enableNotification, const bool enableIndication, bool enabledState[2]);
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
*
* Method will attempt to enable notification on the BLE level, if available,
* otherwise indication if available.
*
*
* Notification and/or indication configuration is only performed per characteristic if changed.
*
*
* It is recommended to utilize notification over indication, as its link-layer handshake
* and higher potential bandwidth may deliver material higher performance.
*
* @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 BTGattDesc 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 BTDevice's}'s {@link BTGattHandler} is null, i.e. not connected
*/
bool enableNotificationOrIndication(bool enabledState[2]);
/**
* Add the given BTGattChar::Listener to the listener list if not already present.
*
* Occurring notifications and indications for this characteristic,
* if enabled via configNotificationIndication(bool, bool, bool[]) or enableNotificationOrIndication(bool[]),
* will call the respective BTGattChar::Listener callback method.
*
*
* Returns true if the given listener is not element of the list and has been newly added,
* otherwise false.
*
*
* Implementation wraps given BTGattChar::Listener into an AssociatedBTGattCharListener
* to restrict the listener to listen only to this BTGattChar instance.
*
*
* Convenience delegation call to BTGattHandler via BTDevice
*
* @throws IllegalStateException if the {@link BTDevice's}'s {@link BTGattHandler} is null, i.e. not connected
* @see BTGattChar::enableNotificationOrIndication()
* @see BTGattChar::configNotificationIndication()
* @see BTGattChar::addCharListener()
* @see BTGattChar::removeCharListener()
* @see BTGattChar::removeAllAssociatedCharListener()
*/
bool addCharListener(std::shared_ptr l);
/**
* Add the given BTGattChar::Listener to the listener list if not already present
* and if enabling the notification or indication for this characteristic at BLE level was successful.
* Notification and/or indication configuration is only performed per characteristic if changed.
*
* Implementation will enable notification if available,
* otherwise indication will be enabled if available.
* Implementation uses enableNotificationOrIndication(bool[]) to enable either.
*
*
* Occurring notifications and indications for this characteristic
* will call the respective BTGattChar::Listener callback method.
*
*
* Returns true if enabling the notification and/or indication was successful
* and if the given listener is not element of the list and has been newly added,
* otherwise false.
*
*
* Implementation wraps given BTGattChar::Listener into an AssociatedBTGattCharListener
* to restrict the listener to listen only to this BTGattChar instance.
*
* @param enabledState array of size 2, holding the resulting enabled state for notification and indication
* using enableNotificationOrIndication(bool[])
* @throws IllegalStateException if the {@link BTDevice's}'s {@link BTGattHandler} is null, i.e. not connected
* @see BTGattChar::enableNotificationOrIndication()
* @see BTGattChar::configNotificationIndication()
* @see BTGattChar::addCharListener()
* @see BTGattChar::removeCharListener()
* @see BTGattChar::removeAllAssociatedCharListener()
*/
bool addCharListener(std::shared_ptr l, bool enabledState[2]);
/**
* Disables the notification and/or indication for this characteristic at BLE level
* if `disableIndicationNotification == true`
* and removes all associated BTGattChar::Listener and {@link BTGattCharListener} from the listener list.
*
* Returns the number of removed event listener.
*
*
* If the BTDevice's BTGattHandler is null, i.e. not connected, `zero` is being returned.
*
* @param disableIndicationNotification if true, disables the notification and/or indication for this characteristic
* using {@link #configNotificationIndication(bool, bool, bool[])
* @return
* @see BTGattChar::enableNotificationOrIndication()
* @see BTGattChar::configNotificationIndication()
* @see BTGattChar::addCharListener()
* @see BTGattChar::removeCharListener()
* @see BTGattChar::removeAllAssociatedCharListener()
*/
int removeAllAssociatedCharListener(bool disableIndicationNotification);
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value
*
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value
*
*
* If expectedLength = 0, then only one ATT_READ_REQ/RSP will be used.
*
*
* If expectedLength < 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used until
* the response returns zero. This is the default parameter.
*
*
* If expectedLength > 0, then long values using multiple ATT_READ_BLOB_REQ/RSP will be used
* if required until the response returns zero.
*
*
* Convenience delegation call to BTGattHandler via BTDevice
*
*
* If the BTDevice's BTGattHandler is null, i.e. not connected, an IllegalStateException is thrown.
*
*/
bool readValue(jau::POctets & res, int expectedLength=-1);
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value
*
* Convenience delegation call to BTGattHandler via BTDevice
*
*
* If the BTDevice's BTGattHandler is null, i.e. not connected, an IllegalStateException is thrown.
*
*/
bool writeValue(const jau::TROOctets & value);
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response
*
* Convenience delegation call to BTGattHandler via BTDevice
*
*
* If the BTDevice's BTGattHandler is null, i.e. not connected, an IllegalStateException is thrown.
*
*/
bool writeValueNoResp(const jau::TROOctets & value);
};
typedef std::shared_ptr BTGattCharRef;
inline bool operator==(const BTGattChar& lhs, const BTGattChar& rhs) noexcept
{ return lhs.handle == rhs.handle; /** unique attribute handles */ }
inline bool operator!=(const BTGattChar& lhs, const BTGattChar& rhs) noexcept
{ return !(lhs == rhs); }
/**
* {@link BTGattChar} event listener for notification and indication events.
*
* A listener instance may be attached to a BTGattChar instance via
* {@link BTGattChar::addCharListener(std::shared_ptr)} to listen to its events.
*
*
* A listener instance may be attached to a BTGattHandler via
* {@link BTGattHandler::addCharListener(std::shared_ptr)}
* to listen to all events of the device or the matching filtered events.
*
*
* User may utilize {@link AssociatedBTGattCharListener} to listen to only one {@link BTGattChar}.
*
*
* The listener manager maintains a unique set of listener instances without duplicates.
*
*/
class BTGattCharListener {
public:
/**
* Custom filter for all event methods,
* which will not be called if this method returns false.
*
* User may override this method to test whether the methods shall be called
* for the given BTGattChar.
*
*
* Defaults to true;
*
*/
virtual bool match(const BTGattChar & characteristic) noexcept {
(void)characteristic;
return true;
}
/**
* Called from native BLE stack, initiated by a received notification associated
* with the given {@link BTGattChar}.
* @param charDecl {@link BTGattChar} related to this notification
* @param charValue the notification value
* @param timestamp the indication monotonic timestamp, see getCurrentMilliseconds()
*/
virtual void notificationReceived(BTGattCharRef charDecl,
const jau::TROOctets& charValue, const uint64_t timestamp) = 0;
/**
* Called from native BLE stack, initiated by a received indication associated
* with the given {@link BTGattChar}.
* @param charDecl {@link BTGattChar} related to this indication
* @param charValue the indication value
* @param timestamp 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(BTGattCharRef charDecl,
const jau::TROOctets& charValue, const uint64_t timestamp,
const bool confirmationSent) = 0;
virtual ~BTGattCharListener() noexcept {}
/**
* Default comparison operator, merely testing for same memory reference.
*
* Specializations may override.
*
*/
virtual bool operator==(const BTGattCharListener& rhs) const noexcept
{ return this == &rhs; }
bool operator!=(const BTGattCharListener& rhs) const noexcept
{ return !(*this == rhs); }
};
class AssociatedBTGattCharListener : public BTGattCharListener {
private:
const BTGattChar * associatedChar;
public:
/**
* Passing the associated BTGattChar to filter out non matching events.
*/
AssociatedBTGattCharListener(const BTGattChar * characteristicMatch) noexcept
: associatedChar(characteristicMatch) { }
bool match(const BTGattChar & characteristic) noexcept override {
if( nullptr == associatedChar ) {
return true;
}
return *associatedChar == characteristic;
}
};
} // namespace direct_bt
#endif /* BT_GATT_CHARACTERISTIC_HPP_ */