/*
* 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
typedef std::shared_ptr BTGattHandlerRef;
class BTDevice; // forward
typedef std::shared_ptr BTDeviceRef;
class BTGattService; // forward
typedef std::shared_ptr BTGattServiceRef;
class BTGattCharListener; // forward
typedef std::shared_ptr BTGattCharListenerRef;
/**
* Representing a Gatt Characteristic object from the ::GATTRole::Client perspective.
*
* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3 Characteristic Definition
*
* handle -> CDAV value
*
* BT Core Spec v5.2: Vol 3, Part G GATT: 4.6.1 Discover All Characteristics of a Service
*
* The handle represents 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)
};
/**
* 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;
/* Optional Characteristic User Description index within descriptorList */
int userDescriptionIndex = -1;
BTGattChar(const BTGattServiceRef & service_, const uint16_t handle_,
const PropertyBitVal properties_, const uint16_t value_handle_, std::unique_ptr && value_type_) noexcept
: wbr_service(service_), 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");
}
BTGattServiceRef getServiceUnchecked() const noexcept { return wbr_service.lock(); }
BTGattHandlerRef getGattHandlerUnchecked() const noexcept;
BTDeviceRef getDeviceUnchecked() const noexcept;
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;
userDescriptionIndex = -1;
}
/**
* Return the Client Characteristic Configuration BTGattDescRef if available or nullptr.
*
* The BTGattDesc::Type::CLIENT_CHARACTERISTIC_CONFIGURATION has been indexed while
* retrieving the GATT database from the server.
*/
BTGattDescRef getClientCharConfig() const noexcept {
if( 0 > clientCharConfigIndex ) {
return nullptr;
}
return descriptorList.at(static_cast(clientCharConfigIndex)); // abort if out of bounds
}
/**
* Return the User Description BTGattDescRef if available or nullptr.
*
* The BTGattDesc::Type::CHARACTERISTIC_USER_DESCRIPTION has been indexed while
* retrieving the GATT database from the server.
*/
BTGattDescRef getUserDescription() const noexcept {
if( 0 > userDescriptionIndex ) {
return nullptr;
}
return descriptorList.at(static_cast(userDescriptionIndex)); // 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.
*
* @see enableNotificationOrIndication()
* @see disableIndicationNotification()
*/
bool configNotificationIndication(const bool enableNotification, const bool enableIndication, bool enabledState[2]) noexcept;
/**
* 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.
*
* @see configNotificationIndication()
* @see disableIndicationNotification()
*/
bool enableNotificationOrIndication(bool enabledState[2]) noexcept;
/**
* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
*
* Method will attempt to disable notification and indication on the BLE level.
*
* Notification and/or indication configuration is only performed per characteristic if changed.
*
* @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.
*
* @see configNotificationIndication()
* @see enableNotificationOrIndication()
* @since 2.4.0
*/
bool disableIndicationNotification() noexcept;
/**
* Add the given BTGattCharListener 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 BTGattCharListener callback method.
*
* Returns true if the given listener is not element of the list and has been newly added,
* otherwise false.
*
* Convenience delegation call to BTGattHandler via BTDevice
*
* @see BTGattChar::disableIndicationNotification()
* @see BTGattChar::enableNotificationOrIndication()
* @see BTGattChar::configNotificationIndication()
* @see BTGattChar::addCharListener()
* @see BTGattChar::removeCharListener()
* @see BTGattChar::removeAllAssociatedCharListener()
*/
bool addCharListener(const BTGattCharListenerRef& l) noexcept;
/**
* Add the given BTGattCharListener 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 BTGattCharListener 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.
*
* @param enabledState array of size 2, holding the resulting enabled state for notification and indication
* using enableNotificationOrIndication(bool[])
*
* @see BTGattChar::disableIndicationNotification()
* @see BTGattChar::enableNotificationOrIndication()
* @see BTGattChar::configNotificationIndication()
* @see BTGattChar::addCharListener()
* @see BTGattChar::removeCharListener()
* @see BTGattChar::removeAllAssociatedCharListener()
*/
bool addCharListener(const BTGattCharListenerRef& l, bool enabledState[2]) noexcept;
/**
* Remove the given associated BTGattCharListener from the listener list if present.
*
* To disables the notification and/or indication for this characteristic at BLE level
* use disableIndicationNotification() when desired.
*
* @param l
* @return
*
* @see BTGattChar::disableIndicationNotification()
* @see BTGattChar::enableNotificationOrIndication()
* @see BTGattChar::configNotificationIndication()
* @see BTGattChar::addCharListener()
* @see BTGattChar::removeCharListener()
* @see BTGattChar::removeAllAssociatedCharListener()
* @since 2.4.0
*/
bool removeCharListener(const BTGattCharListenerRef& l) noexcept;
/**
* Removes all associated BTGattCharListener and and {@link BTGattCharListener} from the listener list.
*
* Also disables the notification and/or indication for this characteristic at BLE level
* if `disableIndicationNotification == true`.
*
* Returns the number of removed event listener.
*
* If the BTDevice's BTGattHandler is null, i.e. not connected, `zero` is being returned.
*
* @param shallDisableIndicationNotification if true, disables the notification and/or indication for this characteristic
* using {@link #disableIndicationNotification()
* @return
* @see BTGattChar::disableIndicationNotification()
* @see BTGattChar::configNotificationIndication()
* @see BTGattChar::addCharListener()
* @see BTGattChar::removeCharListener()
* @see BTGattChar::removeAllAssociatedCharListener()
*/
int removeAllAssociatedCharListener(bool shallDisableIndicationNotification) noexcept;
/**
* 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, false is returned.
*
*/
bool readValue(jau::POctets & res, int expectedLength=-1) noexcept;
/**
* 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, false is returned.
*
*/
bool writeValue(const jau::TROOctets & value) noexcept;
/**
* 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, false is returned.
*
*/
bool writeValueNoResp(const jau::TROOctets & value) noexcept;
};
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); }
constexpr uint8_t number(const BTGattChar::PropertyBitVal rhs) noexcept {
return static_cast(rhs);
}
constexpr BTGattChar::PropertyBitVal operator |(const BTGattChar::PropertyBitVal lhs, const BTGattChar::PropertyBitVal rhs) noexcept {
return static_cast ( number(lhs) | number(rhs) );
}
constexpr BTGattChar::PropertyBitVal operator &(const BTGattChar::PropertyBitVal lhs, const BTGattChar::PropertyBitVal rhs) noexcept {
return static_cast ( number(lhs) & number(rhs) );
}
constexpr bool operator ==(const BTGattChar::PropertyBitVal lhs, const BTGattChar::PropertyBitVal rhs) noexcept {
return number(lhs) == number(rhs);
}
constexpr bool operator !=(const BTGattChar::PropertyBitVal lhs, const BTGattChar::PropertyBitVal rhs) noexcept {
return !( lhs == rhs );
}
std::string to_string(const BTGattChar::PropertyBitVal mask) noexcept;
/**
* {@link BTGattChar} event listener for notification and indication events.
*
* A listener instance may be attached to a BTGattChar instance via
* {@link BTGattChar::addCharListener(BTGattCharListenerRef)} to listen to its events.
*
*
* A listener instance may be attached to a BTGattHandler via
* {@link BTGattHandler::addCharListener(BTGattCharListenerRef)}
* to listen to all events of the device or the matching filtered events.
*
*
* The listener manager maintains a unique set of listener instances without duplicates.
*
*/
class BTGattCharListener : public jau::JavaUplink {
public:
/**
* Returns a unique string denominating the type of this instance.
*
* Simple access and provision of a typename string representation
* at compile time like RTTI via jau::type_name_cue.
*/
virtual const char * type_name() const noexcept;
/**
* 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 monotonic timestamp at reception, see jau::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 monotonic timestamp at reception, see jau::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;
~BTGattCharListener() noexcept override {}
/** Return a simple description about this instance. */
std::string toString() const noexcept override {
return std::string(type_name())+"["+jau::to_string(this)+"]";
}
std::string get_java_class() const noexcept override {
return java_class();
}
static std::string java_class() noexcept {
return std::string(JAVA_MAIN_PACKAGE "BTGattCharListener");
}
/**
* 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); }
};
typedef std::shared_ptr BTGattCharListenerRef;
} // namespace direct_bt
#endif /* BT_GATT_CHARACTERISTIC_HPP_ */