/* * 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 GATT_CHARACTERISTIC_HPP_ #define GATT_CHARACTERISTIC_HPP_ #include #include #include #include #include #include #include #include "UUID.hpp" #include "BTTypes.hpp" #include "OctetTypes.hpp" #include "ATTPDUTypes.hpp" #include "GATTDescriptor.hpp" #include "JavaUplink.hpp" /** * - - - - - - - - - - - - - - - * * Module GATTCharacteristic: * * - 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 GATTCharacteristicListener; // forward class GATTService; // forward typedef std::shared_ptr GATTServiceRef; /** *

* 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 GATTCharacteristic : public JavaUplink { public: /** BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties */ enum PropertyBitVal : uint8_t { Broadcast = 0x01, Read = 0x02, WriteNoAck = 0x04, WriteWithAck = 0x08, Notify = 0x10, Indicate = 0x20, AuthSignedWrite = 0x40, ExtProps = 0x80 }; /** * Returns string values as defined in *
             * org.bluez.GattCharacteristic1 :: array{string} Flags [read-only]
             * 
*/ static std::string getPropertyString(const PropertyBitVal prop); static std::string getPropertiesString(const PropertyBitVal properties); static std::vector> getPropertiesStringList(const PropertyBitVal properties); /* Characteristics's Service back-reference */ GATTServiceRef service; /** * 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::shared_ptr value_type; /** List of Characteristic Descriptions as shared reference */ std::vector descriptorList; /* Optional Client Characteristic Configuration index within descriptorList */ int clientCharacteristicsConfigIndex = -1; GATTCharacteristic(const GATTServiceRef & service, const uint16_t service_handle, const uint16_t handle, const PropertyBitVal properties, const uint16_t value_handle, std::shared_ptr value_type) : service(service), service_handle(service_handle), handle(handle), properties(properties), value_handle(value_handle), value_type(value_type) {} std::string get_java_class() const override { return java_class(); } static std::string java_class() { return std::string(JAVA_DBT_PACKAGE "DBTGattCharacteristic"); } std::shared_ptr getDevice(); bool hasProperties(const PropertyBitVal v) const { return v == ( properties & v ); } std::string getPropertiesString() const { return getPropertiesString(properties); } std::string toString() const; void clearDescriptors() { descriptorList.clear(); clientCharacteristicsConfigIndex = -1; } GATTDescriptorRef getClientCharacteristicConfig() { if( 0 > clientCharacteristicsConfigIndex ) { return nullptr; } return descriptorList.at(clientCharacteristicsConfigIndex); } /** * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration *

* Convenience delegation call to GATTHandler via DBTDevice *

*

* If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown. *

*

* Implementation masks this Characteristic properties PropertyBitVal::Notify and PropertyBitVal::Indicate * with the respective user request parameters, hence removes unsupported requests.
* If the resulting combination for both requests is false, method returns false. *

*

* Returns false if there is no GATTDescriptor of type ClientCharacteristicConfiguration, * or if the operation has failed. *

*/ bool configIndicationNotification(const bool enableNotification, const bool enableIndication, bool enableResult[2]); /** * Add the given listener to the list if not already present. *

* Returns true if the given listener is not element of the list and has been newly added, * otherwise false. *

*

* Convenience delegation call to GATTHandler via DBTDevice *

*

* 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. *
* For this purpose, use may derive from SpecificGATTCharacteristicListener, * which provides these simple matching filter facilities. *

*/ bool addCharacteristicListener(std::shared_ptr l); /** * Remove the given listener from the list. *

* Returns true if the given listener is an element of the list and has been removed, * otherwise false. *

*

* Convenience delegation call to GATTHandler via DBTDevice *

*

* If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown. *

*/ bool removeCharacteristicListener(std::shared_ptr l); /** * Remove the given listener from the list. *

* Returns true if the given listener is an element of the list and has been removed, * otherwise false. *

*

* Convenience delegation call to GATTHandler via DBTDevice *

*

* If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown. *

*/ bool removeCharacteristicListener(const GATTCharacteristicListener * l); /** * Remove all event listener from the list. *

* Returns the number of removed event listener. *

*

* Convenience delegation call to GATTHandler via DBTDevice *

*

* If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown. *

*/ int removeAllCharacteristicListener(); /** * 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 GATTHandler via DBTDevice *

*

* If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown. *

*/ bool readValue(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 GATTHandler via DBTDevice *

*

* If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown. *

*/ bool writeValue(const TROOctets & value); /** * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response *

* Convenience delegation call to GATTHandler via DBTDevice *

*

* If the DBTDevice's GATTHandler is null, i.e. not connected, an IllegalStateException is thrown. *

*/ bool writeValueNoResp(const TROOctets & value); }; typedef std::shared_ptr GATTCharacteristicRef; inline bool operator==(const GATTCharacteristic& lhs, const GATTCharacteristic& rhs) { return lhs.handle == rhs.handle; /** unique attribute handles */ } inline bool operator!=(const GATTCharacteristic& lhs, const GATTCharacteristic& rhs) { return !(lhs == rhs); } /** * {@link GATTCharacteristic} event listener for notification and indication events. *

* A listener instance may be attached to a {@link BluetoothGattCharacteristic} via * {@link GATTCharacteristic::addCharacteristicListener(std::shared_ptr)} to listen to events, * see method's API doc for {@link GATTCharacteristic} filtering. *

*

* A listener instance may be attached to a {@link GATTHandler} via * {@link GATTHandler::addCharacteristicListener(std::shared_ptr)} * to listen to all events of the device or the matching filtered events. *

*

* The listener receiver maintains a unique set of listener instances without duplicates. *

*/ class GATTCharacteristicListener { 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 GATTCharacteristic. *

*

* Defaults to true; *

*/ virtual bool match(const GATTCharacteristic & characteristic) { (void)characteristic; return true; } virtual void notificationReceived(GATTCharacteristicRef charDecl, std::shared_ptr charValue, const uint64_t timestamp) = 0; virtual void indicationReceived(GATTCharacteristicRef charDecl, std::shared_ptr charValue, const uint64_t timestamp, const bool confirmationSent) = 0; virtual ~GATTCharacteristicListener() {} /** * Default comparison operator, merely testing for same memory reference. *

* Specializations may override. *

*/ virtual bool operator==(const GATTCharacteristicListener& rhs) const { return this == &rhs; } bool operator!=(const GATTCharacteristicListener& rhs) const { return !(*this == rhs); } }; class SpecificGATTCharacteristicListener : public GATTCharacteristicListener{ private: const GATTCharacteristic * characteristicMatch; public: /** * Passing the specific GATTCharacteristic to filter out non matching events. */ SpecificGATTCharacteristicListener(const GATTCharacteristic * characteristicMatch) : characteristicMatch(characteristicMatch) { } bool match(const GATTCharacteristic & characteristic) override { if( nullptr == characteristicMatch ) { return true; } return *characteristicMatch == characteristic; } }; } // namespace direct_bt #endif /* GATT_CHARACTERISTIC_HPP_ */