Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
BTGattServerHandler.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2022 Gothel Software e.K.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 */
24
25#include <cstring>
26#include <string>
27#include <memory>
28#include <cstdint>
29#include <cstdio>
30
31#include <algorithm>
32
33extern "C" {
34 #include <unistd.h>
35 #include <sys/socket.h>
36 #include <poll.h>
37 #include <signal.h>
38}
39
40#include <jau/debug.hpp>
41
42#include <jau/basic_algos.hpp>
43
44#include "L2CAPIoctl.hpp"
45#include "GattNumbers.hpp"
46
47#include "BTGattHandler.hpp"
48
49#include "BTDevice.hpp"
50
51#include "BTManager.hpp"
52#include "BTAdapter.hpp"
53#include "BTManager.hpp"
54#include "DBTConst.hpp"
55
56using namespace direct_bt;
57
59 private:
60 void close_impl() noexcept {}
61
62 public:
63 NopGattServerHandler() noexcept = default;
64
65 ~NopGattServerHandler() override { close_impl(); }
66
67 void close() noexcept override { close_impl(); }
68
69 DBGattServer::Mode getMode() noexcept override { return DBGattServer::Mode::NOP; }
70
71 bool replyExchangeMTUReq(const AttExchangeMTU * pdu) noexcept override {
72 (void)pdu;
73 return true;
74 }
75
76 bool replyReadReq(const AttPDUMsg * pdu) noexcept override {
77 (void)pdu;
78 return true;
79 }
80
81 bool replyWriteReq(const AttPDUMsg * pdu) noexcept override {
82 (void)pdu;
83 return true;
84 }
85
86 bool replyFindInfoReq(const AttFindInfoReq * pdu) noexcept override {
87 (void)pdu;
88 return true;
89 }
90
91 bool replyFindByTypeValueReq(const AttFindByTypeValueReq * pdu) noexcept override {
92 (void)pdu;
93 return true;
94 }
95
96 bool replyReadByTypeReq(const AttReadByNTypeReq * pdu) noexcept override {
97 (void)pdu;
98 return true;
99 }
100
101 bool replyReadByGroupTypeReq(const AttReadByNTypeReq * pdu) noexcept override {
102 (void)pdu;
103 return true;
104 }
105};
106
108 private:
109 BTGattHandler& gh;
110 DBGattServerRef gattServerData;
111
112 jau::darray<AttPrepWrite> writeDataQueue;
113 jau::darray<uint16_t> writeDataQueueHandles;
114
115 void close_impl() noexcept {
116 BTDeviceRef device = gh.getDeviceUnchecked();
117 if( nullptr == device ) {
118 ERR_PRINT("null device: %s", gh.toString().c_str());
119 } else {
120 int i=0;
121 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
122 try {
123 l->disconnected(device);
124 } catch (std::exception &e) {
125 ERR_PRINT("%d/%zd: %s: Caught exception %s",
126 i+1, gattServerData->listener().size(),
127 gh.toString().c_str(), e.what());
128 }
129 i++;
130 });
131 }
132 writeDataQueue.clear();
133 writeDataQueueHandles.clear();
134 }
135
136 public:
138 : gh(gh_), gattServerData(std::move(gsd)) {}
139
140 ~DBGattServerHandler() override { close_impl(); }
141
142 void close() noexcept override { close_impl(); }
143
144 private:
145 bool hasServerHandle(const uint16_t handle) noexcept {
146 for(DBGattServiceRef& s : gattServerData->getServices()) {
147 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
148 for(DBGattCharRef& c : s->getCharacteristics()) {
149 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
150 if( handle == c->getValueHandle() ) {
151 return true;
152 }
153 for(DBGattDescRef& d : c->getDescriptors()) {
154 if( handle == d->getHandle() ) {
155 return true;
156 }
157 }
158 } // if characteristics-range
159 } // for characteristics
160 } // if service-range
161 } // for services
162 return false;
163 }
164
165 DBGattCharRef findServerGattCharByValueHandle(const uint16_t char_value_handle) noexcept {
166 return gattServerData->findGattCharByValueHandle(char_value_handle);
167 }
168
169 AttErrorRsp::ErrorCode applyWrite(BTDeviceRef device, const uint16_t handle, const jau::TROOctets & value, const uint16_t value_offset) noexcept {
170 for(DBGattServiceRef& s : gattServerData->getServices()) {
171 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
172 for(DBGattCharRef& c : s->getCharacteristics()) {
173 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
174 if( handle == c->getValueHandle() ) {
175 if( c->getValue().size() < value_offset) { // offset at value-end + 1 OK to append
176 return AttErrorRsp::ErrorCode::INVALID_OFFSET;
177 }
178 if( c->hasVariableLength() ) {
179 if( c->getValue().capacity() < value_offset + value.size() ) {
180 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
181 }
182 } else {
183 if( c->getValue().size() < value_offset + value.size() ) {
184 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
185 }
186 }
187 {
188 bool allowed = true;
189 int i=0;
190 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
191 try {
192 allowed = l->writeCharValue(device, s, c, value, value_offset) && allowed;
193 } catch (std::exception &e) {
194 ERR_PRINT("GATT-REQ: WRITE: (%s) %d/%zd: %s of %s: Caught exception %s",
195 c->toString().c_str(), i+1, gattServerData->listener().size(),
196 device->toString().c_str(), e.what());
197 }
198 i++;
199 });
200 if( !allowed ) {
201 return AttErrorRsp::ErrorCode::NO_WRITE_PERM;
202 }
203 }
204 if( c->hasVariableLength() ) {
205 if( c->getValue().size() != value_offset + value.size() ) {
206 c->getValue().resize( value_offset + value.size() );
207 }
208 }
209 c->getValue().put_octets_nc(value_offset, value);
210 return AttErrorRsp::ErrorCode::NO_ERROR;
211 }
212 for(DBGattDescRef& d : c->getDescriptors()) {
213 if( handle == d->getHandle() ) {
214 if( d->getValue().size() < value_offset) { // offset at value-end + 1 OK to append
215 return AttErrorRsp::ErrorCode::INVALID_OFFSET;
216 }
217 if( d->hasVariableLength() ) {
218 if( d->getValue().capacity() < value_offset + value.size() ) {
219 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
220 }
221 } else {
222 if( d->getValue().size() < value_offset + value.size() ) {
223 return AttErrorRsp::ErrorCode::INVALID_ATTRIBUTE_VALUE_LEN;
224 }
225 }
226 if( d->isUserDescription() ) {
227 return AttErrorRsp::ErrorCode::NO_WRITE_PERM;
228 }
229 const bool isCCCD = d->isClientCharConfig();
230 if( !isCCCD ) {
231 bool allowed = true;
232 int i=0;
233 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
234 try {
235 allowed = l->writeDescValue(device, s, c, d, value, value_offset) && allowed;
236 } catch (std::exception &e) {
237 ERR_PRINT("GATT-REQ: WRITE: (%s) %d/%zd: %s of %s: Caught exception %s",
238 d->toString().c_str(), i+1, gattServerData->listener().size(),
239 device->toString().c_str(), e.what());
240 }
241 i++;
242 });
243 if( !allowed ) {
244 return AttErrorRsp::ErrorCode::NO_WRITE_PERM;
245 }
246 }
247 if( d->hasVariableLength() ) {
248 if( d->getValue().size() != value_offset + value.size() ) {
249 d->getValue().resize( value_offset + value.size() );
250 }
251 }
252 if( isCCCD ) {
253 if( value.size() == 0 ) {
254 // no change, exit
255 return AttErrorRsp::ErrorCode::NO_ERROR;
256 }
257 const uint8_t old_v = d->getValue().get_uint8_nc(0);
258 const bool oldEnableNotification = old_v & 0b001;
259 const bool oldEnableIndication = old_v & 0b010;
260
261 const uint8_t req_v = value.get_uint8_nc(0);
262 const bool reqEnableNotification = req_v & 0b001;
263 const bool reqEnableIndication = req_v & 0b010;
264 const bool hasNotification = c->hasProperties(BTGattChar::PropertyBitVal::Notify);
265 const bool hasIndication = c->hasProperties(BTGattChar::PropertyBitVal::Indicate);
266 const bool enableNotification = reqEnableNotification && hasNotification;
267 const bool enableIndication = reqEnableIndication && hasIndication;
268
269 if( oldEnableNotification == enableNotification &&
270 oldEnableIndication == enableIndication ) {
271 // no change, exit
272 return AttErrorRsp::ErrorCode::NO_ERROR;
273 }
274 const uint16_t new_v = enableNotification | ( enableIndication << 1 );
275 d->getValue().put_uint8_nc(0, new_v);
276 {
277 int i=0;
278 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
279 try {
280 l->clientCharConfigChanged(device, s, c, d, enableNotification, enableIndication);
281 } catch (std::exception &e) {
282 ERR_PRINT("GATT-REQ: WRITE CCCD: (%s) %d/%zd: %s of %s: Caught exception %s",
283 d->toString().c_str(), i+1, gattServerData->listener().size(),
284 device->toString().c_str(), e.what());
285 }
286 i++;
287 });
288 }
289 } else {
290 // all other types ..
291 d->getValue().put_octets_nc(value_offset, value);
292 }
293 return AttErrorRsp::ErrorCode::NO_ERROR;
294 }
295 }
296 } // if characteristics-range
297 } // for characteristics
298 } // if service-range
299 } // for services
300 return AttErrorRsp::ErrorCode::INVALID_HANDLE;
301 }
302
303 void signalWriteDone(BTDeviceRef device, const uint16_t handle) noexcept {
304 for(DBGattServiceRef& s : gattServerData->getServices()) {
305 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
306 for(DBGattCharRef& c : s->getCharacteristics()) {
307 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
308 if( handle == c->getValueHandle() ) {
309 {
310 int i=0;
311 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
312 try {
313 l->writeCharValueDone(device, s, c);
314 } catch (std::exception &e) {
315 ERR_PRINT("GATT-REQ: WRITE-Done: (%s) %d/%zd: %s of %s: Caught exception %s",
316 c->toString().c_str(), i+1, gattServerData->listener().size(),
317 device->toString().c_str(), e.what());
318 }
319 i++;
320 });
321 }
322 return;
323 }
324 for(DBGattDescRef& d : c->getDescriptors()) {
325 if( handle == d->getHandle() ) {
326 if( d->isUserDescription() ) {
327 return;
328 }
329 const bool isCCCD = d->isClientCharConfig();
330 if( !isCCCD ) {
331 int i=0;
332 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
333 try {
334 l->writeDescValueDone(device, s, c, d);
335 } catch (std::exception &e) {
336 ERR_PRINT("GATT-REQ: WRITE-Done: (%s) %d/%zd: %s of %s: Caught exception %s",
337 d->toString().c_str(), i+1, gattServerData->listener().size(),
338 device->toString().c_str(), e.what());
339 }
340 i++;
341 });
342 }
343 return;
344 }
345 }
346 } // if characteristics-range
347 } // for characteristics
348 } // if service-range
349 } // for services
350 return;
351 }
352
353
354 public:
355 DBGattServer::Mode getMode() noexcept override { return DBGattServer::Mode::DB; }
356
357 bool replyExchangeMTUReq(const AttExchangeMTU * pdu) noexcept override {
358 const uint16_t clientMTU = pdu->getMTUSize();
359 gh.setUsedMTU( std::min(gh.getServerMTU(), clientMTU) );
360 const AttExchangeMTU rsp(AttPDUMsg::ReqRespType::RESPONSE, gh.getUsedMTU());
361 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: MTU recv: %u, %s -> %u %s from %s",
362 clientMTU, pdu->toString().c_str(),
363 gh.getUsedMTU(), rsp.toString().c_str(), gh.toString().c_str());
364 if( nullptr != gattServerData ) {
365 BTDeviceRef device = gh.getDeviceUnchecked();
366 if( nullptr != device ) {
367 int i=0;
368 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
369 try {
370 l->mtuChanged(device, gh.getUsedMTU());
371 } catch (std::exception &e) {
372 ERR_PRINT("%d/%zd: %s: Caught exception %s",
373 i+1, gattServerData->listener().size(),
374 gh.toString().c_str(), e.what());
375 }
376 i++;
377 });
378 }
379 }
380 return gh.send(rsp);
381 }
382
383 bool replyWriteReq(const AttPDUMsg * pdu) noexcept override {
384 /**
385 * Without Response:
386 * BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.3 ATT_WRITE_CMD
387 * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value without Response
388 *
389 * With Response:
390 * BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.1 ATT_WRITE_REQ
391 * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value
392 * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
393 *
394 * BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.2 ATT_WRITE_RSP
395 * BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value
396 */
397 BTDeviceRef device = gh.getDeviceUnchecked();
398 if( nullptr == device ) {
399 AttErrorRsp err(AttErrorRsp::ErrorCode::UNLIKELY_ERROR, pdu->getOpcode(), 0);
400 ERR_PRINT("GATT-Req: WRITE.0, null device: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
401 return gh.send(err);
402 }
403
404 if( AttPDUMsg::Opcode::PREPARE_WRITE_REQ == pdu->getOpcode() ) {
405 const AttPrepWrite * req = static_cast<const AttPrepWrite*>(pdu);
406 if( !hasServerHandle( req->getHandle() ) ) {
407 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, req->getOpcode(), req->getHandle());
408 WARN_PRINT("GATT-Req: WRITE.10: %s -> %s from %s", req->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
409 return gh.send(err);
410 }
411 const uint16_t handle = req->getHandle();
412 AttPrepWrite rsp(false, *req);
413 writeDataQueue.push_back(rsp);
414 if( writeDataQueueHandles.cend() == jau::find_if(writeDataQueueHandles.cbegin(), writeDataQueueHandles.cend(),
415 [&](const uint16_t it)->bool { return handle == it; }) )
416 {
417 // new entry
418 writeDataQueueHandles.push_back(handle);
419 }
420 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.11: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
421 return gh.send(rsp);
422 } else if( AttPDUMsg::Opcode::EXECUTE_WRITE_REQ == pdu->getOpcode() ) {
423 const AttExeWriteReq * req = static_cast<const AttExeWriteReq*>(pdu);
424 if( 0x01 == req->getFlags() ) { // immediately write all pending prepared values
425 AttErrorRsp::ErrorCode res = AttErrorRsp::ErrorCode::NO_ERROR;
426 for( auto iter_handle = writeDataQueueHandles.cbegin(); iter_handle < writeDataQueueHandles.cend(); ++iter_handle ) {
427 for( auto iter_prep_write = writeDataQueue.cbegin(); iter_prep_write < writeDataQueue.cend(); ++iter_prep_write ) {
428 const AttPrepWrite &p = *iter_prep_write;
429 const uint16_t handle = p.getHandle();
430 if( handle == *iter_handle ) {
431 const jau::TOctetSlice &p_value = p.getValue();
432 jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
433 res = applyWrite(device, handle, p_val, p.getValueOffset());
434
435 if( AttErrorRsp::ErrorCode::NO_ERROR != res ) {
436 writeDataQueue.clear();
437 writeDataQueueHandles.clear();
438 AttErrorRsp err(res, pdu->getOpcode(), handle);
439 WARN_PRINT("GATT-Req: WRITE.12: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
440 return gh.send(err);
441 }
442 }
443 }
444 }
445 for( auto iter_handle = writeDataQueueHandles.cbegin(); iter_handle < writeDataQueueHandles.cend(); ++iter_handle ) {
446 signalWriteDone(device, *iter_handle);
447 }
448 } // else 0x00 == req->getFlags() -> cancel all prepared writes
449 writeDataQueue.clear();
450 writeDataQueueHandles.clear();
451 AttExeWriteRsp rsp;
452 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.13: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
453 return gh.send(rsp);
454 }
455
456 uint16_t handle = 0;
457 const jau::TOctetSlice * vslice = nullptr;
458 bool withResp;
459 if( AttPDUMsg::Opcode::WRITE_REQ == pdu->getOpcode() ) {
460 const AttWriteReq * req = static_cast<const AttWriteReq*>(pdu);
461 handle = req->getHandle();
462 vslice = &req->getValue();
463 withResp = true;
464 } else if( AttPDUMsg::Opcode::WRITE_CMD == pdu->getOpcode() ) {
465 const AttWriteCmd * req = static_cast<const AttWriteCmd*>(pdu);
466 handle = req->getHandle();
467 vslice = &req->getValue();
468 withResp = false;
469 } else {
470 // Actually an internal error, method should not have been called
471 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
472 WARN_PRINT("GATT-Req: WRITE.20: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
473 return gh.send(err);
474 }
475 jau::TROOctets req_val(vslice->get_ptr_nc(0), vslice->size(), vslice->byte_order());
476 AttErrorRsp::ErrorCode res = applyWrite(device, handle, req_val, 0);
477 if( AttErrorRsp::ErrorCode::NO_ERROR != res ) {
478 AttErrorRsp err(res, pdu->getOpcode(), handle);
479 WARN_PRINT("GATT-Req: WRITE.21: %s -> %s (sent %d) from %s",
480 pdu->toString().c_str(), err.toString().c_str(), (int)withResp, gh.toString().c_str());
481 if( withResp ) {
482 return gh.send(err);
483 }
484 return true;;
485 }
486 if( withResp ) {
487 AttWriteRsp rsp;
488 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.22: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
489 if( !gh.send(rsp) ) {
490 return false;
491 }
492 }
493 signalWriteDone(device, handle);
494 return true;
495 }
496
497 bool replyReadReq(const AttPDUMsg * pdu) noexcept override {
498 /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value */
499 /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value */
500 /* For any follow up request, which previous request reply couldn't fit in ATT_MTU */
501 BTDeviceRef device = gh.getDeviceUnchecked();
502 if( nullptr == device ) {
503 AttErrorRsp err(AttErrorRsp::ErrorCode::UNLIKELY_ERROR, pdu->getOpcode(), 0);
504 ERR_PRINT("GATT-Req: READ, null device: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
505 return gh.send(err);
506 }
507 uint16_t handle = 0;
508 uint16_t value_offset = 0;
509 bool isBlobReq;
510 if( AttPDUMsg::Opcode::READ_REQ == pdu->getOpcode() ) {
511 const AttReadReq * req = static_cast<const AttReadReq*>(pdu);
512 handle = req->getHandle();
513 isBlobReq = false;
514 } else if( AttPDUMsg::Opcode::READ_BLOB_REQ == pdu->getOpcode() ) {
515 /**
516 * BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value
517 *
518 * If the Characteristic Value is not longer than (ATT_MTU – 1)
519 * an ATT_ERROR_RSP PDU with the error
520 * code set to Attribute Not Long shall be received on the first
521 * ATT_READ_BLOB_REQ PDU.
522 */
523 const AttReadBlobReq * req = static_cast<const AttReadBlobReq*>(pdu);
524 handle = req->getHandle();
525 value_offset = req->getValueOffset();
526 isBlobReq = true;
527 } else {
528 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
529 WARN_PRINT("GATT-Req: READ: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
530 return gh.send(err);
531 }
532 if( 0 == handle ) {
533 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
534 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.0: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
535 return gh.send(err);
536 }
537 const jau::nsize_t rspMaxSize = gh.getUsedMTU()-1;
538 (void)rspMaxSize;
539
540 for(DBGattServiceRef& s : gattServerData->getServices()) {
541 if( s->getHandle() <= handle && handle <= s->getEndHandle() ) {
542 /**
543 * AttReadByGroupTypeRsp (1 opcode + 1 element_size + 2 handle + 2 handle + 16 uuid128_t = 22 bytes)
544 * always fits in minimum ATT_PDU 23
545 */
546 for(DBGattCharRef& c : s->getCharacteristics()) {
547 if( c->getHandle() <= handle && handle <= c->getEndHandle() ) {
548 if( handle == c->getValueHandle() ) {
549 if( isBlobReq ) {
550#if SEND_ATTRIBUTE_NOT_LONG
551 if( c->getValue().size() <= rspMaxSize ) {
552 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_LONG, pdu->getOpcode(), handle);
553 COND_PRINT(env.DEBUG_DATA, "GATT-Req: READ.0: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), toString().c_str());
554 gh.send(err);
555 return;
556 }
557#endif
558 if( value_offset > c->getValue().size() ) {
559 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_OFFSET, pdu->getOpcode(), handle);
560 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.1: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
561 return gh.send(err);
562 }
563 }
564 {
565 bool allowed = true;
566 int i=0;
567 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
568 try {
569 allowed = l->readCharValue(device, s, c) && allowed;
570 } catch (std::exception &e) {
571 ERR_PRINT("GATT-REQ: READ: (%s) %d/%zd: %s of %s: Caught exception %s",
572 c->toString().c_str(), i+1, gattServerData->listener().size(),
573 device->toString().c_str(), e.what());
574 }
575 i++;
576 });
577 if( !allowed ) {
578 AttErrorRsp err(AttErrorRsp::ErrorCode::NO_READ_PERM, pdu->getOpcode(), handle);
579 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.2: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
580 return gh.send(err);
581 }
582 }
583 AttReadNRsp rsp(isBlobReq, c->getValue(), value_offset); // Blob: value_size == value_offset -> OK, ends communication
584 if( rsp.getPDUValueSize() > rspMaxSize ) {
585 rsp.pdu.resize(gh.getUsedMTU()); // requires another READ_BLOB_REQ
586 }
587 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.3: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
588 return gh.send(rsp);
589 }
590 for(DBGattDescRef& d : c->getDescriptors()) {
591 if( handle == d->getHandle() ) {
592 if( isBlobReq ) {
593#if SEND_ATTRIBUTE_NOT_LONG
594 if( isBlobReq && d->getValue().size() <= rspMaxSize ) {
595 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_LONG, pdu->getOpcode(), handle);
596 COND_PRINT(env.DEBUG_DATA, "GATT-Req: READ.0: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), toString().c_str());
597 gh.send(err);
598 return;
599 }
600#endif
601 if( value_offset > c->getValue().size() ) {
602 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_OFFSET, pdu->getOpcode(), handle);
603 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.1: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
604 return gh.send(err);
605 }
606 }
607 {
608 bool allowed = true;
609 int i=0;
610 jau::for_each_fidelity(gattServerData->listener(), [&](DBGattServer::ListenerRef &l) {
611 try {
612 allowed = l->readDescValue(device, s, c, d) && allowed;
613 } catch (std::exception &e) {
614 ERR_PRINT("GATT-REQ: READ: (%s) %d/%zd: %s of %s: Caught exception %s",
615 d->toString().c_str(), i+1, gattServerData->listener().size(),
616 device->toString().c_str(), e.what());
617 }
618 i++;
619 });
620 if( !allowed ) {
621 AttErrorRsp err(AttErrorRsp::ErrorCode::NO_READ_PERM, pdu->getOpcode(), handle);
622 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.4: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
623 return gh.send(err);
624 }
625 }
626 AttReadNRsp rsp(isBlobReq, d->getValue(), value_offset); // Blob: value_size == value_offset -> OK, ends communication
627 if( rsp.getPDUValueSize() > rspMaxSize ) {
628 rsp.pdu.resize(gh.getUsedMTU()); // requires another READ_BLOB_REQ
629 }
630 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.5: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
631 return gh.send(rsp);
632 }
633 }
634 } // if characteristics-range
635 } // for characteristics
636 } // if service-range
637 } // for services
638 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), handle);
639 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ.6: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
640 return gh.send(err);
641 }
642
643 bool replyFindInfoReq(const AttFindInfoReq * pdu) noexcept override {
644 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.1 ATT_FIND_INFORMATION_REQ
645 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.2 ATT_FIND_INFORMATION_RSP
646 // BT Core Spec v5.2: Vol 3, Part G GATT: 4.7.1 Discover All Characteristic Descriptors
647 if( 0 == pdu->getStartHandle() ) {
648 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
649 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: INFO.0: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
650 return gh.send(err);
651 }
652 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
653 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
654 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: INFO.1: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
655 return gh.send(err);
656 }
657 const uint16_t end_handle = pdu->getEndHandle();
658 const uint16_t start_handle = pdu->getStartHandle();
659
660 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
661 AttFindInfoRsp rsp(gh.getUsedMTU()); // maximum size
662 jau::nsize_t rspElemSize = 0;
663 jau::nsize_t rspSize = 0;
664 jau::nsize_t rspCount = 0;
665
666 for(DBGattServiceRef& s : gattServerData->getServices()) {
667 for(DBGattCharRef& c : s->getCharacteristics()) {
668 for(DBGattDescRef& d : c->getDescriptors()) {
669 if( start_handle <= d->getHandle() && d->getHandle() <= end_handle ) {
670 const jau::nsize_t size = 2 + d->getType()->getTypeSizeInt();
671 if( 0 == rspElemSize ) {
672 // initial setting or reset
673 rspElemSize = size;
674 rsp.setElementSize(rspElemSize);
675 }
676 if( rspSize + size > rspMaxSize || rspElemSize != size ) {
677 // send if rsp is full - or - element size changed
678 rsp.setElementCount(rspCount);
679 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: INFO.2: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
680 return gh.send(rsp); // Client shall issue additional FIND_INFORMATION_REQ
681 }
682 rsp.setElementHandle(rspCount, d->getHandle());
683 rsp.setElementValueUUID(rspCount, *d->getType());
684 rspSize += size;
685 ++rspCount;
686 }
687 }
688 }
689 }
690 if( 0 < rspCount ) { // loop completed, elements added and all fitting in ATT_MTU
691 rsp.setElementCount(rspCount);
692 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: INFO.3: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
693 return gh.send(rsp);
694 }
695 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), start_handle);
696 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: INFO.4: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
697 return gh.send(err);
698 }
699
700 bool replyFindByTypeValueReq(const AttFindByTypeValueReq * pdu) noexcept override {
701 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.3 ATT_FIND_BY_TYPE_VALUE_REQ
702 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.4 ATT_FIND_BY_TYPE_VALUE_RSP
703 // BT Core Spec v5.2: Vol 3, Part G GATT: 4.4.2 Discover Primary Service by Service UUID
704 if( 0 == pdu->getStartHandle() ) {
705 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
706 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPEVALUE.0: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
707 return gh.send(err);
708 }
709 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
710 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
711 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPEVALUE.1: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
712 return gh.send(err);
713 }
716 const uint16_t end_handle = pdu->getEndHandle();
717 const uint16_t start_handle = pdu->getStartHandle();
718 const jau::uuid16_t att_type = pdu->getAttType();
719 std::unique_ptr<const jau::uuid_t> att_value = pdu->getAttValue();
720
721 uint16_t req_group_type;
722 if( att_type.equivalent( uuid_prim_service ) ) {
723 req_group_type = GattAttributeType::PRIMARY_SERVICE;
724 } else if( att_type.equivalent( uuid_secd_service ) ) {
726 } else {
727 // not handled
728 req_group_type = 0;
729 }
730
731 // const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, getUsedMTU()-2);
732 AttFindByTypeValueRsp rsp(gh.getUsedMTU()); // maximum size
733 // jau::nsize_t rspSize = 0;
734 jau::nsize_t rspCount = 0;
735
736 try {
737 // const jau::nsize_t size = 2 + 2;
738
739 for(DBGattServiceRef& s : gattServerData->getServices()) {
740 if( start_handle <= s->getHandle() && s->getHandle() <= end_handle ) {
741 if( ( ( GattAttributeType::PRIMARY_SERVICE == req_group_type && s->isPrimary() ) ||
742 ( GattAttributeType::SECONDARY_SERVICE == req_group_type && !s->isPrimary() )
743 ) &&
744 s->getType()->equivalent(*att_value) )
745 {
746 rsp.setElementHandles(rspCount, s->getHandle(), s->getEndHandle());
747 // rspSize += size;
748 ++rspCount;
749 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPEVALUE.4: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
750 return gh.send(rsp); // done
751 }
752 }
753 }
754 if( 0 < rspCount ) { // loop completed, elements added and all fitting in ATT_MTU
755 rsp.setElementCount(rspCount);
756 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPEVALUE.5: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
757 return gh.send(rsp);
758 }
759 } catch (const jau::ExceptionBase &e) {
760 ERR_PRINT("invalid att uuid: %s", e.message().c_str());
761 } catch (...) {
762 ERR_PRINT("invalid att uuid: Unknown exception");
763 }
764 try {
765 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), start_handle);
766 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPEVALUE.6: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
767 return gh.send(err);
768 } catch (const jau::ExceptionBase &e) {
769 ERR_PRINT("invalid att uuid: %s", e.message().c_str());
770 } catch (...) {
771 ERR_PRINT("invalid att uuid: Unknown exception");
772 }
773 return false;
774 }
775
776 bool replyReadByTypeReq(const AttReadByNTypeReq * pdu) noexcept override {
777 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.1 ATT_READ_BY_TYPE_REQ
778 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.2 ATT_READ_BY_TYPE_RSP
779 if( 0 == pdu->getStartHandle() ) {
780 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
781 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.0: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
782 return gh.send(err);
783 }
784 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
785 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
786 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.1: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
787 return gh.send(err);
788 }
791 std::unique_ptr<const jau::uuid_t> req_attribute = pdu->getNType();
792 uint16_t req_type;
793 if( req_attribute->equivalent( uuid_characteristic ) ) {
794 // BT Core Spec v5.2: Vol 3, Part G GATT: 4.6.1 Discover All Characteristics of a Service
796 } else if( req_attribute->equivalent( uuid_incl_service ) ) {
798 } else {
799 // BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.2 Read Using Characteristic UUID
800 req_type = 0;
801 }
802 if( GattAttributeType::CHARACTERISTIC == req_type ) {
803 // BT Core Spec v5.2: Vol 3, Part G GATT: 4.6.1 Discover All Characteristics of a Service
804 const uint16_t end_handle = pdu->getEndHandle();
805 const uint16_t start_handle = pdu->getStartHandle();
806
807 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
808 // Attribute Handle and Attribute Value pairs corresponding to the Characteristic
809 // - Attribute Handle is the handle for the Characteristic
810 // - Attribute Value contains Properties, Value-Handle and UUID of the Characteristic
811 AttReadByTypeRsp rsp(gh.getUsedMTU()); // maximum size
812 jau::nsize_t rspElemSize = 0;
813 jau::nsize_t rspSize = 0;
814 jau::nsize_t rspCount = 0;
815
816 for(DBGattServiceRef& s : gattServerData->getServices()) {
817 for(DBGattCharRef& c : s->getCharacteristics()) {
818 if( start_handle <= c->getHandle() && c->getHandle() <= end_handle ) {
819 const jau::nsize_t size = 2 + 1 + 2 + c->getValueType()->getTypeSizeInt();
820 if( 0 == rspElemSize ) {
821 // initial setting or reset
822 rspElemSize = size;
823 rsp.setElementSize(rspElemSize);
824 }
825 if( rspSize + size > rspMaxSize || rspElemSize != size ) {
826 // send if rsp is full - or - element size changed
827 rsp.setElementCount(rspCount);
828 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.2: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
829 return gh.send(rsp); // Client shall issue additional READ_BY_TYPE_REQ
830 }
831 jau::nsize_t ePDUOffset = rsp.getElementPDUOffset(rspCount);
832 rsp.setElementHandle(rspCount, c->getHandle()); // Characteristic Handle
833 ePDUOffset += 2;
834 rsp.pdu.put_uint8_nc(ePDUOffset, c->getProperties()); // Characteristics Property
835 ePDUOffset += 1;
836 rsp.pdu.put_uint16_nc(ePDUOffset, c->getValueHandle()); // Characteristics Value Handle
837 ePDUOffset += 2;
838 c->getValueType()->put(rsp.pdu.get_wptr_nc(ePDUOffset) + 0, jau::lb_endian_t::little); // Characteristics Value Type UUID
839 ePDUOffset += c->getValueType()->getTypeSizeInt();
840 rspSize += size;
841 ++rspCount;
842 (void)ePDUOffset;
843 }
844 }
845 }
846 if( 0 < rspCount ) { // loop completed, elements added and all fitting in ATT_MTU
847 rsp.setElementCount(rspCount);
848 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.3: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
849 return gh.send(rsp);
850 }
851 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
852 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.4: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
853 return gh.send(err);
854 } else if( GattAttributeType::INCLUDE_DECLARATION == req_type ) {
855 // TODO: Support INCLUDE_DECLARATION ??
856 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
857 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.5: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
858 return gh.send(err);
859 } else { // TODO: Add other group types ???
860 // BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.2 Read Using Characteristic UUID
861 const uint16_t end_handle = pdu->getEndHandle();
862 const uint16_t start_handle = pdu->getStartHandle();
863
864 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
865 // Attribute Handle and Attribute Value pairs corresponding to the Characteristic
866 // - Attribute Handle is the handle for the Characteristic
867 // - Attribute Value contains the value of the Characteristic
868 AttReadByTypeRsp rsp(gh.getUsedMTU()); // maximum size
869
870 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.6: Searching for %s, req %s from %s",
871 req_attribute->toString().c_str(), pdu->toString().c_str(), gh.toString().c_str());
872 for(DBGattServiceRef& s : gattServerData->getServices()) {
873 for(DBGattCharRef& c : s->getCharacteristics()) {
874 if( start_handle <= c->getHandle() && c->getHandle() <= end_handle && c->getValueType()->equivalent(*req_attribute) ) {
875 jau::POctets& value = c->getValue();
876 const jau::nsize_t value_size_max = std::min(value.size(), rspMaxSize-2);
877 const jau::nsize_t size = 2 + value_size_max;
878 rsp.setElementSize(size);
879 jau::nsize_t ePDUOffset = rsp.getElementPDUOffset(0);
880 rsp.setElementHandle(0, c->getHandle()); // Characteristic Handle
881 ePDUOffset += 2;
882 rsp.pdu.put_bytes(ePDUOffset, value.get_ptr(), value_size_max);
883 (void)ePDUOffset;
884 rsp.setElementCount(1);
885 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.6: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
886 return gh.send(rsp);
887 }
888 }
889 }
890 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
891 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE.7: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
892 return gh.send(err);
893 }
894 }
895
896 bool replyReadByGroupTypeReq(const AttReadByNTypeReq * pdu) noexcept override {
897 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.9 ATT_READ_BY_GROUP_TYPE_REQ
898 // BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.10 ATT_READ_BY_GROUP_TYPE_RSP
899 // BT Core Spec v5.2: Vol 3, Part G GATT: 4.4.1 Discover All Primary Services
900 if( 0 == pdu->getStartHandle() ) {
901 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), 0);
902 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE.0: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
903 return gh.send(err);
904 }
905 if( pdu->getStartHandle() > pdu->getEndHandle() ) {
906 AttErrorRsp err(AttErrorRsp::ErrorCode::INVALID_HANDLE, pdu->getOpcode(), pdu->getStartHandle());
907 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE.1: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
908 return gh.send(err);
909 }
912 std::unique_ptr<const jau::uuid_t> req_attribute_group = pdu->getNType();
913 uint16_t req_group_type;
914 if( req_attribute_group->equivalent( uuid_prim_service ) ) {
915 req_group_type = GattAttributeType::PRIMARY_SERVICE;
916 } else if( req_attribute_group->equivalent( uuid_secd_service ) ) {
918 } else {
919 // not handled
920 req_group_type = 0;
921 }
922 if( 0 != req_group_type ) {
923 const uint16_t end_handle = pdu->getEndHandle();
924 const uint16_t start_handle = pdu->getStartHandle();
925
926 const jau::nsize_t rspMaxSize = std::min<jau::nsize_t>(255, gh.getUsedMTU()-2);
927 AttReadByGroupTypeRsp rsp(gh.getUsedMTU()); // maximum size
928 jau::nsize_t rspElemSize = 0;
929 jau::nsize_t rspSize = 0;
930 jau::nsize_t rspCount = 0;
931
932 for(DBGattServiceRef& s : gattServerData->getServices()) {
933 if( ( ( GattAttributeType::PRIMARY_SERVICE == req_group_type && s->isPrimary() ) ||
934 ( GattAttributeType::SECONDARY_SERVICE == req_group_type && !s->isPrimary() )
935 ) &&
936 start_handle <= s->getHandle() && s->getHandle() <= end_handle )
937 {
938 const jau::nsize_t size = 2 + 2 + s->getType()->getTypeSizeInt();
939 if( 0 == rspElemSize ) {
940 // initial setting or reset
941 rspElemSize = size;
942 rsp.setElementSize(rspElemSize);
943 }
944 if( rspSize + size > rspMaxSize || rspElemSize != size ) {
945 // send if rsp is full - or - element size changed
946 /**
947 * AttReadByGroupTypeRsp (1 opcode + 1 element_size + 2 handle + 2 handle + 16 uuid128_t = 22 bytes)
948 * always fits in minimum ATT_PDU 23
949 */
950 rsp.setElementCount(rspCount);
951 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE.3: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
952 return gh.send(rsp); // Client shall issue additional READ_BY_TYPE_REQ
953 }
954 rsp.setElementStartHandle(rspCount, s->getHandle());
955 rsp.setElementEndHandle(rspCount, s->getEndHandle());
956 rsp.setElementValueUUID(rspCount, *s->getType());
957 rspSize += size;
958 ++rspCount;
959 }
960 }
961 if( 0 < rspCount ) { // loop completed, elements added and all fitting in ATT_MTU
962 rsp.setElementCount(rspCount);
963 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE.4: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), gh.toString().c_str());
964 return gh.send(rsp);
965 }
966 AttErrorRsp err(AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_FOUND, pdu->getOpcode(), pdu->getStartHandle());
967 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE.5: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
968 return gh.send(err);
969 } else {
970 // TODO: Add other group types ???
971 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_GROUP_TYPE, pdu->getOpcode(), pdu->getStartHandle());
972 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE.6: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
973 return gh.send(err);
974 }
975 }
976};
977
979 private:
980 BTGattHandler& gh;
981 BTDeviceRef fwdServer;
982 std::shared_ptr<BTGattHandler> fwd_gh;
983
984 jau::darray<AttPrepWrite> writeDataQueue;
985 jau::darray<uint16_t> writeDataQueueHandles;
986
987 void close_impl() noexcept {
988 writeDataQueue.clear();
989 writeDataQueueHandles.clear();
990 }
991
992 public:
994 : gh(gh_), fwdServer(std::move(fwdServer_))
995 {
996 fwd_gh = fwdServer->getGattHandler();
997 }
998
999 ~FwdGattServerHandler() override { close_impl(); }
1000
1001 void close() noexcept override { close_impl(); }
1002
1003 DBGattServer::Mode getMode() noexcept override { return DBGattServer::Mode::FWD; }
1004
1005 bool replyExchangeMTUReq(const AttExchangeMTU * pdu) noexcept override {
1006 if( !fwd_gh->isConnected() ) {
1007 close();
1008 return false;
1009 }
1010 BTDeviceRef clientSource = gh.getDeviceUnchecked();
1011 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1012 const uint16_t clientMTU = pdu->getMTUSize();
1013 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
1014 if( nullptr == rsp ) {
1015 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1016 return false;
1017 }
1018 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: MTU: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1019 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1020 if( AttPDUMsg::Opcode::EXCHANGE_MTU_RSP == rsp->getOpcode() ) {
1021 const AttExchangeMTU* mtuRsp = static_cast<const AttExchangeMTU*>( rsp.get() );
1022 const uint16_t serverMTU = mtuRsp->getMTUSize();
1023 gh.setUsedMTU( std::min(gh.getServerMTU(), std::min(clientMTU, serverMTU) ) );
1024 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: MTU: %u -> %u -> %u", clientMTU, serverMTU, gh.getUsedMTU());
1025 fwd_gh->notifyNativeMTUResponse(clientMTU, *rsp, AttErrorRsp::ErrorCode::NO_ERROR, serverMTU, gh.getUsedMTU(), clientSource);
1026 } else {
1027 const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
1028 static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1029 fwd_gh->notifyNativeMTUResponse(clientMTU, *rsp, error_code, 0, 0, clientSource);
1030 }
1031 return gh.send(*rsp);
1032 }
1033
1034 bool replyWriteReq(const AttPDUMsg * pdu) noexcept override {
1035 if( !fwd_gh->isConnected() ) {
1036 close();
1037 return false;
1038 }
1039 BTDeviceRef clientSource = gh.getDeviceUnchecked();
1040
1041 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1042
1043 if( AttPDUMsg::Opcode::PREPARE_WRITE_REQ == pdu->getOpcode() ) {
1044 {
1045 const AttPrepWrite * req = static_cast<const AttPrepWrite*>(pdu);
1046 const uint16_t handle = req->getHandle();
1047 writeDataQueue.push_back(*req);
1048 if( writeDataQueueHandles.cend() == jau::find_if(writeDataQueueHandles.cbegin(), writeDataQueueHandles.cend(),
1049 [&](const uint16_t it)->bool { return handle == it; }) )
1050 {
1051 // new entry
1052 writeDataQueueHandles.push_back(handle);
1053 }
1054 }
1055 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
1056 if( nullptr == rsp ) {
1057 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1058 return false;
1059 }
1060 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.11: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1061 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1062 {
1063 const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
1064 static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1065 fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
1066 }
1067 return gh.send(*rsp);
1068 } else if( AttPDUMsg::Opcode::EXECUTE_WRITE_REQ == pdu->getOpcode() ) {
1069 {
1070 const AttExeWriteReq * req = static_cast<const AttExeWriteReq*>(pdu);
1071 if( 0x01 == req->getFlags() ) { // immediately write all pending prepared values
1072 for( auto iter_handle = writeDataQueueHandles.cbegin(); iter_handle < writeDataQueueHandles.cend(); ++iter_handle ) {
1073 const jau::lb_endian_t byte_order = writeDataQueue.size() > 0 ? writeDataQueue[0].getValue().byte_order() : jau::lb_endian_t::little;
1074 jau::POctets data(256, 0, byte_order); // same byte order across all requests
1076 for( auto iter_prep_write = writeDataQueue.cbegin(); iter_prep_write < writeDataQueue.cend(); ++iter_prep_write ) {
1077 const AttPrepWrite &p = *iter_prep_write;
1078 const uint16_t handle = p.getHandle();
1079 if( handle == *iter_handle ) {
1080 const jau::TOctetSlice &p_value = p.getValue();
1081 jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
1082 const jau::nsize_t p_end = p.getValueOffset() + p_value.size();
1083 if( p_end > data.capacity() ) {
1084 data.recapacity(p_end);
1085 }
1086 if( p_end > data.size() ) {
1087 data.resize(p_end);
1088 }
1089 data.put_octets_nc(p.getValueOffset(), p_val);
1091 if( sections.size() > 0 &&
1092 section.start >= sections[sections.size()-1].start &&
1093 section.start <= sections[sections.size()-1].end )
1094 {
1095 // quick merge of consecutive sections write requests
1096 if( section.end > sections[sections.size()-1].end ) {
1097 sections[sections.size()-1].end = section.end;
1098 } // else section lies within last section
1099 } else {
1100 sections.push_back(section);
1101 }
1102 }
1103 if( iter_prep_write + 1 == writeDataQueue.cend() ) {
1104 // last entry
1105 fwd_gh->notifyNativeWriteRequest(handle, data, sections, true /* with_response */, clientSource);
1106 }
1107 }
1108 }
1109 } // else 0x00 == req->getFlags() -> cancel all prepared writes
1110 writeDataQueue.clear();
1111 writeDataQueueHandles.clear();
1112 }
1113 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
1114 if( nullptr == rsp ) {
1115 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1116 return false;
1117 }
1118 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.13: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1119 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1120 {
1121 const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
1122 static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1123 fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
1124 }
1125 return gh.send(*rsp);
1126 }
1127
1128 if( AttPDUMsg::Opcode::WRITE_REQ == pdu->getOpcode() ) {
1129 {
1130 const AttWriteReq &p = *static_cast<const AttWriteReq*>(pdu);
1131 const jau::TOctetSlice &p_value = p.getValue();
1133 jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
1134 sections.emplace_back( 0, (uint16_t)p_value.size() );
1135 fwd_gh->notifyNativeWriteRequest(p.getHandle(), p_val, sections, true /* with_response */, clientSource);
1136 }
1137 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.write_cmd_reply_timeout); // valid reply or exception
1138 if( nullptr == rsp ) {
1139 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1140 return false;
1141 }
1142 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.22: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1143 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1144 {
1145 const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
1146 static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1147 fwd_gh->notifyNativeWriteResponse(*rsp, error_code, clientSource);
1148 }
1149 return gh.send(*rsp);
1150 } else if( AttPDUMsg::Opcode::WRITE_CMD == pdu->getOpcode() ) {
1151 {
1152 const AttWriteCmd &p = *static_cast<const AttWriteCmd*>(pdu);
1153 const jau::TOctetSlice &p_value = p.getValue();
1155 jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
1156 sections.emplace_back( 0, (uint16_t)p_value.size() );
1157 fwd_gh->notifyNativeWriteRequest(p.getHandle(), p_val, sections, false /* with_response */, clientSource);
1158 }
1159 const bool res = fwd_gh->send(*pdu);
1160 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: WRITE.21: res %d, %s to %s", res, pdu->toString().c_str(), fwd_gh->toString().c_str());
1161 return res;
1162 } else {
1163 // Actually an internal error, method should not have been called
1164 AttErrorRsp err(AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST, pdu->getOpcode(), 0);
1165 WARN_PRINT("GATT-Req: WRITE.20: %s -> %s from %s", pdu->toString().c_str(), err.toString().c_str(), gh.toString().c_str());
1166 fwd_gh->notifyNativeReplyReceived(err, clientSource);
1167 {
1168 fwd_gh->notifyNativeWriteResponse(err, err.getErrorCode(), clientSource);
1169 }
1170 return gh.send(err);
1171 }
1172 }
1173
1174 bool replyReadReq(const AttPDUMsg * pdu) noexcept override {
1175 if( !fwd_gh->isConnected() ) {
1176 close();
1177 return false;
1178 }
1179 BTDeviceRef clientSource = gh.getDeviceUnchecked();
1180 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1181 uint16_t handle;
1182 uint16_t value_offset;
1183 {
1184 if( AttPDUMsg::Opcode::READ_REQ == pdu->getOpcode() ) {
1185 const AttReadReq * req = static_cast<const AttReadReq*>(pdu);
1186 handle = req->getHandle();
1187 value_offset = 0;
1188 } else if( AttPDUMsg::Opcode::READ_BLOB_REQ == pdu->getOpcode() ) {
1189 /**
1190 * BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value
1191 *
1192 * If the Characteristic Value is not longer than (ATT_MTU – 1)
1193 * an ATT_ERROR_RSP PDU with the error
1194 * code set to Attribute Not Long shall be received on the first
1195 * ATT_READ_BLOB_REQ PDU.
1196 */
1197 const AttReadBlobReq * req = static_cast<const AttReadBlobReq*>(pdu);
1198 handle = req->getHandle();
1199 value_offset = req->getValueOffset();
1200 } else {
1201 // Internal error
1202 handle = 0;
1203 value_offset = 0;
1204 }
1205 }
1206 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
1207 if( nullptr == rsp ) {
1208 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1209 return false;
1210 }
1211 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: READ: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1212 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1213 {
1214 if( AttPDUMsg::Opcode::READ_RSP == rsp->getOpcode() ||
1215 AttPDUMsg::Opcode::READ_BLOB_RSP == rsp->getOpcode() ) {
1216 const AttReadNRsp * p = static_cast<const AttReadNRsp*>(rsp.get());
1217 const jau::TOctetSlice& p_value = p->getValue();
1218 jau::TROOctets p_val(p_value.get_ptr_nc(0), p_value.size(), p_value.byte_order());
1219 fwd_gh->notifyNativeReadResponse(handle, value_offset, *rsp, AttErrorRsp::ErrorCode::NO_ERROR, p_val, clientSource);
1220 } else {
1221 const AttErrorRsp::ErrorCode error_code = AttPDUMsg::Opcode::ERROR_RSP == rsp->getOpcode() ?
1222 static_cast<const AttErrorRsp*>(rsp.get())->getErrorCode() : AttErrorRsp::ErrorCode::NO_ERROR;
1223 jau::TROOctets p_val;
1224 fwd_gh->notifyNativeReadResponse(handle, value_offset, *rsp, error_code, p_val, clientSource);
1225 }
1226 }
1227 return gh.send(*rsp);
1228 }
1229
1230 bool replyFindInfoReq(const AttFindInfoReq * pdu) noexcept override {
1231 if( !fwd_gh->isConnected() ) {
1232 close();
1233 return false;
1234 }
1235 BTDeviceRef clientSource = gh.getDeviceUnchecked();
1236 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1237 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
1238 if( nullptr == rsp ) {
1239 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1240 return false;
1241 }
1242 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: INFO: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1243 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1244 return gh.send(*rsp);
1245 }
1246
1247 bool replyFindByTypeValueReq(const AttFindByTypeValueReq * pdu) noexcept override {
1248 if( !fwd_gh->isConnected() ) {
1249 close();
1250 return false;
1251 }
1252 BTDeviceRef clientSource = gh.getDeviceUnchecked();
1253 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1254 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
1255 if( nullptr == rsp ) {
1256 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1257 return false;
1258 }
1259 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPEVALUE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1260 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1261 return gh.send(*rsp);
1262 }
1263
1264 bool replyReadByTypeReq(const AttReadByNTypeReq * pdu) noexcept override {
1265 if( !fwd_gh->isConnected() ) {
1266 close();
1267 return false;
1268 }
1269 BTDeviceRef clientSource = gh.getDeviceUnchecked();
1270 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1271 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
1272 if( nullptr == rsp ) {
1273 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1274 return false;
1275 }
1276 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: TYPE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1277 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1278 return gh.send(*rsp);
1279 }
1280
1281 bool replyReadByGroupTypeReq(const AttReadByNTypeReq * pdu) noexcept override {
1282 if( !fwd_gh->isConnected() ) {
1283 close();
1284 return false;
1285 }
1286 BTDeviceRef clientSource = gh.getDeviceUnchecked();
1287 fwd_gh->notifyNativeRequestSent(*pdu, clientSource);
1288 std::unique_ptr<const AttPDUMsg> rsp = fwd_gh->sendWithReply(*pdu, gh.read_cmd_reply_timeout); // valid reply or exception
1289 if( nullptr == rsp ) {
1290 ERR_PRINT2("No reply; req %s from %s", pdu->toString().c_str(), fwd_gh->toString().c_str());
1291 return false;
1292 }
1293 COND_PRINT(gh.env.DEBUG_DATA, "GATT-Req: GROUP_TYPE: %s -> %s from %s", pdu->toString().c_str(), rsp->toString().c_str(), fwd_gh->toString().c_str());
1294 fwd_gh->notifyNativeReplyReceived(*rsp, clientSource);
1295 return gh.send(*rsp);
1296 }
1297};
1298
1299std::unique_ptr<BTGattHandler::GattServerHandler> BTGattHandler::selectGattServerHandler(BTGattHandler& gh, const DBGattServerRef& gattServerData) noexcept {
1300 if( nullptr != gattServerData ) {
1301 switch( gattServerData->getMode() ) {
1302 case DBGattServer::Mode::DB: {
1303 if( gattServerData->getServices().size() > 0 ) {
1304 return std::make_unique<DBGattServerHandler>(gh, gattServerData);
1305 }
1306 [[fallthrough]];
1307 }
1308 case DBGattServer::Mode::FWD: {
1309 BTDeviceRef fwdServer = gattServerData->getFwdServer();
1310 if( nullptr != fwdServer ) {
1311 return std::make_unique<FwdGattServerHandler>(gh, fwdServer);
1312 }
1313 [[fallthrough]];
1314 }
1315 default:
1316 break;
1317 }
1318 }
1319 return std::make_unique<NopGattServerHandler>();
1320}
1321
bool replyFindInfoReq(const AttFindInfoReq *pdu) noexcept override
Reply to a find info request.
DBGattServer::Mode getMode() noexcept override
bool replyWriteReq(const AttPDUMsg *pdu) noexcept override
Reply to a write request.
bool replyExchangeMTUReq(const AttExchangeMTU *pdu) noexcept override
Reply to an exchange MTU request.
bool replyFindByTypeValueReq(const AttFindByTypeValueReq *pdu) noexcept override
Reply to a find by type value request.
bool replyReadByTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by type request.
bool replyReadByGroupTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by group type request.
void close() noexcept override
Close and clear this handler, i.e.
bool replyReadReq(const AttPDUMsg *pdu) noexcept override
Reply to a read request.
DBGattServerHandler(BTGattHandler &gh_, DBGattServerRef gsd) noexcept
bool replyExchangeMTUReq(const AttExchangeMTU *pdu) noexcept override
Reply to an exchange MTU request.
bool replyReadByTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by type request.
bool replyWriteReq(const AttPDUMsg *pdu) noexcept override
Reply to a write request.
bool replyReadReq(const AttPDUMsg *pdu) noexcept override
Reply to a read request.
bool replyFindInfoReq(const AttFindInfoReq *pdu) noexcept override
Reply to a find info request.
FwdGattServerHandler(BTGattHandler &gh_, BTDeviceRef fwdServer_) noexcept
bool replyFindByTypeValueReq(const AttFindByTypeValueReq *pdu) noexcept override
Reply to a find by type value request.
bool replyReadByGroupTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by group type request.
void close() noexcept override
Close and clear this handler, i.e.
DBGattServer::Mode getMode() noexcept override
bool replyFindByTypeValueReq(const AttFindByTypeValueReq *pdu) noexcept override
Reply to a find by type value request.
bool replyExchangeMTUReq(const AttExchangeMTU *pdu) noexcept override
Reply to an exchange MTU request.
void close() noexcept override
Close and clear this handler, i.e.
bool replyFindInfoReq(const AttFindInfoReq *pdu) noexcept override
Reply to a find info request.
bool replyReadByTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by type request.
bool replyReadReq(const AttPDUMsg *pdu) noexcept override
Reply to a read request.
bool replyWriteReq(const AttPDUMsg *pdu) noexcept override
Reply to a write request.
NopGattServerHandler() noexcept=default
bool replyReadByGroupTypeReq(const AttReadByNTypeReq *pdu) noexcept override
Reply to a read by group type request.
DBGattServer::Mode getMode() noexcept override
jau::nsize_t getElementPDUOffset(const jau::nsize_t elementIdx) const
void setElementCount(const jau::nsize_t count)
Fixate element count.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.1.1 ATT_ERROR_RSP.
constexpr ErrorCode getErrorCode() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.2.1 ATT_EXCHANGE_MTU_REQ BT Core Spec v5....
constexpr uint16_t getMTUSize() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.6.3 ATT_EXECUTE_WRITE_REQ.
constexpr uint8_t getFlags() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.6.4 ATT_EXECUTE_WRITE_RSP.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.3 ATT_FIND_BY_TYPE_VALUE_REQ.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.4 ATT_FIND_BY_TYPE_VALUE_RSP.
void setElementHandles(const jau::nsize_t elementIdx, const uint16_t handle, const uint16_t handle_end)
void setElementCount(const jau::nsize_t count)
Fixate element count.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.1 ATT_FIND_INFORMATION_REQ.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.3.2 ATT_FIND_INFORMATION_RSP.
void setElementSize(const uint8_t element_length) override
Fixate element length.
void setElementHandle(const jau::nsize_t elementIdx, const uint16_t h)
void setElementValueUUID(const jau::nsize_t elementIdx, const jau::uuid_t &v)
Handles the Attribute Protocol (ATT) using Protocol Data Unit (PDU) encoded messages over L2CAP chann...
jau::POctets pdu
actual received PDU
virtual std::string toString() const noexcept
constexpr_cxx20 jau::nsize_t getPDUValueSize() const noexcept
Returns the net octet size of this PDU's attributes value, i.e.
constexpr Opcode getOpcode() const noexcept
ATT PDU Format Vol 3, Part F 3.3.1.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.6.1 ATT_PREPARE_WRITE_REQ BT Core Spec v5....
constexpr uint16_t getValueOffset() const noexcept
constexpr jau::TOctetSlice const & getValue() const noexcept
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.5 ATT_BLOB_READ_REQ.
constexpr uint16_t getHandle() const noexcept
constexpr uint16_t getValueOffset() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.10 ATT_READ_BY_GROUP_TYPE_RSP.
void setElementSize(const uint8_t element_length) override
Fixate element length.
void setElementEndHandle(const jau::nsize_t elementIdx, const uint16_t h)
void setElementValueUUID(const jau::nsize_t elementIdx, const jau::uuid_t &v)
void setElementStartHandle(const jau::nsize_t elementIdx, const uint16_t h)
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.1 ATT_READ_BY_TYPE_REQ.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.2 ATT_READ_BY_TYPE_RSP.
void setElementHandle(const jau::nsize_t elementIdx, const uint16_t h)
void setElementSize(const uint8_t element_length) override
Fixate element length.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.4 ATT_READ_RSP BT Core Spec v5.2: Vol 3,...
constexpr jau::TOctetSlice const & getValue() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.3 ATT_READ_REQ.
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.3 ATT_WRITE_CMD.
constexpr jau::TOctetSlice const & getValue() const noexcept
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.1 ATT_WRITE_REQ.
constexpr jau::TOctetSlice const & getValue() const noexcept
constexpr uint16_t getHandle() const noexcept
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.2 ATT_WRITE_RSP.
const bool DEBUG_DATA
Debug all GATT Data communication.
Internal handler implementation for given DBGattServer instance matching its DBGattServer::Mode.
A thread safe GATT handler associated to one device via one L2CAP connection.
bool send(const AttPDUMsg &msg) noexcept
Sends the given AttPDUMsg to the connected device via l2cap.
BTDeviceRef getDeviceUnchecked() const noexcept
uint16_t getUsedMTU() const noexcept
std::string toString() const noexcept
void setUsedMTU(const uint16_t mtu) noexcept
uint16_t getServerMTU() const noexcept
const BTGattEnv & env
Environment runtime configuration, usually used internally only.
const jau::fraction_i64 read_cmd_reply_timeout
Derived environment runtime configuration, usually used internally only.
const jau::fraction_i64 write_cmd_reply_timeout
Derived environment runtime configuration, usually used internally only.
std::shared_ptr< Listener > ListenerRef
Mode
Operating mode of a DBGattServer instance.
const std::string & message() const noexcept
Persistent endian aware octet data, i.e.
Definition: octets.hpp:560
POctets & resize(const nsize_t newCapacity, const nsize_t newSize)
Resizes this instance, including its capacity.
Definition: octets.hpp:839
POctets & recapacity(const nsize_t newCapacity)
Changes the capacity.
Definition: octets.hpp:879
constexpr nsize_t capacity() const noexcept
Returns the memory capacity, never zero, greater or equal size().
Definition: octets.hpp:591
Transient endian aware octet data slice, i.e.
Definition: octets.hpp:495
constexpr lb_endian_t byte_order() const noexcept
Returns byte order of this octet store.
Definition: octets.hpp:519
constexpr nsize_t size() const noexcept
Definition: octets.hpp:521
constexpr uint8_t const * get_ptr_nc(const nsize_t i) const noexcept
Definition: octets.hpp:542
void put_octets_nc(const nsize_t i, const TROOctets &v) noexcept
Definition: octets.hpp:399
constexpr void put_uint16_nc(const nsize_t i, const uint16_t v) noexcept
Definition: octets.hpp:343
constexpr void put_uint8_nc(const nsize_t i, const uint8_t v) noexcept
Definition: octets.hpp:335
void put_bytes(const nsize_t i, const uint8_t *source, const nsize_t byte_count)
Definition: octets.hpp:412
uint8_t * get_wptr_nc(const nsize_t i) noexcept
Definition: octets.hpp:480
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
constexpr nsize_t size() const noexcept
Returns the used memory size for read and write operations, may be zero.
Definition: octets.hpp:162
constexpr uint8_t const * get_ptr() const noexcept
Definition: octets.hpp:272
constexpr iterator end() noexcept
Definition: darray.hpp:707
constexpr const_iterator cend() const noexcept
Definition: darray.hpp:711
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:763
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: darray.hpp:1124
constexpr reference emplace_back(Args &&... args)
Like std::vector::emplace_back(), construct a new element in place at the end().
Definition: darray.hpp:1171
constexpr const_iterator cbegin() const noexcept
Definition: darray.hpp:705
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: darray.hpp:904
bool equivalent(uuid_t const &o) const noexcept
Relaxed equality operator.
Definition: uuid.cpp:109
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
Definition: debug.hpp:151
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:112
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
Definition: debug.hpp:123
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
constexpr InputIt find_if(InputIt first, InputIt last, UnaryPredicate p)
Like std::find_if() of 'algorithm'.
lb_endian_t
Simplified reduced endian type only covering little- and big-endian.
Definition: byte_util.hpp:227
@ little
Identifier for little endian, equivalent to endian::little.
std::shared_ptr< BTDevice > BTDeviceRef
Definition: BTDevice.hpp:1347
@ INCLUDE_DECLARATION
Definition: GattNumbers.hpp:69
@ SECONDARY_SERVICE
Definition: GattNumbers.hpp:66
std::shared_ptr< DBGattDesc > DBGattDescRef
std::shared_ptr< DBGattService > DBGattServiceRef
std::shared_ptr< DBGattServer > DBGattServerRef
std::shared_ptr< DBGattChar > DBGattCharRef
constexpr T min(const T x, const T y) noexcept
Returns the minimum of two integrals (w/ branching) in O(1)
Definition: base_math.hpp:177
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
@ Notify
@ Indicate