Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
BTGattChar.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2020 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include <cstring>
27#include <string>
28#include <memory>
29#include <cstdint>
30#include <vector>
31#include <cstdio>
32
33#include <algorithm>
34
36#include <jau/debug.hpp>
37
38#include "BTDevice.hpp"
39#include "BTGattService.hpp"
40#include "BTGattChar.hpp"
41#include "BTGattHandler.hpp"
42
43using namespace direct_bt;
44using namespace jau;
45
46/**
47 * Simple access and provision of a typename string representation
48 * at compile time like RTTI via jau::type_name_cue.
49 */
51
53
54#define CHAR_DECL_PROPS_ENUM(X) \
55 X(BTGattChar,Broadcast,broadcast) \
56 X(BTGattChar,Read,read) \
57 X(BTGattChar,WriteNoAck,write-noack) \
58 X(BTGattChar,WriteWithAck,write-ack) \
59 X(BTGattChar,Notify,notify) \
60 X(BTGattChar,Indicate,indicate) \
61 X(BTGattChar,AuthSignedWrite,authenticated-signed-writes) \
62 X(BTGattChar,ExtProps,extended-properties)
63
64/**
65 "reliable-write"
66 "writable-auxiliaries"
67 "encrypt-read"
68 "encrypt-write"
69 "encrypt-authenticated-read"
70 "encrypt-authenticated-write"
71 "secure-read" (Server only)
72 "secure-write" (Server only)
73 "authorize"
74 */
75
76#define CASE2_TO_STRING2(U,V,W) case U::V: return #W;
77
78static std::string _getPropertyBitValStr(const BTGattChar::PropertyBitVal prop) noexcept {
79 switch(prop) {
81 default: ; // fall through intended
82 }
83 return "Unknown property";
84}
85
86std::string direct_bt::to_string(const BTGattChar::PropertyBitVal mask) noexcept {
87 const BTGattChar::PropertyBitVal none = static_cast<BTGattChar::PropertyBitVal>(0);
88 const uint8_t one = 1;
89 bool has_pre = false;
90 std::string out("[");
91 for(int i=0; i<8; i++) {
92 const BTGattChar::PropertyBitVal propertyBit = static_cast<BTGattChar::PropertyBitVal>( one << i );
93 if( none != ( mask & propertyBit ) ) {
94 if( has_pre ) { out.append(", "); }
95 out.append(_getPropertyBitValStr(propertyBit));
96 has_pre = true;
97 }
98 }
99 out.append("]");
100 return out;
101}
102
103std::shared_ptr<BTGattDesc> BTGattChar::findGattDesc(const jau::uuid_t& desc_uuid) noexcept {
104 const size_t descriptors_size = descriptorList.size();
105 for(size_t j = 0; j < descriptors_size; j++) {
106 direct_bt::BTGattDescRef descriptor = descriptorList[j];
107 if( nullptr != descriptor && desc_uuid == *(descriptor->type) ) {
108 return descriptor;
109 }
110 }
111 return nullptr;
112}
113
114std::string BTGattChar::toString() const noexcept {
115 std::string char_name = "";
116 std::string desc_str;
117
118 if( uuid_t::TypeSize::UUID16_SZ == value_type->getTypeSize() ) {
119 const uint16_t uuid16 = (static_cast<const uuid16_t*>(value_type.get()))->value;
120 char_name = ", "+GattCharacteristicTypeToString(static_cast<GattCharacteristicType>(uuid16));
121 }
122 {
124 if( nullptr != ud ) {
125 char_name.append( ", '" + dfa_utf8_decode( ud->value.get_ptr(), ud->value.size() ) + "'");
126 }
127 }
128 if( 0 < descriptorList.size() ) {
129 bool comma = false;
130 desc_str = ", descr[";
131 for(const auto& cd : descriptorList) {
132 if( comma ) {
133 desc_str += ", ";
134 }
135 desc_str += "handle "+to_hexstring(cd->handle);
136 comma = true;
137 }
138 desc_str += "]";
139 }
140
141 std::string notify_str;
143 notify_str = ", enabled[notify "+std::to_string(enabledNotifyState)+", indicate "+std::to_string(enabledIndicateState)+"]";
144 }
145 return "Char[handle "+to_hexstring(handle)+", props "+to_hexstring(properties)+" "+to_string(properties)+
146 char_name+desc_str+", ccd-idx "+std::to_string(clientCharConfigIndex)+notify_str+
147 ", value[type 0x"+value_type->toString()+", handle "+to_hexstring(value_handle)+
148 "]]";
149}
150
151std::string BTGattChar::toShortString() const noexcept {
152 std::string char_name;
153
154 if( uuid_t::TypeSize::UUID16_SZ == value_type->getTypeSize() ) {
155 const uint16_t uuid16 = (static_cast<const uuid16_t*>(value_type.get()))->value;
156 char_name = ", "+GattCharacteristicTypeToString(static_cast<GattCharacteristicType>(uuid16));
157 }
158 {
160 if( nullptr != ud ) {
161 char_name.append( ", '" + dfa_utf8_decode( ud->value.get_ptr(), ud->value.size() ) + "'");
162 }
163 }
164 std::string notify_str;
166 notify_str = ", enabled[notify "+std::to_string(enabledNotifyState)+", indicate "+std::to_string(enabledIndicateState)+"]";
167 }
168 return "Char[handle "+to_hexstring(handle)+", props "+to_hexstring(properties)+" "+to_string(properties)+
169 char_name+", value[handle "+to_hexstring(value_handle)+
170 "], ccd-idx "+std::to_string(clientCharConfigIndex)+notify_str+"]";
171}
172
174 std::shared_ptr<BTGattService> s = getServiceUnchecked();
175 if( nullptr != s ) {
176 return s->getGattHandlerUnchecked();
177 }
178 return nullptr;
179}
180
182 std::shared_ptr<BTGattService> s = getServiceUnchecked();
183 if( nullptr != s ) {
184 return s->getDeviceUnchecked();
185 }
186 return nullptr;
187}
188
189bool BTGattChar::configNotificationIndication(const bool enableNotification, const bool enableIndication, bool enabledState[2]) noexcept {
190 enabledState[0] = false;
191 enabledState[1] = false;
192
193 const bool hasEnableNotification = hasProperties(BTGattChar::PropertyBitVal::Notify);
194 const bool hasEnableIndication = hasProperties(BTGattChar::PropertyBitVal::Indicate);
195 if( !hasEnableNotification && !hasEnableIndication ) {
196 DBG_PRINT("Characteristic has neither Notify nor Indicate property present: %s", toString().c_str());
197 return false;
198 }
199
200 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
201 std::shared_ptr<BTGattHandler> gatt = nullptr != device ? device->getGattHandler() : nullptr;
202 if( nullptr == gatt ) {
203 if( !enableNotification && !enableIndication ) {
204 // OK to have GATTHandler being shutdown @ disable
205 DBG_PRINT("Characteristic's device GATTHandle not connected: %s", toShortString().c_str());
206 } else {
207 ERR_PRINT("Characteristic's device GATTHandle not connected: %s", toShortString().c_str());
208 }
209 return false;
210 }
211 const bool resEnableNotification = hasEnableNotification && enableNotification;
212 const bool resEnableIndication = hasEnableIndication && enableIndication;
213
214 if( resEnableNotification == enabledNotifyState &&
215 resEnableIndication == enabledIndicateState )
216 {
217 enabledState[0] = resEnableNotification;
218 enabledState[1] = resEnableIndication;
219 DBG_PRINT("GATTCharacteristic::configNotificationIndication: Unchanged: notification[shall %d, has %d: %d == %d], indication[shall %d, has %d: %d == %d]",
220 enableNotification, hasEnableNotification, enabledNotifyState, resEnableNotification,
221 enableIndication, hasEnableIndication, enabledIndicateState, resEnableIndication);
222 return true;
223 }
224
225 BTGattDescRef cccd = this->getClientCharConfig();
226 if( nullptr == cccd ) {
227 DBG_PRINT("Characteristic has no ClientCharacteristicConfig descriptor: %s", toString().c_str());
228 return false;
229 }
230 bool res = gatt->configNotificationIndication(*cccd, resEnableNotification, resEnableIndication);
231 DBG_PRINT("GATTCharacteristic::configNotificationIndication: res %d, notification[shall %d, has %d: %d -> %d], indication[shall %d, has %d: %d -> %d]",
232 res,
233 enableNotification, hasEnableNotification, enabledNotifyState, resEnableNotification,
234 enableIndication, hasEnableIndication, enabledIndicateState, resEnableIndication);
235 if( res ) {
236 enabledNotifyState = resEnableNotification;
237 enabledIndicateState = resEnableIndication;
238 enabledState[0] = resEnableNotification;
239 enabledState[1] = resEnableIndication;
240 }
241 return res;
242}
243
244bool BTGattChar::enableNotificationOrIndication(bool enabledState[2]) noexcept {
245 const bool hasEnableNotification = hasProperties(BTGattChar::PropertyBitVal::Notify);
246 const bool hasEnableIndication = hasProperties(BTGattChar::PropertyBitVal::Indicate);
247
248 const bool enableNotification = hasEnableNotification;
249 const bool enableIndication = !enableNotification && hasEnableIndication;
250
251 return configNotificationIndication(enableNotification, enableIndication, enabledState);
252}
253
255 bool enabledState[2];
256 return configNotificationIndication(false, false, enabledState);
257}
258
260 BTDeviceRef device = getDeviceUnchecked();
261 if( nullptr == device ) {
262 ERR_PRINT("Characteristic's device null: %s", toShortString().c_str());
263 return false;
264 }
265 BTGattServiceRef service = getServiceUnchecked();
266 if( nullptr == service ) {
267 ERR_PRINT("Characteristic's service null: %s", toShortString().c_str());
268 return false;
269 }
270 BTGattCharRef characteristic = service->findGattChar(*this);
271 if( nullptr == service ) {
272 ERR_PRINT("Characteristic not in service: %s", toShortString().c_str());
273 return false;
274 }
275 return device->addCharListener(l, characteristic);
276}
277
278bool BTGattChar::addCharListener(const BTGattCharListenerRef& l, bool enabledState[2]) noexcept {
279 if( !enableNotificationOrIndication(enabledState) ) {
280 return false;
281 }
282 return addCharListener(l);
283}
284
286 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
287 if( nullptr == device ) {
288 ERR_PRINT("Characteristic's device null: %s", toShortString().c_str());
289 return false;
290 }
291 return device->removeCharListener(l);
292}
293
294BTGattChar::size_type BTGattChar::removeAllAssociatedCharListener(bool shallDisableIndicationNotification) noexcept {
295 if( shallDisableIndicationNotification ) {
296 disableIndicationNotification();
297 }
298 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
299 if( nullptr == device ) {
300 ERR_PRINT("Characteristic's device null: %s", toShortString().c_str());
301 return 0;
302 }
303 return device->removeAllAssociatedCharListener(this);
304}
305
306bool BTGattChar::readValue(POctets & res, int expectedLength) noexcept {
307 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
308 if( nullptr == device ) {
309 ERR_PRINT("Characteristic's device null: %s", toShortString().c_str());
310 return false;
311 }
312 std::shared_ptr<BTGattHandler> gatt = device->getGattHandler();
313 if( nullptr == gatt ) {
314 ERR_PRINT("Characteristic's device GATTHandle not connected: %s", toShortString().c_str());
315 return false;
316 }
317 return gatt->readCharacteristicValue(*this, res, expectedLength);
318}
319/**
320 * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value
321 */
322bool BTGattChar::writeValue(const TROOctets & value) noexcept {
323 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
324 if( nullptr == device ) {
325 ERR_PRINT("Characteristic's device null: %s", toShortString().c_str());
326 return false;
327 }
328 std::shared_ptr<BTGattHandler> gatt = device->getGattHandler();
329 if( nullptr == gatt ) {
330 ERR_PRINT("Characteristic's device GATTHandle not connected: %s", toShortString().c_str());
331 return false;
332 }
333 return gatt->writeCharacteristicValue(*this, value);
334}
335
336/**
337 * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response
338 */
339bool BTGattChar::writeValueNoResp(const TROOctets & value) noexcept {
340 std::shared_ptr<BTDevice> device = getDeviceUnchecked();
341 if( nullptr == device ) {
342 ERR_PRINT("Characteristic's device null: %s", toShortString().c_str());
343 return false;
344 }
345 std::shared_ptr<BTGattHandler> gatt = device->getGattHandler();
346 if( nullptr == gatt ) {
347 ERR_PRINT("Characteristic's device GATTHandle not connected: %s", toShortString().c_str());
348 return false;
349 }
350 return gatt->writeCharacteristicValueNoResp(*this, value);
351}
static std::string _getPropertyBitValStr(const BTGattChar::PropertyBitVal prop) noexcept
Definition: BTGattChar.cpp:78
#define CASE2_TO_STRING2(U, V, W)
"reliable-write" "writable-auxiliaries" "encrypt-read" "encrypt-write" "encrypt-authenticated-read" "...
Definition: BTGattChar.cpp:76
#define CHAR_DECL_PROPS_ENUM(X)
Definition: BTGattChar.cpp:54
BTGattChar event listener for notification and indication events.
Definition: BTGattChar.hpp:450
BTGattServiceRef getServiceUnchecked() const noexcept
Definition: BTGattChar.hpp:163
ssize_type clientCharConfigIndex
Definition: BTGattChar.hpp:146
std::unique_ptr< const jau::uuid_t > value_type
Definition: BTGattChar.hpp:140
bool disableIndicationNotification() noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
Definition: BTGattChar.cpp:254
BTGattHandlerRef getGattHandlerUnchecked() const noexcept
Definition: BTGattChar.cpp:173
bool addCharListener(const BTGattCharListenerRef &l) noexcept
Add the given BTGattCharListener to the listener list if not already present.
Definition: BTGattChar.cpp:259
const uint16_t handle
Characteristic Handle of this instance.
Definition: BTGattChar.hpp:126
BTDeviceRef getDeviceUnchecked() const noexcept
Definition: BTGattChar.cpp:181
BTGattDescRef getUserDescription() const noexcept
Return the User Description BTGattDescRef if available or nullptr.
Definition: BTGattChar.hpp:204
const PropertyBitVal properties
Definition: BTGattChar.hpp:129
const uint16_t value_handle
Characteristics Value Handle.
Definition: BTGattChar.hpp:137
bool removeCharListener(const BTGattCharListenerRef &l) noexcept
Remove the given associated BTGattCharListener from the listener list if present.
Definition: BTGattChar.cpp:285
jau::nsize_t size_type
Definition: BTGattChar.hpp:117
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.
Definition: BTGattChar.cpp:189
bool enableNotificationOrIndication(bool enabledState[2]) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
Definition: BTGattChar.cpp:244
size_type removeAllAssociatedCharListener(bool shallDisableIndicationNotification) noexcept
Removes all associated BTGattCharListener and and BTGattCharListener from the listener list.
Definition: BTGattChar.cpp:294
PropertyBitVal
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties.
Definition: BTGattChar.hpp:105
bool hasProperties(const PropertyBitVal v) const noexcept
Definition: BTGattChar.hpp:167
bool writeValue(const jau::TROOctets &value) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value.
Definition: BTGattChar.cpp:322
jau::darray< BTGattDescRef > descriptorList
List of Characteristic Descriptions as shared reference.
Definition: BTGattChar.hpp:143
bool readValue(jau::POctets &res, int expectedLength=-1) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value.
Definition: BTGattChar.cpp:306
bool writeValueNoResp(const jau::TROOctets &value) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response.
Definition: BTGattChar.cpp:339
Persistent endian aware octet data, i.e.
Definition: octets.hpp:560
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:763
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
uint32_t dfa_utf8_decode(uint32_t &state, uint32_t &codep, const uint32_t byte_value)
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
#define JAU_TYPENAME_CUE(A)
const char * type_name() noexcept
Returns the type name of given type T using template Compile Time Type Information (CTTI) only via RT...
std::shared_ptr< BTGattHandler > BTGattHandlerRef
Definition: BTGattChar.hpp:61
std::shared_ptr< BTDevice > BTDeviceRef
Definition: BTDevice.hpp:1347
std::string to_string(const DiscoveryPolicy v) noexcept
Definition: BTAdapter.cpp:58
std::string GattCharacteristicTypeToString(const GattCharacteristicType v) noexcept
GattCharacteristicType
GATT Assigned Characteristic Attribute Type for single logical value.
std::shared_ptr< BTGattCharListener > BTGattCharListenerRef
Definition: BTGattChar.hpp:70
std::shared_ptr< BTGattChar > BTGattCharRef
Definition: BTGattChar.hpp:410
std::shared_ptr< BTGattService > BTGattServiceRef
Definition: BTGattChar.hpp:67
std::shared_ptr< BTGattDesc > BTGattDescRef
Definition: BTGattDesc.hpp:190
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
constexpr const jau::fraction_i64 one(1l, 1lu)
one is 10^0 or 1/1
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
static const char * name()
Return the string representation of this type.
@ Notify
@ Indicate