36 #include <sys/socket.h>
50#include "L2CAPIoctl.hpp"
68BTGattEnv::BTGattEnv() noexcept
69: exploding(
jau::environment::getExplodingProperties("
direct_bt.gatt") ),
70 GATT_READ_COMMAND_REPLY_TIMEOUT(
jau::environment::getFractionProperty("
direct_bt.gatt.cmd.read.timeout", 550_ms, 550_ms , 365_d ) ),
71 GATT_WRITE_COMMAND_REPLY_TIMEOUT(
jau::environment::getFractionProperty("
direct_bt.gatt.cmd.write.timeout", 550_ms, 550_ms , 365_d ) ),
72 GATT_INITIAL_COMMAND_REPLY_TIMEOUT(
jau::environment::getFractionProperty("
direct_bt.gatt.cmd.init.timeout", 2500_ms, 2000_ms , 365_d ) ),
73 ATTPDU_RING_CAPACITY(
jau::environment::getInt32Property("
direct_bt.gatt.ringsize", 128, 64 , 1024 ) ),
74 DEBUG_DATA(
jau::environment::getBooleanProperty("
direct_bt.debug.gatt.data", false) )
80 if(
nullptr == ref ) {
86bool BTGattHandler::validateConnected() noexcept {
87 const bool l2capIsConnected = l2cap.
is_open();
88 const bool l2capHasIOError = l2cap.
hasIOError();
90 if( has_ioerror || l2capHasIOError ) {
91 DBG_PRINT(
"ioerr state: GattHandler %s, l2cap %s: %s",
97 if( !is_connected || !l2capIsConnected ) {
98 DBG_PRINT(
"Disconnected state: GattHandler %s, l2cap %s: %s",
106 [](
const GattCharListenerPair& a,
const GattCharListenerPair& b) ->
bool {
return *a.listener == *b.listener; };
110 ERR_PRINT(
"GATTCharacteristicListener ref is null");
113 return gattCharListenerList.push_back_unique(GattCharListenerPair{l, std::weak_ptr<BTGattChar>{} },
114 gattCharListenerRefEqComparator);
119 ERR_PRINT(
"GATTCharacteristicListener ref is null");
126 return gattCharListenerList.push_back_unique(GattCharListenerPair{l, d},
127 gattCharListenerRefEqComparator);
132 ERR_PRINT(
"GATTCharacteristicListener ref is null");
135 const size_type count = gattCharListenerList.erase_matching(GattCharListenerPair{l, std::weak_ptr<BTGattChar>{}},
137 gattCharListenerRefEqComparator);
143 ERR_PRINT(
"GATTCharacteristicListener ref is null");
146 auto it = gattCharListenerList.begin();
147 for (; !it.is_end(); ++it ) {
148 if ( *it->listener == *l ) {
162 ERR_PRINT(
"NativeGattCharListener ref is null");
170 ERR_PRINT(
"NativeGattCharListener ref is null");
181 auto it = gattCharListenerList.
begin();
182 for (; !it.is_end(); ++it, ++i ) {
186 jau::INFO_PRINT(
"BTGattHandler: NativeGattChar %u listener", nativeGattCharListenerList.
size());
189 auto it = nativeGattCharListenerList.
begin();
190 for (; !it.is_end(); ++it, ++i ) {
197 if(
nullptr == associatedCharacteristic ) {
198 ERR_PRINT(
"Given GATTCharacteristic ref is null");
201 return removeAllAssociatedCharListener( associatedCharacteristic.get() );
205 if(
nullptr == associatedCharacteristic ) {
206 ERR_PRINT(
"Given GATTCharacteristic ref is null");
210 auto it = gattCharListenerList.begin();
211 while( !it.is_end() ) {
212 if ( it->match(*associatedCharacteristic) ) {
227 gattCharListenerList.
clear();
228 count += nativeGattCharListenerList.
size();
229 nativeGattCharListenerList.
clear();
235 if(
nullptr != serverDest ) {
237 jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
239 l->requestSent(pduRequest, serverDest, clientSource);
240 }
catch (std::exception &e) {
241 ERR_PRINT(
"GATTHandler::requestSent-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
242 i+1, nativeGattCharListenerList.size(),
252 if(
nullptr != serverSource ) {
254 jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
256 l->replyReceived(pduReply, serverSource, clientDest);
257 }
catch (std::exception &e) {
258 ERR_PRINT(
"GATTHandler::replyReceived-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
259 i+1, nativeGattCharListenerList.size(),
269 const uint16_t serverMTU_,
const uint16_t usedMTU_,
273 if(
nullptr != serverReplier ) {
275 jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
277 l->mtuResponse(clientMTU_, pduReply, error_reply, serverMTU_, usedMTU_, serverReplier, clientRequester);
278 }
catch (std::exception &e) {
279 ERR_PRINT(
"GATTHandler::mtuResponse-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
280 i+1, nativeGattCharListenerList.size(),
289 const bool with_response,
const BTDeviceRef& clientSource)
noexcept
292 if(
nullptr != serverDest ) {
294 jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
296 l->writeRequest(handle, data, sections, with_response, serverDest, clientSource);
297 }
catch (std::exception &e) {
298 ERR_PRINT(
"GATTHandler::writeRequest-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
299 i+1, nativeGattCharListenerList.size(),
309 if(
nullptr != serverSource ) {
311 jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
313 l->writeResponse(pduReply, error_code, serverSource, clientDest);
314 }
catch (std::exception &e) {
315 ERR_PRINT(
"GATTHandler::writeResponse-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
316 i+1, nativeGattCharListenerList.size(),
328 if(
nullptr != serverReplier ) {
330 jau::for_each_fidelity(nativeGattCharListenerList, [&](std::shared_ptr<BTGattHandler::NativeGattCharListener> &l) {
332 l->readResponse(handle, value_offset, pduReply, error_code, data_reply, serverReplier, clientRequester);
333 }
catch (std::exception &e) {
334 ERR_PRINT(
"GATTHandler::readResponse-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
335 i+1, nativeGattCharListenerList.size(),
344 sendIndicationConfirmation = v;
348 return sendIndicationConfirmation;
351bool BTGattHandler::replyAttPDUReq(std::unique_ptr<const AttPDUMsg> && pdu)
noexcept {
352 if( !validateConnected() ) {
353 DBG_PRINT(
"GATT-Req: disconnected: req %s from %s",
354 pdu->toString().c_str(), toString().c_str());
357 switch( pdu->getOpcode() ) {
359 return gattServerHandler->replyExchangeMTUReq(
static_cast<const AttExchangeMTU*
>( pdu.get() ) );
363 return gattServerHandler->replyFindInfoReq(
static_cast<const AttFindInfoReq*
>( pdu.get() ) );
367 return gattServerHandler->replyFindByTypeValueReq(
static_cast<const AttFindByTypeValueReq*
>( pdu.get() ) );
371 return gattServerHandler->replyReadByTypeReq(
static_cast<const AttReadByNTypeReq*
>( pdu.get() ) );
377 return gattServerHandler->replyReadReq( pdu.get() );
381 return gattServerHandler->replyReadByGroupTypeReq(
static_cast<const AttReadByNTypeReq*
>( pdu.get() ) );
391 return gattServerHandler->replyWriteReq( pdu.get() );
402 WARN_PRINT(
"GATT Req: Ignored: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), toString().c_str());
404 ERR_PRINT2(
"l2cap send: Error req %s; %s", rsp.toString().c_str(), toString().c_str());
412 ERR_PRINT(
"GATT Req: Unhandled: %s -> %s from %s", pdu->toString().c_str(), rsp.toString().c_str(), toString().c_str());
414 ERR_PRINT2(
"l2cap send: Error req %s; %s", rsp.toString().c_str(), toString().c_str());
423 if( !validateConnected() ) {
424 DBG_PRINT(
"GATTHandler::reader: Invalid IO state -> Stop");
429 len = l2cap.read(rbuffer.get_wptr(), rbuffer.size());
432 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::reader: Got %s", attPDU->toString().c_str());
439 ERR_PRINT(
"MULTI-NTF not implemented: %s", attPDU->toString().c_str());
442 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::reader: NTF: %s, listener [native %zd, bt %zd]",
443 a->
toString().c_str(), nativeGattCharListenerList.size(), gattCharListenerList.size());
445 const uint16_t a_handle = a->
getHandle();
449 if(
nullptr != device ) {
453 l->notificationReceived(device, a_handle, a_data_view, a_timestamp);
454 }
catch (std::exception &e) {
455 ERR_PRINT(
"GATTHandler::notificationReceived-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
456 i+1, nativeGattCharListenerList.size(),
462 BTGattCharRef characteristic = findCharacterisicsByValueHandle(services, a_handle);
463 if(
nullptr != characteristic ) {
467 if( p.match(*characteristic) ) {
468 p.listener->notificationReceived(characteristic, a_data_view, a_timestamp);
470 }
catch (std::exception &e) {
471 ERR_PRINT(
"GATTHandler::notificationReceived-CBs %d/%zd: BTGattCharListener %s: Caught exception %s",
472 i+1, gattCharListenerList.size(),
480 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::reader: IND: %s, sendIndicationConfirmation %d, listener [native %zd, bt %zd]",
481 a->
toString().c_str(), sendIndicationConfirmation.load(), nativeGattCharListenerList.size(), gattCharListenerList.size());
482 bool cfmSent =
false;
483 if( sendIndicationConfirmation ) {
486 ERR_PRINT2(
"Indication Confirmation: Error req %s; %s", cfm.
toString().c_str(), toString().c_str());
494 const uint16_t a_handle = a->
getHandle();
498 if(
nullptr != device ) {
502 l->indicationReceived(device, a_handle, a_data_view, a_timestamp, cfmSent);
503 }
catch (std::exception &e) {
504 ERR_PRINT(
"GATTHandler::indicationReceived-CBs %d/%zd: NativeGattCharListener %s: Caught exception %s",
505 i+1, nativeGattCharListenerList.size(),
511 BTGattCharRef characteristic = findCharacterisicsByValueHandle(services, a_handle);
512 if(
nullptr != characteristic ) {
516 if( p.match(*characteristic) ) {
517 p.listener->indicationReceived(characteristic, a_data_view, a_timestamp, cfmSent);
519 }
catch (std::exception &e) {
520 ERR_PRINT(
"GATTHandler::indicationReceived-CBs %d/%zd: BTGattCharListener %s, cfmSent %d: Caught exception %s",
521 i+1, gattCharListenerList.size(),
528 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::reader: Ring: %s", attPDU->toString().c_str());
529 if( !attPDURing.putBlocking( std::move(attPDU), 0_s ) ) {
530 ERR_PRINT2(
"attPDURing put: %s", attPDURing.toString().c_str());
535 if( !replyAttPDUReq( std::move( attPDU ) ) ) {
536 ERR_PRINT2(
"ATT Reply: %s", toString().c_str());
542 ERR_PRINT(
"Unhandled: %s", attPDU->toString().c_str());
545 WORDY_PRINT(
"GATTHandler::reader: l2cap read: IRQed res %d (%s); %s",
547 if( !sr.shall_stop() ) {
554 IRQ_PRINT(
"GATTHandler::reader: l2cap read: Error res %d (%s); %s",
559 WORDY_PRINT(
"GATTHandler::reader: l2cap read: Zero res %d (%s); %s",
567 WORDY_PRINT(
"GATTHandler::reader: EndLocked. Ring has %u entries flushed: %s", attPDURing.size(), toString().c_str());
574 if(
nullptr != device ) {
582bool BTGattHandler::l2capReaderInterrupted(
int dummy)
noexcept {
584 if( l2cap_reader_service.shall_stop() || !is_connected ) {
588 if(
nullptr == device ) {
591 return !device->getConnected();
595: supervision_timeout(supervision_timeout_),
597 read_cmd_reply_timeout(
jau::max(env.GATT_READ_COMMAND_REPLY_TIMEOUT, 1_ms*(supervision_timeout+50_i64))),
598 write_cmd_reply_timeout(
jau::max(env.GATT_WRITE_COMMAND_REPLY_TIMEOUT, 1_ms*(supervision_timeout+50_i64))),
600 role(device->getLocalGATTRole()),
602 deviceString(device->getAddressAndType().address.toString()),
604 is_connected(l2cap.is_open()), has_ioerror(
false),
609 attPDURing(env.ATTPDU_RING_CAPACITY),
610 serverMTU(
number(Defaults::MIN_ATT_MTU)), usedMTU(
number(Defaults::MIN_ATT_MTU)), clientMTUExchanged(
false),
611 gattServerData( device->getAdapter().getGATTServerData() ),
612 gattServerHandler( selectGattServerHandler(*
this, gattServerData) )
614 if( !validateConnected() ) {
616 is_connected =
false;
625 l2cap.set_interrupted_query(
jau::bind_member(
this, &BTGattHandler::l2capReaderInterrupted) );
626 l2cap_reader_service.start();
628 DBG_PRINT(
"GATTHandler::ctor: Started: GattHandler[%s], l2cap[%s]: %s",
629 getStateString().c_str(), l2cap.getStateString().c_str(), toString().c_str());
633 serverMTU =
number(Defaults::MAX_ATT_MTU);
634 usedMTU =
number(Defaults::MIN_ATT_MTU);
637 if(
nullptr != gattServerData ) {
640 serverMTU =
number(Defaults::MAX_ATT_MTU);
642 usedMTU =
number(Defaults::MIN_ATT_MTU);
644 if(
nullptr != gattServerData ) {
648 l->connected(device, usedMTU);
649 } catch (std::exception &e) {
650 ERR_PRINT(
"GATTHandler::connected: %d/%zd: %s: Caught exception %s",
651 i+1, gattServerData->listener().size(),
652 toString().c_str(), e.what());
663 gattCharListenerList.
clear();
664 nativeGattCharListenerList.
clear();
666 genericAccess =
nullptr;
676 if(
nullptr == device ) {
685 if( !is_connected.compare_exchange_strong(expConn,
false) ) {
687 const bool l2cap_service_stopped = l2cap_reader_service.join();
689 DBG_PRINT(
"GATTHandler::disconnect: Not connected: disconnect_device %d, ioerr %d: GattHandler[%s], l2cap[%s], stopped %d: %s",
690 disconnect_device, ioerr_cause, getStateString().c_str(), l2cap.getStateString().c_str(),
691 l2cap_service_stopped, toString().c_str());
692 gattCharListenerList.clear();
693 nativeGattCharListenerList.clear();
698 const bool l2cap_service_stop_res = l2cap_reader_service.stop();
702 gattServerHandler->close();
705 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
706 DBG_PRINT(
"GATTHandler::disconnect: Start: disconnect_device %d, ioerr %d: GattHandler[%s], l2cap[%s]: %s",
707 disconnect_device, ioerr_cause, getStateString().c_str(), l2cap.getStateString().c_str(), toString().c_str());
708 gattCharListenerList.clear();
709 nativeGattCharListenerList.clear();
711 clientMTUExchanged =
false;
713 DBG_PRINT(
"GATTHandler::disconnect: End: stopped %d, disconnect_device %d, %s",
714 l2cap_service_stop_res, disconnect_device, toString().c_str());
716 if( disconnect_device ) {
722 device->disconnect(reason);
728 if( !validateConnected() ) {
729 if( !l2capReaderInterrupted() ) {
730 ERR_PRINT(
"Invalid IO State: req %s to %s", msg.toString().c_str(), toString().c_str());
735 if( msg.pdu.size() > usedMTU ) {
736 ERR_PRINT(
"Msg PDU size %zu >= used MTU %u, req %s to $s",
737 msg.pdu.size(), usedMTU.load(), msg.toString().c_str(), toString().c_str());
742 const jau::snsize_t len = l2cap.write(msg.pdu.get_ptr(), msg.pdu.size());
745 WORDY_PRINT(
"GATTHandler::reader: l2cap read: IRQed res %d (%s); %s",
748 ERR_PRINT(
"l2cap write: Error res %d (%s); %s; %s -> disconnect: %s",
750 msg.toString().c_str(), toString().c_str());
752 disconnect(
true ,
true );
756 if(
static_cast<size_t>(len) != msg.pdu.size() ) {
757 ERR_PRINT(
"l2cap write: Error: Message size has %d != exp %zu: %s -> disconnect: %s",
758 len, msg.pdu.size(), msg.toString().c_str(), toString().c_str());
760 disconnect(
true ,
true );
772 std::unique_ptr<const AttPDUMsg> res;
773 if( !attPDURing.getBlocking(res, timeout) ||
nullptr == res ) {
775 ERR_PRINT(
"GATTHandler::sendWithReply: nullptr result (timeout %" PRIi64
" ms): req %s to %s", timeout.to_ms(), msg.toString().c_str(), toString().c_str());
777 disconnect(
true ,
true );
783uint16_t BTGattHandler::clientMTUExchange(
const jau::fraction_i64& timeout)
noexcept {
785 ERR_PRINT(
"GATT MTU exchange only allowed in client mode");
792 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
796 DBG_PRINT(
"GATT MTU-REQ send: %s to %s", req.toString().c_str(), toString().c_str());
798 std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, timeout);
800 if(
nullptr == pdu ) {
801 ERR_PRINT2(
"No reply; req %s from %s", req.toString().c_str(), toString().c_str());
805 DBG_PRINT(
"GATT MTU-RSP recv: %u, %s from %s", mtu, pdu->toString().c_str(), toString().c_str());
814 mtu =
number(Defaults::MIN_ATT_MTU);
815 DBG_PRINT(
"GATT MTU handled error -> ATT_MTU %u, %s from %s", mtu, pdu->toString().c_str(), toString().c_str());
817 WORDY_PRINT(
"GATT MTU unexpected error %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), toString().c_str());
820 ERR_PRINT(
"GATT MTU unexpected reply %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), toString().c_str());
827DBGattCharRef BTGattHandler::findServerGattCharByValueHandle(
const uint16_t char_value_handle)
noexcept {
828 if(
nullptr != gattServerData ) {
829 return gattServerData->findGattCharByValueHandle(char_value_handle);
841 nullptr == findServerGattCharByValueHandle(char_value_handle) )
846 if( 0 == value.size() ) {
847 COND_PRINT(env.DEBUG_DATA,
"GATT SEND NTF: Zero size, skipped sending to %s", toString().c_str());
850 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
852 COND_PRINT(env.DEBUG_DATA,
"GATT SEND NTF: %s to %s", data.
toString().c_str(), toString().c_str());
862 nullptr == findServerGattCharByValueHandle(char_value_handle) )
867 if( 0 == value.size() ) {
868 COND_PRINT(env.DEBUG_DATA,
"GATT SEND IND: Zero size, skipped sending to %s", toString().c_str());
871 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
873 std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, write_cmd_reply_timeout);
874 if(
nullptr == pdu ) {
879 COND_PRINT(env.DEBUG_DATA,
"GATT SENT IND: %s -> %s to/from %s",
880 req.
toString().c_str(), pdu->toString().c_str(), toString().c_str());
883 WARN_PRINT(
"GATT SENT IND: Failed, no CFM reply: %s -> %s to/from %s",
884 req.
toString().c_str(), pdu->toString().c_str(), toString().c_str());
890 for(
const auto & service : services_) {
891 BTGattCharRef decl = findCharacterisicsByValueHandle(service, charValueHandle);
892 if(
nullptr != decl ) {
900 for(
auto decl : service->characteristicList) {
901 if( charValueHandle == decl->value_handle ) {
909 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
910 already_init = clientMTUExchanged && services.size() > 0 &&
nullptr != genericAccess;
914 if( !isConnected() ) {
915 DBG_PRINT(
"GATTHandler::initClientGatt: Not connected: %s", toString().c_str());
918 if( !clientMTUExchanged) {
921 DBG_PRINT(
"GATTHandler::initClientGatt: Local GATT Client: MTU Exchange Start: %s", toString().c_str());
922 uint16_t mtu = clientMTUExchange(initial_command_reply_timeout);
924 ERR_PRINT2(
"Local GATT Client: Zero serverMTU -> disconnect: %s", toString().c_str());
925 disconnect(
true ,
false );
929 usedMTU =
std::min(
number(Defaults::MAX_ATT_MTU), serverMTU.load());
930 clientMTUExchanged =
true;
931 DBG_PRINT(
"GATTHandler::initClientGatt: Local GATT Client: MTU Exchanged: server %u -> used %u, %s", serverMTU.load(), usedMTU.load(), toString().c_str());
934 if( services.size() > 0 &&
nullptr != genericAccess ) {
941 DBG_PRINT(
"GATTHandler::initClientGatt: Local GATT Client: Service Discovery Start: %s", toString().c_str());
942 if( !discoverCompletePrimaryServices(shared_this) ) {
945 disconnect(
true ,
true );
948 if( services.size() == 0 ) {
951 disconnect(
true ,
false );
954 genericAccess = getGenericAccess(services);
955 if(
nullptr == genericAccess ) {
958 disconnect(
true ,
false );
961 DBG_PRINT(
"GATTHandler::initClientGatt: End: %zu services discovered: %s, %s",
962 services.size(), genericAccess->toString().c_str(), toString().c_str());
966bool BTGattHandler::discoverCompletePrimaryServices(
const std::shared_ptr<BTGattHandler>& shared_this)
noexcept {
967 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
968 if( !discoverPrimaryServices(shared_this, services) ) {
971 for(
auto primSrv : services) {
972 if( !discoverCharacteristics(primSrv) ) {
975 if( primSrv->characteristicList.size() > 0 ) {
976 if( !discoverDescriptors(primSrv) ) {
984bool BTGattHandler::discoverPrimaryServices(
const std::shared_ptr<BTGattHandler>& shared_this,
jau::darray<BTGattServiceRef> & result)
noexcept {
988 if( given_this !=
this ) {
989 ABORT(
"Given shared GATTHandler reference %s not matching this %s, %s",
1001 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1005 uint16_t startHandle=0x0001;
1009 COND_PRINT(env.DEBUG_DATA,
"GATT PRIM SRV discover send: %s to %s", req.toString().c_str(), toString().c_str());
1011 std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, read_cmd_reply_timeout);
1012 if(
nullptr == pdu ) {
1013 ERR_PRINT2(
"No reply; req %s from %s", req.toString().c_str(), toString().c_str());
1016 COND_PRINT(env.DEBUG_DATA,
"GATT PRIM SRV discover recv: %s on %s", pdu->toString().c_str(), toString().c_str());
1023 for(size_type i=0; i<count; ++i) {
1026 result.push_back( std::make_shared<BTGattService>( shared_this,
true,
1031 }
catch (
const std::bad_alloc &e) {
1032 ABORT(
"Error: bad_alloc: BTGattServiceRef allocation failed");
1035 COND_PRINT(env.DEBUG_DATA,
"GATT PRIM SRV discovered[%d/%d]: %s on %s", i,
1036 count, result.at(result.size()-1)->toString().c_str(), toString().c_str());
1039 if( startHandle < 0xffff ) {
1047 ERR_PRINT(
"GATT discoverPrimary unexpected reply %s, req %s from %s",
1048 pdu->toString().c_str(), req.toString().c_str(), toString().c_str());
1056bool BTGattHandler::discoverCharacteristics(
BTGattServiceRef & service)
noexcept {
1067 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1068 COND_PRINT(env.DEBUG_DATA,
"GATT discoverCharacteristics Service: %s on %s", service->toString().c_str(), toString().c_str());
1073 uint16_t handle=service->handle;
1074 service->characteristicList.clear();
1076 const AttReadByNTypeReq req(
false , handle, service->end_handle, characteristicTypeReq);
1077 COND_PRINT(env.DEBUG_DATA,
"GATT C discover send: %s to %s", req.toString().c_str(), toString().c_str());
1079 std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, read_cmd_reply_timeout);
1080 if(
nullptr == pdu ) {
1081 ERR_PRINT2(
"No reply; req %s from %s", req.toString().c_str(), toString().c_str());
1084 COND_PRINT(env.DEBUG_DATA,
"GATT C discover recv: %s from %s", pdu->toString().c_str(), toString().c_str());
1091 for(size_type e_iter=0; e_iter<e_count; ++e_iter) {
1096 service->characteristicList.push_back( std::make_shared<BTGattChar>(
1102 }
catch (
const std::bad_alloc &e) {
1103 ABORT(
"Error: bad_alloc: BTGattCharRef allocation failed");
1106 COND_PRINT(env.DEBUG_DATA,
"GATT C discovered[%d/%d]: char%s on %s", e_iter, e_count,
1107 service->characteristicList.at(service->characteristicList.size()-1)->toString().c_str(), toString().c_str());
1110 if( handle < service->end_handle ) {
1118 ERR_PRINT(
"GATT discoverCharacteristics unexpected reply %s, req %s within service%s from %s",
1119 pdu->toString().c_str(), req.toString().c_str(), service->toString().c_str(), toString().c_str());
1128bool BTGattHandler::discoverDescriptors(
BTGattServiceRef & service)
noexcept {
1135 COND_PRINT(env.DEBUG_DATA,
"GATT discoverDescriptors Service: %s on %s", service->toString().c_str(), toString().c_str());
1136 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1139 const size_type charCount = service->characteristicList.size();
1140 for(size_type charIter=0; charIter < charCount; ++charIter ) {
1141 BTGattCharRef charDecl = service->characteristicList[charIter];
1142 charDecl->clearDescriptors();
1143 COND_PRINT(env.DEBUG_DATA,
"GATT discoverDescriptors Characteristic[%d/%d]: %s on %s", charIter, charCount, charDecl->toString().c_str(), toString().c_str());
1145 uint16_t cd_handle_iter = charDecl->value_handle + 1;
1146 uint16_t cd_handle_end;
1147 if( charIter+1 < charCount ) {
1148 cd_handle_end = service->characteristicList.at(charIter+1)->handle - 1;
1150 cd_handle_end = service->end_handle;
1155 while( !done && cd_handle_iter <= cd_handle_end ) {
1157 COND_PRINT(env.DEBUG_DATA,
"GATT CD discover send: %s", req.toString().c_str());
1159 std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, read_cmd_reply_timeout);
1160 if(
nullptr == pdu ) {
1161 ERR_PRINT2(
"No reply; req %s from %s", req.toString().c_str(), toString().c_str());
1164 COND_PRINT(env.DEBUG_DATA,
"GATT CD discover recv: %s from ", pdu->toString().c_str(), toString().c_str());
1170 for(size_type e_iter=0; e_iter<e_count; ++e_iter) {
1174 std::unique_ptr<const jau::uuid_t> cd_uuid = p->
getElementValue(e_iter);
1176 std::shared_ptr<BTGattDesc> cd( std::make_shared<BTGattDesc>(charDecl, std::move(cd_uuid), cd_handle) );
1177 if( cd_handle <= charDecl->value_handle || cd_handle > cd_handle_end ) {
1178 ERR_PRINT(
"GATT discoverDescriptors CD handle %s not in range ]%s..%s]: descr%s within char%s on %s",
1181 cd->toString().c_str(), charDecl->toString().c_str(), toString().c_str());
1186 if( !readDescriptorValue(*cd, 0) ) {
1187 WORDY_PRINT(
"GATT discoverDescriptors readDescriptorValue failed: req %s, descr%s within char%s on %s",
1188 req.toString().c_str(), cd->toString().c_str(), charDecl->toString().c_str(), toString().c_str());
1192 if( cd->isClientCharConfig() ) {
1194 }
else if( cd->isUserDescription() ) {
1197 charDecl->descriptorList.push_back(cd);
1198 COND_PRINT(env.DEBUG_DATA,
"GATT CD discovered[%d/%d]: %s", e_iter, e_count, cd->toString().c_str());
1201 if( cd_handle_iter < cd_handle_end ) {
1209 ERR_PRINT(
"GATT discoverDescriptors unexpected reply %s; req %s within char%s from %s",
1210 pdu->toString().c_str(), req.toString().c_str(), charDecl->toString().c_str(), toString().c_str());
1220 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::readDescriptorValue expLen %zd, desc %s", (
size_t)expectedLength, desc.toString().c_str());
1221 const bool res = readValue(desc.handle, desc.value, expectedLength);
1223 WORDY_PRINT(
"GATT readDescriptorValue error on desc%s within char%s from %s",
1224 desc.toString().c_str(), desc.getGattCharChecked()->toString().c_str(), toString().c_str());
1230 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::readCharacteristicValue expLen %zd, decl %s", (
size_t)expectedLength, decl.toString().c_str());
1231 const bool res = readValue(decl.value_handle, resValue, expectedLength);
1233 WORDY_PRINT(
"GATT readCharacteristicValue error on char%s from %s", decl.toString().c_str(), toString().c_str());
1241 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1247 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::readValue expLen %zd, handle %s from %s", (
size_t)expectedLength,
jau::to_hexstring(handle).c_str(), toString().c_str());
1250 if( 0 < expectedLength && (
size_type)expectedLength <= offset ) {
1252 }
else if( 0 == expectedLength && 0 < offset ) {
1256 std::unique_ptr<const AttPDUMsg> pdu =
nullptr;
1262 pdu = sendWithReply(req, read_cmd_reply_timeout);
1263 if(
nullptr == pdu ) {
1268 COND_PRINT(env.DEBUG_DATA,
"GATT RV recv: %s from %s", pdu->toString().c_str(), toString().c_str());
1280 if( 0 == v.
size() ) {
1302 WORDY_PRINT(
"GATT readValue unexpected error %s; req %s from %s", pdu->toString().c_str(), req.
toString().c_str(), toString().c_str());
1306 ERR_PRINT(
"GATT readValue unexpected reply %s; req %s from %s", pdu->toString().c_str(), req.
toString().c_str(), toString().c_str());
1320 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::writeDesccriptorValue desc %s", cd.toString().c_str());
1321 const bool res = writeValue(cd.handle, cd.value,
true);
1323 WORDY_PRINT(
"GATT writeDescriptorValue error on desc%s within char%s from %s",
1324 cd.toString().c_str(), cd.getGattCharChecked()->toString().c_str(), toString().c_str());
1331 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::writeCharacteristicValue desc %s, value %s", c.toString().c_str(), value.toString().c_str());
1332 const bool res = writeValue(c.value_handle, value,
true);
1334 WORDY_PRINT(
"GATT writeCharacteristicValue error on char%s from %s", c.toString().c_str(), toString().c_str());
1341 COND_PRINT(env.DEBUG_DATA,
"GATT writeCharacteristicValueNoResp decl %s, value %s", c.toString().c_str(), value.toString().c_str());
1342 return writeValue(c.value_handle, value,
false);
1351 if( value.size() <= 0 ) {
1352 WARN_PRINT(
"GATT writeValue size <= 0, no-op: %s", value.toString().c_str());
1355 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1360 if( !withResponse ) {
1362 COND_PRINT(env.DEBUG_DATA,
"GATT WV send(resp %d): %s to %s", withResponse, req.
toString().c_str(), toString().c_str());
1364 const bool res = send( req );
1367 ERR_PRINT2(
"Send failed; req %s from %s", req.
toString().c_str(), toString().c_str());
1375 COND_PRINT(env.DEBUG_DATA,
"GATT WV send(resp %d): %s to %s", withResponse, req.
toString().c_str(), toString().c_str());
1378 std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, write_cmd_reply_timeout);
1379 if(
nullptr == pdu ) {
1383 COND_PRINT(env.DEBUG_DATA,
"GATT WV recv: %s from %s", pdu->toString().c_str(), toString().c_str());
1389 WORDY_PRINT(
"GATT writeValue unexpected error %s; req %s from %s", pdu->toString().c_str(), req.
toString().c_str(), toString().c_str());
1391 ERR_PRINT(
"GATT writeValue unexpected reply %s; req %s from %s", pdu->toString().c_str(), req.
toString().c_str(), toString().c_str());
1398 if( !cccd.isClientCharConfig() ) {
1399 ERR_PRINT(
"Not a ClientCharacteristicConfiguration: %s", cccd.toString().c_str());
1403 const uint16_t ccc_value = enableNotification | ( enableIndication << 1 );
1404 COND_PRINT(env.DEBUG_DATA,
"GATTHandler::configNotificationIndication decl %s, enableNotification %d, enableIndication %d",
1405 cccd.toString().c_str(), enableNotification, enableIndication);
1406 cccd.value.resize(2, 2);
1407 cccd.value.put_uint16_nc(0, ccc_value);
1408 return writeDescriptorValue(cccd);
1432 std::shared_ptr<GattGenericAccessSvc> res =
nullptr;
1434 std::string deviceName =
"";
1436 std::shared_ptr<GattPeriphalPreferredConnectionParameters> prefConnParam =
nullptr;
1438 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1440 for(
auto & i : genericAccessCharDeclList) {
1447 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1451 if( readCharacteristicValue(charDecl, value.
resize(0)) && value.
size() >= 2 ) {
1455 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1460 if( deviceName.size() > 0 ) {
1461 res = std::make_shared<GattGenericAccessSvc>(deviceName, appearance, prefConnParam);
1467 for(
auto & primService : primServices) {
1470 return getGenericAccess(primService->characteristicList);
1477 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1480 for(
size_t i=0; readOK && i<services.size(); i++) {
1484 for(
size_t j=0; readOK && j<genericAccessCharDeclList.
size(); j++) {
1485 const BTGattChar & charDecl = *genericAccessCharDeclList.
at(j);
1500 jau::INFO_PRINT(
"GATTHandler::pingGATT: No GENERIC_ACCESS Service with APPEARANCE Characteristic available -> disconnect");
1509 std::shared_ptr<GattDeviceInformationSvc> res =
nullptr;
1513 std::string modelNumber;
1514 std::string serialNumber;
1515 std::string firmwareRevision;
1516 std::string hardwareRevision;
1517 std::string softwareRevision;
1518 std::string manufacturer;
1520 std::shared_ptr<GattPnP_ID> pnpID =
nullptr;
1523 const std::lock_guard<std::recursive_mutex> lock(mtx_command);
1525 for(
auto & i : characteristicDeclList) {
1533 if( readCharacteristicValue(charDecl, systemID.
resize(0)) ) {
1537 if( readCharacteristicValue(charDecl, regulatoryCertDataList.
resize(0)) ) {
1541 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1545 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1549 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1553 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1557 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1561 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1565 if( readCharacteristicValue(charDecl, value.
resize(0)) ) {
1571 res = std::make_shared<GattDeviceInformationSvc>(systemID, modelNumber, serialNumber,
1572 firmwareRevision, hardwareRevision, softwareRevision,
1573 manufacturer, regulatoryCertDataList, pnpID);
1579 for(
auto & primService : primServices) {
1582 return getDeviceInformation(primService->characteristicList);
1590 ", mode "+
to_string(gattServerHandler->getMode())+
static const jau::uuid16_t _SERIAL_NUMBER_STRING(GattCharacteristicType::SERIAL_NUMBER_STRING)
static const jau::uuid16_t _FIRMWARE_REVISION_STRING(GattCharacteristicType::FIRMWARE_REVISION_STRING)
static const jau::uuid16_t _GENERIC_ACCESS(GattServiceType::GENERIC_ACCESS)
static const jau::uuid16_t _APPEARANCE(GattCharacteristicType::APPEARANCE)
static const jau::uuid16_t _HARDWARE_REVISION_STRING(GattCharacteristicType::HARDWARE_REVISION_STRING)
static const jau::uuid16_t _PNP_ID(GattCharacteristicType::PNP_ID)
static const jau::uuid16_t _DEVICE_INFORMATION(GattServiceType::DEVICE_INFORMATION)
static const jau::uuid16_t _DEVICE_NAME(GattCharacteristicType::DEVICE_NAME)
static const jau::uuid16_t _REGULATORY_CERT_DATA_LIST(GattCharacteristicType::REGULATORY_CERT_DATA_LIST)
static const jau::uuid16_t _MANUFACTURER_NAME_STRING(GattCharacteristicType::MANUFACTURER_NAME_STRING)
static const jau::uuid16_t _PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS(GattCharacteristicType::PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS)
static const jau::uuid16_t _SOFTWARE_REVISION_STRING(GattCharacteristicType::SOFTWARE_REVISION_STRING)
static const jau::uuid16_t _SYSTEM_ID(GattCharacteristicType::SYSTEM_ID)
static jau::cow_darray< BTGattHandler::NativeGattCharListenerRef >::equal_comparator _nativeGattCharListenerRefEqComparator
static const jau::uuid16_t _MODEL_NUMBER_STRING(GattCharacteristicType::MODEL_NUMBER_STRING)
constexpr_cxx20 jau::nsize_t getElementCount() const noexcept
Number of elements.
jau::nsize_t getElementPDUOffset(const jau::nsize_t elementIdx) const
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.3.3 ATT_FIND_BY_TYPE_VALUE_REQ.
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.
uint16_t getElementHandle(const jau::nsize_t elementIdx) const
std::unique_ptr< const jau::uuid_t > getElementValue(const jau::nsize_t elementIdx) const
ATT Protocol PDUs Vol 3, Part F 3.4.7.3.
ATT Protocol PDUs Vol 3, Part F 3.4.7.1 and 3.4.7.2.
jau::TOctetSlice const & getValue() const noexcept
constexpr uint16_t getHandle() const noexcept
Handles the Attribute Protocol (ATT) using Protocol Data Unit (PDU) encoded messages over L2CAP chann...
const uint64_t ts_creation
creation timestamp in milliseconds
Opcode
ATT Opcode Summary Vol 3, Part F 3.4.8.
@ MULTIPLE_HANDLE_VALUE_NTF
@ READ_MULTIPLE_VARIABLE_REQ
jau::POctets pdu
actual received PDU
static constexpr OpcodeType get_type(const Opcode rhs) noexcept
virtual std::string toString() const noexcept
static std::unique_ptr< const AttPDUMsg > getSpecialized(const uint8_t *buffer, jau::nsize_t const buffer_size) noexcept
Return a newly created specialized instance pointer to base class.
constexpr_cxx20 jau::nsize_t getPDUValueSize() const noexcept
Returns the net octet size of this PDU's attributes value, i.e.
constexpr_cxx20 jau::nsize_t getMaxPDUValueSize(const jau::nsize_t mtu) const noexcept
Returns the theoretical maximum value size of a PDU's attribute value.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.5 ATT_BLOB_READ_REQ.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.4.10 ATT_READ_BY_GROUP_TYPE_RSP.
constexpr_cxx20 jau::nsize_t getElementSize() const noexcept override
Returns size of each element, i.e.
uint16_t getElementEndHandle(const jau::nsize_t elementIdx) const
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.
uint16_t getElementHandle(const jau::nsize_t elementIdx) const
constexpr_cxx20 jau::nsize_t getElementSize() const noexcept override
Returns size of each element, i.e.
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.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.3 ATT_WRITE_CMD.
BT Core Spec v5.2: Vol 3, Part F ATT: 3.4.5.1 ATT_WRITE_REQ.
HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept
Disconnect the LE or BREDR peer's GATT and HCI connection.
BTGattChar event listener for notification and indication events.
Representing a Gatt Characteristic object from the GATTRole::Client perspective.
BTGattServiceRef getServiceUnchecked() const noexcept
std::unique_ptr< const jau::uuid_t > value_type
PropertyBitVal
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties.
Representing a Gatt Characteristic Descriptor object from the GATTRole::Client perspective.
static BTGattEnv & get() noexcept
A thread safe GATT handler associated to one device via one L2CAP connection.
bool readDescriptorValue(BTGattDesc &cd, ssize_type expectedLength=-1) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.1 Read Characteristic Descriptor.
bool disconnect(const bool disconnect_device, const bool ioerr_cause) noexcept
Disconnect this BTGattHandler and optionally the associated device.
bool send(const AttPDUMsg &msg) noexcept
Sends the given AttPDUMsg to the connected device via l2cap.
bool writeValue(const uint16_t handle, const jau::TROOctets &value, const bool withResponse) noexcept
Generic write GATT value and long value.
void notifyNativeReplyReceived(const AttPDUMsg &pduReply, const BTDeviceRef &clientDest) noexcept
Notify all NativeGattCharListener about a low-level AttPDUMsg reply being received from this GATTRole...
bool addCharListener(const BTGattCharListenerRef &l) noexcept
Add the given listener to the list if not already present.
bool getSendIndicationConfirmation() noexcept
Returns whether sending an immediate confirmation for received indication events from the device is e...
void notifyNativeMTUResponse(const uint16_t clientMTU, const AttPDUMsg &pduReply, const AttErrorRsp::ErrorCode error_reply, const uint16_t serverMTU, const uint16_t usedMTU, const BTDeviceRef &clientRequester) noexcept
Notify all NativeGattCharListener about a completed MTU exchange request and response to and from thi...
std::string toString() const noexcept
bool readValue(const uint16_t handle, jau::POctets &res, ssize_type expectedLength=-1) noexcept
Generic read GATT value and long value.
bool writeCharacteristicValueNoResp(const BTGattChar &c, const jau::TROOctets &value) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response.
void setSendIndicationConfirmation(const bool v) noexcept
Enable or disable sending an immediate confirmation for received indication events from the device.
bool readCharacteristicValue(const BTGattChar &c, jau::POctets &res, ssize_type expectedLength=-1) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value.
void notifyNativeWriteResponse(const AttPDUMsg &pduReply, const AttErrorRsp::ErrorCode error_code, const BTDeviceRef &clientDest) noexcept
Notify all NativeGattCharListener about a write response received from this GATTRole::Server.
bool sendIndication(const uint16_t char_value_handle, const jau::TROOctets &value) noexcept
Send an indication event consisting out of the given value representing the given characteristic valu...
void notifyNativeReadResponse(const uint16_t handle, const uint16_t value_offset, const AttPDUMsg &pduReply, const AttErrorRsp::ErrorCode error_reply, const jau::TROOctets &data_reply, const BTDeviceRef &clientRequester) noexcept
Notify all NativeGattCharListener about a completed read request and response to and from this GATTRo...
bool configNotificationIndication(BTGattDesc &cd, const bool enableNotification, const bool enableIndication) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
bool initClientGatt(const std::shared_ptr< BTGattHandler > &shared_this, bool &already_init) noexcept
Initialize the connection and internal data set for GATT client operations:
bool writeDescriptorValue(const BTGattDesc &cd) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.3 Write Characteristic Descriptors.
std::string getStateString() const noexcept
bool sendNotification(const uint16_t char_value_handle, const jau::TROOctets &value) noexcept
Send a notification event consisting out of the given value representing the given characteristic val...
size_type removeAllAssociatedCharListener(const BTGattCharRef &associatedChar) noexcept
Remove all BTGattCharListener from the list, which are associated to the given BTGattChar when added ...
bool removeCharListener(const BTGattCharListenerRef &l) noexcept
Remove the given listener from the list.
BTGattHandler(const BTDeviceRef &device, L2CAPClient &l2cap_att, const int32_t supervision_timeout) noexcept
Constructing a new BTGattHandler instance with its opened and connected L2CAP channel.
bool writeCharacteristicValue(const BTGattChar &c, const jau::TROOctets &value) noexcept
BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value.
std::unique_ptr< const AttPDUMsg > sendWithReply(const AttPDUMsg &msg, const jau::fraction_i64 &timeout) noexcept
Sends the given AttPDUMsg to the connected device via l2cap using send().
std::shared_ptr< GattGenericAccessSvc > getGenericAccess() noexcept
Returns the internal kept shared GattGenericAccessSvc instance.
BTGattCharRef findCharacterisicsByValueHandle(const GattServiceList_t &services_, const uint16_t charValueHandle) noexcept
Find and return the BTGattChar within given list of primary services via given characteristic value h...
bool ping() noexcept
Issues a ping to the device, validating whether it is still reachable.
std::shared_ptr< NativeGattCharListener > NativeGattCharListenerRef
BTDeviceRef getDeviceChecked() const
size_type removeAllCharListener() noexcept
Remove all event listener from the list.
std::shared_ptr< GattDeviceInformationSvc > getDeviceInformation(GattServiceList_t &primServices) noexcept
~BTGattHandler() noexcept
Destructor closing this instance including L2CAP channel, see disconnect().
void printCharListener() noexcept
Print a list of all BTGattCharListener and NativeGattCharListener.
void notifyNativeRequestSent(const AttPDUMsg &pduRequest, const BTDeviceRef &clientSource) noexcept
Notify all NativeGattCharListener about a low-level AttPDUMsg request being sent to this GATTRole::Se...
void notifyNativeWriteRequest(const uint16_t handle, const jau::TROOctets &data, const NativeGattCharSections_t §ions, const bool with_response, const BTDeviceRef &clientSource) noexcept
Notify all NativeGattCharListener about a completed write request sent to this GATTRole::Server.
GATTRole getRole() const noexcept
Return the local GATTRole to the remote BTDevice.
std::shared_ptr< Listener > ListenerRef
@ DB
Database mode, the default operating on given list of DBGattService.
L2CAP read/write communication channel to remote device.
static std::string getRWExitCodeString(const RWExitCode ec) noexcept
std::string getStateString() const noexcept override
static constexpr int number(const Defaults d) noexcept
bool hasIOError() const noexcept
@ READ_TIMEOUT
READ_TIMEOUT.
@ POLL_TIMEOUT
POLL_TIMEOUT.
@ INTERRUPTED
INTERRUPTED.
virtual std::string getStateString() const noexcept=0
bool is_open() const noexcept
Persistent endian aware octet data, i.e.
POctets & resize(const nsize_t newCapacity, const nsize_t newSize)
Resizes this instance, including its capacity.
Transient endian aware octet data slice, i.e.
constexpr lb_endian_t byte_order() const noexcept
Returns byte order of this octet store.
constexpr nsize_t size() const noexcept
constexpr uint8_t const * get_ptr_nc(const nsize_t i) const noexcept
Transient read only and endian aware octet data, i.e.
std::unique_ptr< const uuid_t > get_uuid(const nsize_t i, const uuid_t::TypeSize tsize) const
constexpr nsize_t size() const noexcept
Returns the used memory size for read and write operations, may be zero.
uint16_t get_uint16(const nsize_t i) const
uint8_t get_uint8(const nsize_t i) const
Implementation of a Copy-On-Write (CoW) using jau::darray as the underlying storage,...
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
bool(* equal_comparator)(const value_type &a, const value_type &b)
Generic value_type equal comparator to be user defined for e.g.
constexpr_atomic size_type size() const noexcept
Like std::vector::size().
constexpr iterator begin()
Returns an jau::cow_rw_iterator to the first element of this CoW storage.
Implementation of a dynamic linear array storage, aka vector.
constexpr size_type size() const noexcept
Like std::vector::size().
const_reference at(size_type i) const
Like std::vector::at(size_type), immutable reference.
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Service runner, a reusable dedicated thread performing custom user services.
bool is_running() const noexcept
Returns true if service is running.
pthread_t thread_id() const noexcept
Return the thread-id of this service service thread, zero if not running.
bool shall_stop() const noexcept
Returns true if service shall stop.
static TypeSize toTypeSize(const jau::nsize_t size)
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE FUNC: '.
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
@ little
Identifier for little endian, equivalent to endian::little.
std::string to_string(const alphabet &v) noexcept
constexpr const jau::fraction_i64 THREAD_SHUTDOWN_TIMEOUT_MS
Maximum time in fractions of seconds to wait for a thread shutdown.
std::string GattNameToString(const jau::TROOctets &v) noexcept
Converts a GATT Name (not null-terminated) UTF8 to a null-terminated C++ string.
std::shared_ptr< BTDevice > BTDeviceRef
std::string to_string(const DiscoveryPolicy v) noexcept
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
constexpr uint8_t number(const DiscoveryPolicy rhs) noexcept
@ Client
Local GATT client role to a remote BTDevice BTRole::Slave running a GATTRole::Server.
@ Server
Local GATT server role to a remote BTDevice BTRole::Master running a GATTRole::Client.
@ REMOTE_USER_TERMINATED_CONNECTION
@ REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF
std::shared_ptr< BTGattCharListener > BTGattCharListenerRef
std::shared_ptr< BTGattChar > BTGattCharRef
std::shared_ptr< BTGattService > BTGattServiceRef
std::shared_ptr< DBGattChar > DBGattCharRef
jau::function< R(A...)> bind_member(C1 *base, R(C0::*mfunc)(A...)) noexcept
Bind given class instance and non-void member function to an anonymous function using func_member_tar...
constexpr T min(const T x, const T y) noexcept
Returns the minimum of two integrals (w/ branching) in O(1)
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
constexpr T max(const T x, const T y) noexcept
Returns the maximum of two integrals (w/ branching) in O(1)
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
void INFO_PRINT(const char *format,...) noexcept
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
static std::shared_ptr< GattPeriphalPreferredConnectionParameters > get(const jau::TROOctets &source) noexcept
static std::shared_ptr< GattPnP_ID > get(const jau::TROOctets &source) noexcept
CXX_ALWAYS_INLINE _Tp load() const noexcept
@ GENERIC_ACCESS
This service contains generic information about the device.
@ DEVICE_INFORMATION
This service exposes manufacturer and/or vendor information about a device.
@ SOFTWARE_REVISION_STRING
@ REGULATORY_CERT_DATA_LIST
@ MANUFACTURER_NAME_STRING
@ PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS
@ FIRMWARE_REVISION_STRING
@ SYSTEM_ID
Mandatory: uint40.
@ HARDWARE_REVISION_STRING