Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
BTGattHandler.cpp
Go to the documentation of this file.
1 /*
2  * Author: Sven Gothel <sgothel@jausoft.com>
3  * Copyright (c) 2020 Gothel Software e.K.
4  * Copyright (c) 2020 ZAFENA AB
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include <cstring>
27 #include <string>
28 #include <memory>
29 #include <cstdint>
30 #include <cstdio>
31 
32 #include <algorithm>
33 
34 extern "C" {
35  #include <unistd.h>
36  #include <sys/socket.h>
37  #include <poll.h>
38  #include <signal.h>
39 }
40 
41 // #define PERF_PRINT_ON 1
42 // PERF2_PRINT_ON for read/write single values
43 // #define PERF2_PRINT_ON 1
44 // PERF3_PRINT_ON for disconnect
45 // #define PERF3_PRINT_ON 1
46 #include <jau/debug.hpp>
47 
48 #include <jau/basic_algos.hpp>
49 
50 #include "L2CAPIoctl.hpp"
51 #include "GattNumbers.hpp"
52 
53 #include "BTGattHandler.hpp"
54 
55 #include "BTDevice.hpp"
56 
57 #include "BTManager.hpp"
58 #include "BTAdapter.hpp"
59 #include "BTManager.hpp"
60 
61 using namespace direct_bt;
62 
63 BTGattEnv::BTGattEnv() noexcept
64 : exploding( jau::environment::getExplodingProperties("direct_bt.gatt") ),
65  GATT_READ_COMMAND_REPLY_TIMEOUT( jau::environment::getInt32Property("direct_bt.gatt.cmd.read.timeout", 500, 250 /* min */, INT32_MAX /* max */) ),
66  GATT_WRITE_COMMAND_REPLY_TIMEOUT( jau::environment::getInt32Property("direct_bt.gatt.cmd.write.timeout", 500, 250 /* min */, INT32_MAX /* max */) ),
67  GATT_INITIAL_COMMAND_REPLY_TIMEOUT( jau::environment::getInt32Property("direct_bt.gatt.cmd.init.timeout", 2500, 2000 /* min */, INT32_MAX /* max */) ),
68  ATTPDU_RING_CAPACITY( jau::environment::getInt32Property("direct_bt.gatt.ringsize", 128, 64 /* min */, 1024 /* max */) ),
69  DEBUG_DATA( jau::environment::getBooleanProperty("direct_bt.debug.gatt.data", false) )
70 {
71 }
72 
73 #define CASE_TO_STRING(V) case V: return #V;
74 
75 std::shared_ptr<BTDevice> BTGattHandler::getDeviceChecked() const {
76  std::shared_ptr<BTDevice> ref = wbr_device.lock();
77  if( nullptr == ref ) {
78  throw jau::IllegalStateException("GATTHandler's device already destructed: "+deviceString, E_FILE_LINE);
79  }
80  return ref;
81 }
82 
83 bool BTGattHandler::validateConnected() noexcept {
84  bool l2capIsConnected = l2cap.isOpen();
85  bool l2capHasIOError = l2cap.hasIOError();
86 
87  if( has_ioerror || l2capHasIOError ) {
88  has_ioerror = true; // propagate l2capHasIOError -> has_ioerror
89  ERR_PRINT("IOError state: GattHandler %s, l2cap %s: %s",
90  getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
91  return false;
92  }
93 
94  if( !is_connected || !l2capIsConnected ) {
95  ERR_PRINT("Disconnected state: GattHandler %s, l2cap %s: %s",
96  getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
97  return false;
98  }
99  return true;
100 }
101 
103  [](const std::shared_ptr<BTGattCharListener> &a, const std::shared_ptr<BTGattCharListener> &b) -> bool { return *a == *b; };
104 
105 bool BTGattHandler::addCharListener(std::shared_ptr<BTGattCharListener> l) {
106  if( nullptr == l ) {
107  throw jau::IllegalArgumentException("GATTEventListener ref is null", E_FILE_LINE);
108  }
109  return characteristicListenerList.push_back_unique(l, _characteristicListenerRefEqComparator);
110 }
111 
112 bool BTGattHandler::removeCharListener(std::shared_ptr<BTGattCharListener> l) noexcept {
113  if( nullptr == l ) {
114  ERR_PRINT("Given GATTCharacteristicListener ref is null");
115  return false;
116  }
117  const int count = characteristicListenerList.erase_matching(l, false /* all_matching */, _characteristicListenerRefEqComparator);
118  return count > 0;
119 }
120 
122  if( nullptr == l ) {
123  ERR_PRINT("Given GATTCharacteristicListener ref is null");
124  return false;
125  }
126  auto it = characteristicListenerList.begin(); // lock mutex and copy_store
127  for (; !it.is_end(); ++it ) {
128  if ( **it == *l ) {
129  it.erase();
130  it.write_back();
131  return true;
132  }
133  }
134  return false;
135 }
136 
137 int BTGattHandler::removeAllAssociatedCharListener(std::shared_ptr<BTGattChar> associatedCharacteristic) noexcept {
138  if( nullptr == associatedCharacteristic ) {
139  ERR_PRINT("Given GATTCharacteristic ref is null");
140  return false;
141  }
142  return removeAllAssociatedCharListener( associatedCharacteristic.get() );
143 }
144 
145 int BTGattHandler::removeAllAssociatedCharListener(const BTGattChar * associatedCharacteristic) noexcept {
146  if( nullptr == associatedCharacteristic ) {
147  ERR_PRINT("Given GATTCharacteristic ref is null");
148  return false;
149  }
150  int count = 0;
151  auto it = characteristicListenerList.begin(); // lock mutex and copy_store
152  while( !it.is_end() ) {
153  if ( (*it)->match(*associatedCharacteristic) ) {
154  it.erase();
155  ++count;
156  } else {
157  ++it;
158  }
159  }
160  if( 0 < count ) {
161  it.write_back();
162  }
163  return count;
164 }
165 
167  int count = characteristicListenerList.size();
168  characteristicListenerList.clear();
169  return count;
170 }
171 
173  sendIndicationConfirmation = v;
174 }
175 
177  return sendIndicationConfirmation;
178 }
179 
180 void BTGattHandler::l2capReaderThreadImpl() {
181  {
182  const std::lock_guard<std::mutex> lock(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
183  l2capReaderShallStop = false;
184  l2capReaderRunning = true;
185  DBG_PRINT("GATTHandler::reader Started");
186  cv_l2capReaderInit.notify_all();
187  }
188  thread_local jau::call_on_release thread_cleanup([&]() {
189  DBG_PRINT("GATTHandler::l2capReaderThreadCleanup: l2capReaderRunning %d -> 0", l2capReaderRunning.load());
190  l2capReaderRunning = false;
191  });
192 
193  while( !l2capReaderShallStop ) {
194  jau::snsize_t len;
195  if( !validateConnected() ) {
196  ERR_PRINT("GATTHandler::reader: Invalid IO state -> Stop");
197  l2capReaderShallStop = true;
198  break;
199  }
200 
201  len = l2cap.read(rbuffer.get_wptr(), rbuffer.getSize());
202  if( 0 < len ) {
203  std::unique_ptr<const AttPDUMsg> attPDU = AttPDUMsg::getSpecialized(rbuffer.get_ptr(), static_cast<jau::nsize_t>(len));
204  const AttPDUMsg::Opcode opc = attPDU->getOpcode();
205 
207  const AttHandleValueRcv * a = static_cast<const AttHandleValueRcv*>(attPDU.get());
208  COND_PRINT(env.DEBUG_DATA, "GATTHandler::reader: NTF: %s, listener %zd", a->toString().c_str(), characteristicListenerList.size());
210  const TOctetSlice& a_value_view = a->getValue();
211  const TROOctets data_view(a_value_view.get_ptr_nc(0), a_value_view.getSize()); // just a view, still owned by attPDU
212  // const std::shared_ptr<TROOctets> data( std::make_shared<POctets>(a->getValue()) );
213  const uint64_t timestamp = a->ts_creation;
214  int i=0;
215  jau::for_each_fidelity(characteristicListenerList, [&](std::shared_ptr<BTGattCharListener> &l) {
216  try {
217  if( l->match(*decl) ) {
218  l->notificationReceived(decl, data_view, timestamp);
219  }
220  } catch (std::exception &e) {
221  ERR_PRINT("GATTHandler::notificationReceived-CBs %d/%zd: GATTCharacteristicListener %s: Caught exception %s",
222  i+1, characteristicListenerList.size(),
223  jau::to_hexstring((void*)l.get()).c_str(), e.what());
224  }
225  i++;
226  });
227  } else if( AttPDUMsg::Opcode::HANDLE_VALUE_IND == opc ) {
228  const AttHandleValueRcv * a = static_cast<const AttHandleValueRcv*>(attPDU.get());
229  COND_PRINT(env.DEBUG_DATA, "GATTHandler::reader: IND: %s, sendIndicationConfirmation %d, listener %zd",
230  a->toString().c_str(), sendIndicationConfirmation.load(), characteristicListenerList.size());
231  bool cfmSent = false;
232  if( sendIndicationConfirmation ) {
233  AttHandleValueCfm cfm;
234  send(cfm);
235  cfmSent = true;
236  }
238  const TOctetSlice& a_value_view = a->getValue();
239  const TROOctets data_view(a_value_view.get_ptr_nc(0), a_value_view.getSize()); // just a view, still owned by attPDU
240  // const std::shared_ptr<TROOctets> data( std::make_shared<POctets>(a->getValue()) );
241  const uint64_t timestamp = a->ts_creation;
242  int i=0;
243  jau::for_each_fidelity(characteristicListenerList, [&](std::shared_ptr<BTGattCharListener> &l) {
244  try {
245  if( l->match(*decl) ) {
246  l->indicationReceived(decl, data_view, timestamp, cfmSent);
247  }
248  } catch (std::exception &e) {
249  ERR_PRINT("GATTHandler::indicationReceived-CBs %d/%zd: GATTCharacteristicListener %s, cfmSent %d: Caught exception %s",
250  i+1, characteristicListenerList.size(),
251  jau::to_hexstring((void*)l.get()).c_str(), cfmSent, e.what());
252  }
253  i++;
254  });
256  // FIXME TODO ..
257  ERR_PRINT("GATTHandler::reader: MULTI-NTF not implemented: %s", attPDU->toString().c_str());
258  } else {
259  attPDURing.putBlocking( std::move(attPDU) );
260  }
261  } else if( ETIMEDOUT != errno && !l2capReaderShallStop ) { // expected exits
262  IRQ_PRINT("GATTHandler::reader: l2cap read error -> Stop; l2cap.read %d", len);
263  l2capReaderShallStop = true;
264  has_ioerror = true;
265  }
266  }
267  {
268  const std::lock_guard<std::mutex> lock(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
269  WORDY_PRINT("GATTHandler::reader: Ended. Ring has %u entries flushed", attPDURing.getSize());
270  attPDURing.clear();
271  l2capReaderRunning = false;
272  cv_l2capReaderInit.notify_all();
273  }
274  disconnect(true /* disconnectDevice */, has_ioerror);
275 }
276 
277 BTGattHandler::BTGattHandler(const std::shared_ptr<BTDevice> &device, L2CAPComm& l2cap_att) noexcept
278 : env(BTGattEnv::get()),
279  wbr_device(device),
280  l2cap(l2cap_att),
281  deviceString(device->getAddressAndType().toString()),
282  rbuffer(number(Defaults::MAX_ATT_MTU)),
283  is_connected(l2cap.isOpen()), has_ioerror(false),
284  attPDURing(nullptr, env.ATTPDU_RING_CAPACITY), l2capReaderShallStop(false),
285  l2capReaderThreadId(0), l2capReaderRunning(false),
286  serverMTU(number(Defaults::MIN_ATT_MTU)), usedMTU(number(Defaults::MIN_ATT_MTU))
287 {
288  if( !validateConnected() ) {
289  ERR_PRINT("GATTHandler.ctor: L2CAP could not connect");
290  is_connected = false;
291  return;
292  }
293  DBG_PRINT("GATTHandler::ctor: Start Connect: GattHandler[%s], l2cap[%s]: %s",
294  getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
295 
296  /**
297  * We utilize DBTManager's mgmthandler_sigaction SIGALRM handler,
298  * as we only can install one handler.
299  */
300  {
301  std::unique_lock<std::mutex> lock(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
302 
303  std::thread l2capReaderThread(&BTGattHandler::l2capReaderThreadImpl, this); // @suppress("Invalid arguments")
304  l2capReaderThreadId = l2capReaderThread.native_handle();
305  // Avoid 'terminate called without an active exception'
306  // as l2capReaderThread may end due to I/O errors.
307  l2capReaderThread.detach();
308 
309  while( false == l2capReaderRunning ) {
310  cv_l2capReaderInit.wait(lock);
311  }
312  }
313 
314  // First point of failure if device exposes no GATT functionality. Allow a longer timeout!
315  uint16_t mtu = 0;
316  try {
317  mtu = exchangeMTUImpl(number(Defaults::MAX_ATT_MTU), env.GATT_INITIAL_COMMAND_REPLY_TIMEOUT);
318  } catch (std::exception &e) {
319  ERR_PRINT2("GattHandler.ctor: exchangeMTU failed: %s", e.what());
320  } catch (std::string &msg) {
321  ERR_PRINT2("GattHandler.ctor: exchangeMTU failed: %s", msg.c_str());
322  } catch (const char *msg) {
323  ERR_PRINT2("GattHandler.ctor: exchangeMTU failed: %s", msg);
324  }
325  if( 0 == mtu ) {
326  ERR_PRINT2("GATTHandler::ctor: Zero serverMTU -> disconnect: %s", deviceString.c_str());
327  disconnect(true /* disconnectDevice */, false /* ioErrorCause */);
328  } else {
329  serverMTU = mtu;
330  usedMTU = std::min(number(Defaults::MAX_ATT_MTU), (int)serverMTU);
331  }
332 }
333 
335  disconnect(false /* disconnectDevice */, false /* ioErrorCause */);
336  characteristicListenerList.clear();
337  services.clear();
338  genericAccess = nullptr;
339 }
340 
341 bool BTGattHandler::disconnect(const bool disconnectDevice, const bool ioErrorCause) noexcept {
342  PERF3_TS_T0();
343  // Interrupt GATT's L2CAP::connect(..) and L2CAP::read(..), avoiding prolonged hang
344  // and pull all underlying l2cap read operations!
345  l2cap.close();
346 
347  // Avoid disconnect re-entry -> potential deadlock
348  bool expConn = true; // C++11, exp as value since C++20
349  if( !is_connected.compare_exchange_strong(expConn, false) ) {
350  // not connected
351  DBG_PRINT("GATTHandler::disconnect: Not connected: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
352  disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
353  characteristicListenerList.clear();
354  return false;
355  }
356  // Lock to avoid other threads using instance while disconnecting
357  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
358  DBG_PRINT("GATTHandler::disconnect: Start: disconnectDevice %d, ioErrorCause %d: GattHandler[%s], l2cap[%s]: %s",
359  disconnectDevice, ioErrorCause, getStateString().c_str(), l2cap.getStateString().c_str(), deviceString.c_str());
360  characteristicListenerList.clear();
361 
362  PERF3_TS_TD("GATTHandler::disconnect.1");
363  {
364  std::unique_lock<std::mutex> lockReader(mtx_l2capReaderLifecycle); // RAII-style acquire and relinquish via destructor
365  has_ioerror = false;
366 
367  const pthread_t tid_self = pthread_self();
368  const pthread_t tid_l2capReader = l2capReaderThreadId;
369  l2capReaderThreadId = 0;
370  const bool is_l2capReader = tid_l2capReader == tid_self;
371  DBG_PRINT("GATTHandler.disconnect: l2capReader[running %d, shallStop %d, isReader %d, tid %p)",
372  l2capReaderRunning.load(), l2capReaderShallStop.load(), is_l2capReader, (void*)tid_l2capReader);
373  if( l2capReaderRunning ) {
374  l2capReaderShallStop = true;
375  if( !is_l2capReader && 0 != tid_l2capReader ) {
376  int kerr;
377  if( 0 != ( kerr = pthread_kill(tid_l2capReader, SIGALRM) ) ) {
378  ERR_PRINT("GATTHandler::disconnect: pthread_kill %p FAILED: %d", (void*)tid_l2capReader, kerr);
379  }
380  }
381  // Ensure the reader thread has ended, no runaway-thread using *this instance after destruction
382  while( true == l2capReaderRunning ) {
383  cv_l2capReaderInit.wait(lockReader);
384  }
385  }
386  }
387  PERF3_TS_TD("GATTHandler::disconnect.2");
388 
389  if( disconnectDevice ) {
390  std::shared_ptr<BTDevice> device = getDeviceUnchecked();
391  if( nullptr != device ) {
392  // Cleanup device resources, proper connection state
393  // Intentionally giving the POWER_OFF reason for the device in case of ioErrorCause!
394  const HCIStatusCode reason = ioErrorCause ?
397  device->disconnect(reason);
398  }
399  }
400 
401  PERF3_TS_TD("GATTHandler::disconnect.X");
402  DBG_PRINT("GATTHandler::disconnect: End: %s", deviceString.c_str());
403  return true;
404 }
405 
406 void BTGattHandler::send(const AttPDUMsg & msg) {
407  if( !validateConnected() ) {
408  throw jau::IllegalStateException("GATTHandler::send: Invalid IO State: req "+msg.toString()+" to "+deviceString, E_FILE_LINE);
409  }
410  if( msg.pdu.getSize() > usedMTU ) {
411  throw jau::IllegalArgumentException("clientMaxMTU "+std::to_string(msg.pdu.getSize())+" > usedMTU "+std::to_string(usedMTU)+
412  " to "+deviceString, E_FILE_LINE);
413  }
414 
415  // Thread safe l2cap.write(..) operation..
416  const ssize_t res = l2cap.write(msg.pdu.get_ptr(), msg.pdu.getSize());
417  if( 0 > res ) {
418  IRQ_PRINT("GATTHandler::send: l2cap write error -> disconnect: %s to %s", msg.toString().c_str(), deviceString.c_str());
419  has_ioerror = true;
420  disconnect(true /* disconnectDevice */, true /* ioErrorCause */); // state -> Disconnected
421  throw BTException("GATTHandler::send: l2cap write error: req "+msg.toString()+" to "+deviceString, E_FILE_LINE);
422  }
423  if( static_cast<size_t>(res) != msg.pdu.getSize() ) {
424  ERR_PRINT("GATTHandler::send: l2cap write count error, %zd != %zu: %s -> disconnect: %s",
425  res, msg.pdu.getSize(), msg.toString().c_str(), deviceString.c_str());
426  has_ioerror = true;
427  disconnect(true /* disconnectDevice */, true /* ioErrorCause */); // state -> Disconnected
428  throw BTException("GATTHandler::send: l2cap write count error, "+std::to_string(res)+" != "+std::to_string(res)
429  +": "+msg.toString()+" -> disconnect: "+deviceString, E_FILE_LINE);
430  }
431 }
432 
433 std::unique_ptr<const AttPDUMsg> BTGattHandler::sendWithReply(const AttPDUMsg & msg, const int timeout) {
434  send( msg );
435 
436  // Ringbuffer read is thread safe
437  std::unique_ptr<const AttPDUMsg> res = attPDURing.getBlocking(timeout);
438  if( nullptr == res ) {
439  errno = ETIMEDOUT;
440  IRQ_PRINT("GATTHandler::sendWithReply: nullptr result (timeout %d): req %s to %s", timeout, msg.toString().c_str(), deviceString.c_str());
441  has_ioerror = true;
442  disconnect(true /* disconnectDevice */, true /* ioErrorCause */);
443  throw BTException("GATTHandler::sendWithReply: nullptr result (timeout "+std::to_string(timeout)+"): req "+msg.toString()+" to "+deviceString, E_FILE_LINE);
444  }
445  return res;
446 }
447 
448 uint16_t BTGattHandler::exchangeMTUImpl(const uint16_t clientMaxMTU, const int32_t timeout) {
449  /***
450  * BT Core Spec v5.2: Vol 3, Part G GATT: 4.3.1 Exchange MTU (Server configuration)
451  */
452  if( clientMaxMTU > number(Defaults::MAX_ATT_MTU) ) {
453  throw jau::IllegalArgumentException("clientMaxMTU "+std::to_string(clientMaxMTU)+" > ClientMaxMTU "+std::to_string(number(Defaults::MAX_ATT_MTU)), E_FILE_LINE);
454  }
455  const AttExchangeMTU req(clientMaxMTU);
456  // called by ctor only, no locking: const std::lock_guard<std::recursive_mutex> lock(mtx_command);
457  PERF_TS_T0();
458 
459  uint16_t mtu = 0;
460  DBG_PRINT("GATT MTU send: %s to %s", req.toString().c_str(), deviceString.c_str());
461 
462  std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, timeout); // valid reply or exception
463 
465  const AttExchangeMTU * p = static_cast<const AttExchangeMTU*>(pdu.get());
466  mtu = p->getMTUSize();
467  DBG_PRINT("GATT MTU recv: %u, %s from %s", mtu, pdu->toString().c_str(), deviceString.c_str());
468  } else if( pdu->getOpcode() == AttPDUMsg::Opcode::ERROR_RSP ) {
469  /**
470  * If the ATT_ERROR_RSP PDU is sent by the server
471  * with the error code set to 'Request Not Supported',
472  * the Attribute Opcode is not supported and the default MTU shall be used.
473  */
474  const AttErrorRsp * p = static_cast<const AttErrorRsp *>(pdu.get());
476  mtu = number(Defaults::MIN_ATT_MTU); // OK by spec: Use default MTU
477  DBG_PRINT("GATT MTU handled error -> ATT_MTU %u, %s from %s", mtu, pdu->toString().c_str(), deviceString.c_str());
478  } else {
479  WORDY_PRINT("GATT MTU unexpected error %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), deviceString.c_str());
480  }
481  } else {
482  ERR_PRINT("GATT MTU unexpected reply %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), deviceString.c_str());
483  }
484  PERF_TS_TD("GATT exchangeMTU");
485 
486  return mtu;
487 }
488 
489 BTGattCharRef BTGattHandler::findCharacterisicsByValueHandle(const uint16_t charValueHandle) noexcept {
490  return findCharacterisicsByValueHandle(charValueHandle, services);
491 }
492 
494  for(auto it = services_.begin(); it != services_.end(); it++) {
495  BTGattCharRef decl = findCharacterisicsByValueHandle(charValueHandle, *it);
496  if( nullptr != decl ) {
497  return decl;
498  }
499  }
500  return nullptr;
501 }
502 
503 BTGattCharRef BTGattHandler::findCharacterisicsByValueHandle(const uint16_t charValueHandle, BTGattServiceRef service) noexcept {
504  for(auto it = service->characteristicList.begin(); it != service->characteristicList.end(); it++) {
505  BTGattCharRef decl = *it;
506  if( charValueHandle == decl->value_handle ) {
507  return decl;
508  }
509  }
510  return nullptr;
511 }
512 
514  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
515  if( !discoverPrimaryServices(shared_this, services) ) {
516  return services;
517  }
518  for(auto it = services.begin(); it != services.end(); it++) {
519  BTGattServiceRef primSrv = *it;
520  if( discoverCharacteristics(primSrv) ) {
521  discoverDescriptors(primSrv);
522  }
523  }
524  genericAccess = getGenericAccess(services);
525  return services;
526 }
527 
528 bool BTGattHandler::discoverPrimaryServices(std::shared_ptr<BTGattHandler> shared_this, jau::darray<BTGattServiceRef> & result) {
529  {
530  // validate shared_this first!
531  BTGattHandler *given_this = shared_this.get();
532  if( given_this != this ) {
533  throw jau::IllegalArgumentException("Given shared GATTHandler reference "+
534  jau::to_hexstring(given_this)+" not matching this "+jau::to_hexstring(this), E_FILE_LINE);
535  }
536  }
537  /***
538  * BT Core Spec v5.2: Vol 3, Part G GATT: 4.4.1 Discover All Primary Services
539  *
540  * This sub-procedure is complete when the ATT_ERROR_RSP PDU is received
541  * and the error code is set to Attribute Not Found or when the End Group Handle
542  * in the Read by Type Group Response is 0xFFFF.
543  */
545  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
546  PERF_TS_T0();
547 
548  bool done=false;
549  uint16_t startHandle=0x0001;
550  result.clear();
551  while(!done) {
552  const AttReadByNTypeReq req(true /* group */, startHandle, 0xffff, groupType);
553  COND_PRINT(env.DEBUG_DATA, "GATT PRIM SRV discover send: %s to %s", req.toString().c_str(), deviceString.c_str());
554 
555  std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, env.GATT_READ_COMMAND_REPLY_TIMEOUT); // valid reply or exception
556  COND_PRINT(env.DEBUG_DATA, "GATT PRIM SRV discover recv: %s on %s", pdu->toString().c_str(), deviceString.c_str());
557 
559  const AttReadByGroupTypeRsp * p = static_cast<const AttReadByGroupTypeRsp*>(pdu.get());
560  const int count = p->getElementCount();
561 
562  for(int i=0; i<count; i++) {
563  const int ePDUOffset = p->getElementPDUOffset(i);
564  const int esz = p->getElementTotalSize();
565  result.push_back( BTGattServiceRef( new BTGattService( shared_this, true,
566  p->pdu.get_uint16(ePDUOffset), // start-handle
567  p->pdu.get_uint16(ePDUOffset + 2), // end-handle
568  p->pdu.get_uuid( ePDUOffset + 2 + 2, uuid_t::toTypeSize(esz-2-2) ) // uuid
569  ) ) );
570  COND_PRINT(env.DEBUG_DATA, "GATT PRIM SRV discovered[%d/%d]: %s on %s", i,
571  count, result.at(result.size()-1)->toString().c_str(), deviceString.c_str());
572  }
573  startHandle = p->getElementEndHandle(count-1);
574  if( startHandle < 0xffff ) {
575  startHandle++;
576  } else {
577  done = true; // OK by spec: End of communication
578  }
579  } else if( pdu->getOpcode() == AttPDUMsg::Opcode::ERROR_RSP ) {
580  done = true; // OK by spec: End of communication
581  } else {
582  ERR_PRINT("GATT discoverPrimary unexpected reply %s, req %s from %s",
583  pdu->toString().c_str(), req.toString().c_str(), deviceString.c_str());
584  done = true;
585  }
586  }
587  PERF_TS_TD("GATT discoverPrimaryServices");
588 
589  return result.size() > 0;
590 }
591 
593  /***
594  * BT Core Spec v5.2: Vol 3, Part G GATT: 4.6.1 Discover All Characteristics of a Service
595  * <p>
596  * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1 Characteristic Declaration Attribute Value
597  * </p>
598  * <p>
599  * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration
600  * </p>
601  */
602  const uuid16_t characteristicTypeReq = uuid16_t(GattAttributeType::CHARACTERISTIC);
603  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
604  COND_PRINT(env.DEBUG_DATA, "GATT discoverCharacteristics Service: %s on %s", service->toString().c_str(), deviceString.c_str());
605 
606  PERF_TS_T0();
607 
608  bool done=false;
609  uint16_t handle=service->startHandle;
610  service->characteristicList.clear();
611  while(!done) {
612  const AttReadByNTypeReq req(false /* group */, handle, service->endHandle, characteristicTypeReq);
613  COND_PRINT(env.DEBUG_DATA, "GATT C discover send: %s to %s", req.toString().c_str(), deviceString.c_str());
614 
615  std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, env.GATT_READ_COMMAND_REPLY_TIMEOUT); // valid reply or exception
616  COND_PRINT(env.DEBUG_DATA, "GATT C discover recv: %s from %s", pdu->toString().c_str(), deviceString.c_str());
617 
619  const AttReadByTypeRsp * p = static_cast<const AttReadByTypeRsp*>(pdu.get());
620  const int e_count = p->getElementCount();
621 
622  for(int e_iter=0; e_iter<e_count; e_iter++) {
623  // handle: handle for the Characteristics declaration
624  // value: Characteristics Property, Characteristics Value Handle _and_ Characteristics UUID
625  const int ePDUOffset = p->getElementPDUOffset(e_iter);
626  const int esz = p->getElementTotalSize();
627  service->characteristicList.push_back( BTGattCharRef( new BTGattChar(
628  service,
629  p->pdu.get_uint16(ePDUOffset), // Characteristics's Service Handle
630  p->getElementHandle(e_iter), // Characteristic Handle
631  static_cast<BTGattChar::PropertyBitVal>(p->pdu.get_uint8(ePDUOffset + 2)), // Characteristics Property
632  p->pdu.get_uint16(ePDUOffset + 2 + 1), // Characteristics Value Handle
633  p->pdu.get_uuid(ePDUOffset + 2 + 1 + 2, uuid_t::toTypeSize(esz-2-1-2) ) ) ) ); // Characteristics Value Type UUID
634  COND_PRINT(env.DEBUG_DATA, "GATT C discovered[%d/%d]: char%s on %s", e_iter, e_count,
635  service->characteristicList.at(service->characteristicList.size()-1)->toString().c_str(), deviceString.c_str());
636  }
637  handle = p->getElementHandle(e_count-1); // Last Characteristic Handle
638  if( handle < service->endHandle ) {
639  handle++;
640  } else {
641  done = true; // OK by spec: End of communication
642  }
643  } else if( pdu->getOpcode() == AttPDUMsg::Opcode::ERROR_RSP ) {
644  done = true; // OK by spec: End of communication
645  } else {
646  ERR_PRINT("GATT discoverCharacteristics unexpected reply %s, req %s within service%s from %s",
647  pdu->toString().c_str(), req.toString().c_str(), service->toString().c_str(), deviceString.c_str());
648  done = true;
649  }
650  }
651 
652  PERF_TS_TD("GATT discoverCharacteristics");
653 
654  return service->characteristicList.size() > 0;
655 }
656 
658  /***
659  * BT Core Spec v5.2: Vol 3, Part G GATT: 4.7.1 Discover All Characteristic Descriptors
660  * <p>
661  * BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1 Characteristic Declaration Attribute Value
662  * </p>
663  */
664  COND_PRINT(env.DEBUG_DATA, "GATT discoverDescriptors Service: %s on %s", service->toString().c_str(), deviceString.c_str());
665  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
666  PERF_TS_T0();
667 
668  const int charCount = service->characteristicList.size();
669  for(int charIter=0; charIter < charCount; charIter++ ) {
670  BTGattCharRef charDecl = service->characteristicList[charIter];
671  charDecl->clearDescriptors();
672  COND_PRINT(env.DEBUG_DATA, "GATT discoverDescriptors Characteristic[%d/%d]: %s on %s", charIter, charCount, charDecl->toString().c_str(), deviceString.c_str());
673 
674  uint16_t cd_handle_iter = charDecl->value_handle + 1; // Start @ Characteristic Value Handle + 1
675  uint16_t cd_handle_end;
676  if( charIter+1 < charCount ) {
677  cd_handle_end = service->characteristicList.at(charIter+1)->value_handle;
678  } else {
679  cd_handle_end = service->endHandle;
680  }
681 
682  bool done=false;
683 
684  while( !done && cd_handle_iter <= cd_handle_end ) {
685  const AttFindInfoReq req(cd_handle_iter, cd_handle_end);
686  COND_PRINT(env.DEBUG_DATA, "GATT CD discover send: %s", req.toString().c_str());
687 
688  std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, env.GATT_READ_COMMAND_REPLY_TIMEOUT); // valid reply or exception
689  COND_PRINT(env.DEBUG_DATA, "GATT CD discover recv: %s from ", pdu->toString().c_str(), deviceString.c_str());
690 
692  const AttFindInfoRsp * p = static_cast<const AttFindInfoRsp*>(pdu.get());
693  const int e_count = p->getElementCount();
694 
695  for(int e_iter=0; e_iter<e_count; e_iter++) {
696  // handle: handle of Characteristic Descriptor.
697  // value: Characteristic Descriptor UUID.
698  const uint16_t cd_handle = p->getElementHandle(e_iter);
699  std::unique_ptr<const uuid_t> cd_uuid = p->getElementValue(e_iter);
700 
701  std::shared_ptr<BTGattDesc> cd( std::make_shared<BTGattDesc>(charDecl, std::move(cd_uuid), cd_handle) );
702  if( cd_handle <= charDecl->value_handle || cd_handle > cd_handle_end ) { // should never happen!
703  ERR_PRINT("GATT discoverDescriptors CD handle %s not in range ]%s..%s]: descr%s within char%s on %s",
704  jau::to_hexstring(cd_handle).c_str(),
705  jau::to_hexstring(charDecl->value_handle).c_str(), jau::to_hexstring(cd_handle_end).c_str(),
706  cd->toString().c_str(), charDecl->toString().c_str(), deviceString.c_str());
707  done = true;
708  break;
709 
710  }
711  if( !readDescriptorValue(*cd, 0) ) {
712  WORDY_PRINT("GATT discoverDescriptors readDescriptorValue failed: req %s, descr%s within char%s on %s",
713  req.toString().c_str(), cd->toString().c_str(), charDecl->toString().c_str(), deviceString.c_str());
714  done = true;
715  break;
716  }
717  if( cd->isClientCharConfig() ) {
718  charDecl->clientCharConfigIndex = charDecl->descriptorList.size();
719  }
720  charDecl->descriptorList.push_back(cd);
721  COND_PRINT(env.DEBUG_DATA, "GATT CD discovered[%d/%d]: %s", e_iter, e_count, cd->toString().c_str());
722  }
723  cd_handle_iter = p->getElementHandle(e_count-1); // Last Descriptor Handle
724  if( cd_handle_iter < cd_handle_end ) {
725  cd_handle_iter++;
726  } else {
727  done = true; // OK by spec: End of communication
728  }
729  } else if( pdu->getOpcode() == AttPDUMsg::Opcode::ERROR_RSP ) {
730  done = true; // OK by spec: End of communication
731  } else {
732  ERR_PRINT("GATT discoverDescriptors unexpected reply %s; req %s within char%s from %s",
733  pdu->toString().c_str(), req.toString().c_str(), charDecl->toString().c_str(), deviceString.c_str());
734  done = true;
735  }
736  }
737  }
738  PERF_TS_TD("GATT discoverDescriptors");
739 
740  return service->characteristicList.size() > 0;
741 }
742 
743 bool BTGattHandler::readDescriptorValue(BTGattDesc & desc, int expectedLength) {
744  COND_PRINT(env.DEBUG_DATA, "GATTHandler::readDescriptorValue expLen %d, desc %s", expectedLength, desc.toString().c_str());
745  const bool res = readValue(desc.handle, desc.value, expectedLength);
746  if( !res ) {
747  WORDY_PRINT("GATT readDescriptorValue error on desc%s within char%s from %s",
748  desc.toString().c_str(), desc.getGattCharChecked()->toString().c_str(), deviceString.c_str());
749  }
750  return res;
751 }
752 
753 bool BTGattHandler::readCharacteristicValue(const BTGattChar & decl, POctets & resValue, int expectedLength) {
754  COND_PRINT(env.DEBUG_DATA, "GATTHandler::readCharacteristicValue expLen %d, decl %s", expectedLength, decl.toString().c_str());
755  const bool res = readValue(decl.value_handle, resValue, expectedLength);
756  if( !res ) {
757  WORDY_PRINT("GATT readCharacteristicValue error on char%s from %s", decl.toString().c_str(), deviceString.c_str());
758  }
759  return res;
760 }
761 
762 bool BTGattHandler::readValue(const uint16_t handle, POctets & res, int expectedLength) {
763  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value */
764  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value */
765  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
766  PERF2_TS_T0();
767 
768  bool done=false;
769  int offset=0;
770 
771  COND_PRINT(env.DEBUG_DATA, "GATTHandler::readValue expLen %d, handle %s from %s", expectedLength, jau::to_hexstring(handle).c_str(), deviceString.c_str());
772 
773  while(!done) {
774  if( 0 < expectedLength && expectedLength <= offset ) {
775  break; // done
776  } else if( 0 == expectedLength && 0 < offset ) {
777  break; // done w/ only one request
778  } // else 0 > expectedLength: implicit
779 
780  std::unique_ptr<const AttPDUMsg> pdu = nullptr;
781 
782  const AttReadReq req0(handle);
783  const AttReadBlobReq req1(handle, offset);
784  const AttPDUMsg & req = ( 0 == offset ) ? static_cast<const AttPDUMsg &>(req0) : static_cast<const AttPDUMsg &>(req1);
785  COND_PRINT(env.DEBUG_DATA, "GATT RV send: %s", req.toString().c_str());
786  pdu = sendWithReply(req, env.GATT_READ_COMMAND_REPLY_TIMEOUT); // valid reply or exception
787 
788  COND_PRINT(env.DEBUG_DATA, "GATT RV recv: %s from %s", pdu->toString().c_str(), deviceString.c_str());
789  if( pdu->getOpcode() == AttPDUMsg::Opcode::READ_RSP ) {
790  const AttReadRsp * p = static_cast<const AttReadRsp*>(pdu.get());
791  const TOctetSlice & v = p->getValue();
792  res += v;
793  offset += v.getSize();
794  if( p->getPDUValueSize() < p->getMaxPDUValueSize(usedMTU) ) {
795  done = true; // No full ATT_MTU PDU used - end of communication
796  }
797  } else if( pdu->getOpcode() == AttPDUMsg::Opcode::READ_BLOB_RSP ) {
798  const AttReadBlobRsp * p = static_cast<const AttReadBlobRsp*>(pdu.get());
799  const TOctetSlice & v = p->getValue();
800  if( 0 == v.getSize() ) {
801  done = true; // OK by spec: No more data - end of communication
802  } else {
803  res += v;
804  offset += v.getSize();
805  if( p->getPDUValueSize() < p->getMaxPDUValueSize(usedMTU) ) {
806  done = true; // No full ATT_MTU PDU used - end of communication
807  }
808  }
809  } else if( pdu->getOpcode() == AttPDUMsg::Opcode::ERROR_RSP ) {
810  /**
811  * BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.3 Read Long Characteristic Value
812  *
813  * If the Characteristic Value is not longer than (ATT_MTU – 1)
814  * an ATT_ERROR_RSP PDU with the error
815  * code set to Attribute Not Long shall be received on the first
816  * ATT_READ_BLOB_REQ PDU.
817  */
818  const AttErrorRsp * p = static_cast<const AttErrorRsp *>(pdu.get());
820  done = true; // OK by spec: No more data - end of communication
821  } else {
822  WORDY_PRINT("GATT readValue unexpected error %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), deviceString.c_str());
823  done = true;
824  }
825  } else {
826  ERR_PRINT("GATT readValue unexpected reply %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), deviceString.c_str());
827  done = true;
828  }
829  }
830  PERF2_TS_TD("GATT readValue");
831 
832  return offset > 0;
833 }
834 
836  /* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration */
837  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value */
838  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.11 Characteristic Value Indication */
839  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.3 Write Characteristic Descriptor */
840  COND_PRINT(env.DEBUG_DATA, "GATTHandler::writeDesccriptorValue desc %s", cd.toString().c_str());
841  const bool res = writeValue(cd.handle, cd.value, true);
842  if( !res ) {
843  WORDY_PRINT("GATT writeDescriptorValue error on desc%s within char%s from %s",
844  cd.toString().c_str(), cd.getGattCharChecked()->toString().c_str(), deviceString.c_str());
845  }
846  return res;
847 }
848 
850  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value */
851  COND_PRINT(env.DEBUG_DATA, "GATTHandler::writeCharacteristicValue desc %s, value %s", c.toString().c_str(), value.toString().c_str());
852  const bool res = writeValue(c.value_handle, value, true);
853  if( !res ) {
854  WORDY_PRINT("GATT writeCharacteristicValue error on char%s from %s", c.toString().c_str(), deviceString.c_str());
855  }
856  return res;
857 }
858 
860  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response */
861  COND_PRINT(env.DEBUG_DATA, "GATT writeCharacteristicValueNoResp decl %s, value %s", c.toString().c_str(), value.toString().c_str());
862  return writeValue(c.value_handle, value, false); // complete or exception
863 }
864 
865 bool BTGattHandler::writeValue(const uint16_t handle, const TROOctets & value, const bool withResponse) {
866  /* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration */
867  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value */
868  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.11 Characteristic Value Indication */
869  /* BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.3 Write Characteristic Descriptor */
870 
871  if( value.getSize() <= 0 ) {
872  WARN_PRINT("GATT writeValue size <= 0, no-op: %s", value.toString().c_str());
873  return false;
874  }
875  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
876 
877  // FIXME TODO: Long Value if value.getSize() > ( ATT_MTU - 3 )
878  PERF2_TS_T0();
879 
880  if( !withResponse ) {
881  AttWriteCmd req(handle, value);
882  COND_PRINT(env.DEBUG_DATA, "GATT WV send(resp %d): %s to %s", withResponse, req.toString().c_str(), deviceString.c_str());
883 
884  send( req ); // complete or exception
885  PERF2_TS_TD("GATT writeValue (no-resp)");
886  return true;
887  }
888 
889  AttWriteReq req(handle, value);
890  COND_PRINT(env.DEBUG_DATA, "GATT WV send(resp %d): %s to %s", withResponse, req.toString().c_str(), deviceString.c_str());
891 
892  bool res = false;
893  std::unique_ptr<const AttPDUMsg> pdu = sendWithReply(req, env.GATT_WRITE_COMMAND_REPLY_TIMEOUT); // valid reply or exception
894  COND_PRINT(env.DEBUG_DATA, "GATT WV recv: %s from %s", pdu->toString().c_str(), deviceString.c_str());
895 
896  if( pdu->getOpcode() == AttPDUMsg::Opcode::WRITE_RSP ) {
897  // OK
898  res = true;
899  } else if( pdu->getOpcode() == AttPDUMsg::Opcode::ERROR_RSP ) {
900  WORDY_PRINT("GATT writeValue unexpected error %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), deviceString.c_str());
901  } else {
902  ERR_PRINT("GATT writeValue unexpected reply %s; req %s from %s", pdu->toString().c_str(), req.toString().c_str(), deviceString.c_str());
903  }
904  PERF2_TS_TD("GATT writeValue (with-resp)");
905  return res;
906 }
907 
908 bool BTGattHandler::configNotificationIndication(BTGattDesc & cccd, const bool enableNotification, const bool enableIndication) {
909  if( !cccd.isClientCharConfig() ) {
910  throw jau::IllegalArgumentException("Not a ClientCharacteristicConfiguration: "+cccd.toString(), E_FILE_LINE);
911  }
912  /* BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration */
913  const uint16_t ccc_value = enableNotification | ( enableIndication << 1 );
914  COND_PRINT(env.DEBUG_DATA, "GATTHandler::configNotificationIndication decl %s, enableNotification %d, enableIndication %d",
915  cccd.toString().c_str(), enableNotification, enableIndication);
916  cccd.value.resize(2, 2);
917  cccd.value.put_uint16_nc(0, ccc_value);
918  try {
919  return writeDescriptorValue(cccd);
920  } catch (BTException & bte) {
921  if( !enableNotification && !enableIndication ) {
922  // OK to have lost connection @ disable
923  WORDY_PRINT("GATTHandler::configNotificationIndication(disable) on %s caught exception: %s", deviceString.c_str(), bte.what());
924  return false;
925  } else {
926  throw; // re-throw current exception
927  }
928  }
929 }
930 
931 /*********************************************************************************************************************/
932 /*********************************************************************************************************************/
933 /*********************************************************************************************************************/
934 
939 
950 
951 std::shared_ptr<GattGenericAccessSvc> BTGattHandler::getGenericAccess(jau::darray<BTGattCharRef> & genericAccessCharDeclList) {
952  std::shared_ptr<GattGenericAccessSvc> res = nullptr;
954  std::string deviceName = "";
956  std::shared_ptr<GattPeriphalPreferredConnectionParameters> prefConnParam = nullptr;
957 
958  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
959 
960  for(size_t i=0; i<genericAccessCharDeclList.size(); i++) {
961  const BTGattChar & charDecl = *genericAccessCharDeclList.at(i);
962  std::shared_ptr<BTGattService> service = charDecl.getServiceUnchecked();
963  if( nullptr == service || _GENERIC_ACCESS != *(service->type) ) {
964  continue;
965  }
966  if( _DEVICE_NAME == *charDecl.value_type ) {
967  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
968  deviceName = GattNameToString(value); // mandatory
969  }
970  } else if( _APPEARANCE == *charDecl.value_type ) {
971  if( readCharacteristicValue(charDecl, value.resize(0)) && value.getSize() >= 2 ) {
972  appearance = static_cast<AppearanceCat>(value.get_uint16(0)); // manatory
973  }
974  } else if( _PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS == *charDecl.value_type ) {
975  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
976  prefConnParam = GattPeriphalPreferredConnectionParameters::get(value); // optional
977  }
978  }
979  }
980  if( deviceName.size() > 0 ) {
981  res = std::make_shared<GattGenericAccessSvc>(deviceName, appearance, prefConnParam);
982  }
983  return res;
984 }
985 
986 std::shared_ptr<GattGenericAccessSvc> BTGattHandler::getGenericAccess(jau::darray<BTGattServiceRef> & primServices) {
987  std::shared_ptr<GattGenericAccessSvc> res = nullptr;
988  for(size_t i=0; i<primServices.size() && nullptr == res; i++) {
989  res = getGenericAccess(primServices.at(i)->characteristicList);
990  }
991  return res;
992 }
993 
995  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
996  bool isOK = true;
997 
998  for(size_t i=0; isOK && i<services.size(); i++) {
999  jau::darray<BTGattCharRef> & genericAccessCharDeclList = services.at(i)->characteristicList;
1000  POctets value(32, 0);
1001 
1002  for(size_t j=0; isOK && j<genericAccessCharDeclList.size(); j++) {
1003  const BTGattChar & charDecl = *genericAccessCharDeclList.at(j);
1004  std::shared_ptr<BTGattService> service = charDecl.getServiceUnchecked();
1005  if( nullptr == service || _GENERIC_ACCESS != *(service->type) ) {
1006  continue;
1007  }
1008  if( _APPEARANCE == *charDecl.value_type ) {
1009  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1010  return true; // unique success case
1011  }
1012  // read failure, but not disconnected as no exception thrown from sendWithReply
1013  isOK = false;
1014  }
1015  }
1016  }
1017  if( isOK ) {
1018  jau::INFO_PRINT("GATTHandler::pingGATT: No GENERIC_ACCESS Service with APPEARANCE Characteristic available -> disconnect");
1019  } else {
1020  jau::INFO_PRINT("GATTHandler::pingGATT: Read error -> disconnect");
1021  }
1022  disconnect(true /* disconnectDevice */, true /* ioErrorCause */); // state -> Disconnected
1023  return false;
1024 }
1025 
1026 std::shared_ptr<GattDeviceInformationSvc> BTGattHandler::getDeviceInformation(jau::darray<BTGattCharRef> & characteristicDeclList) {
1027  std::shared_ptr<GattDeviceInformationSvc> res = nullptr;
1029 
1030  POctets systemID(8, 0);
1031  std::string modelNumber;
1032  std::string serialNumber;
1033  std::string firmwareRevision;
1034  std::string hardwareRevision;
1035  std::string softwareRevision;
1036  std::string manufacturer;
1037  POctets regulatoryCertDataList(128, 0);
1038  std::shared_ptr<GattPnP_ID> pnpID = nullptr;
1039  bool found = false;
1040 
1041  const std::lock_guard<std::recursive_mutex> lock(mtx_command); // RAII-style acquire and relinquish via destructor
1042 
1043  for(size_t i=0; i<characteristicDeclList.size(); i++) {
1044  const BTGattChar & charDecl = *characteristicDeclList.at(i);
1045  std::shared_ptr<BTGattService> service = charDecl.getServiceUnchecked();
1046  if( nullptr == service || _DEVICE_INFORMATION != *(service->type) ) {
1047  continue;
1048  }
1049  found = true;
1050  if( _SYSTEM_ID == *charDecl.value_type ) {
1051  if( readCharacteristicValue(charDecl, systemID.resize(0)) ) {
1052  // nop
1053  }
1054  } else if( _REGULATORY_CERT_DATA_LIST == *charDecl.value_type ) {
1055  if( readCharacteristicValue(charDecl, regulatoryCertDataList.resize(0)) ) {
1056  // nop
1057  }
1058  } else if( _PNP_ID == *charDecl.value_type ) {
1059  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1060  pnpID = GattPnP_ID::get(value);
1061  }
1062  } else if( _MODEL_NUMBER_STRING == *charDecl.value_type ) {
1063  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1064  modelNumber = GattNameToString(value);
1065  }
1066  } else if( _SERIAL_NUMBER_STRING == *charDecl.value_type ) {
1067  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1068  serialNumber = GattNameToString(value);
1069  }
1070  } else if( _FIRMWARE_REVISION_STRING == *charDecl.value_type ) {
1071  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1072  firmwareRevision = GattNameToString(value);
1073  }
1074  } else if( _HARDWARE_REVISION_STRING == *charDecl.value_type ) {
1075  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1076  hardwareRevision = GattNameToString(value);
1077  }
1078  } else if( _SOFTWARE_REVISION_STRING == *charDecl.value_type ) {
1079  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1080  softwareRevision = GattNameToString(value);
1081  }
1082  } else if( _MANUFACTURER_NAME_STRING == *charDecl.value_type ) {
1083  if( readCharacteristicValue(charDecl, value.resize(0)) ) {
1084  manufacturer = GattNameToString(value);
1085  }
1086  }
1087  }
1088  if( found ) {
1089  res = std::make_shared<GattDeviceInformationSvc>(systemID, modelNumber, serialNumber,
1090  firmwareRevision, hardwareRevision, softwareRevision,
1091  manufacturer, regulatoryCertDataList, pnpID);
1092  }
1093  return res;
1094 }
1095 
1096 std::shared_ptr<GattDeviceInformationSvc> BTGattHandler::getDeviceInformation(jau::darray<BTGattServiceRef> & primServices) {
1097  std::shared_ptr<GattDeviceInformationSvc> res = nullptr;
1098  for(size_t i=0; i<primServices.size() && nullptr == res; i++) {
1099  res = getDeviceInformation(primServices.at(i)->characteristicList);
1100  }
1101  return res;
1102 }
1103 
direct_bt::AttReadRsp
ATT Protocol PDUs Vol 3, Part F 3.4.4.4.
Definition: ATTPDUTypes.hpp:713
_MODEL_NUMBER_STRING
static const uuid16_t _MODEL_NUMBER_STRING(GattCharacteristicType::MODEL_NUMBER_STRING)
jau::darray::at
const_reference at(size_type i) const
Like std::vector::at(size_type), immutable reference.
Definition: darray.hpp:719
direct_bt::BTGattEnv::get
static BTGattEnv & get() noexcept
Definition: BTGattHandler.hpp:115
direct_bt::TOctets::put_uint16_nc
constexpr void put_uint16_nc(const jau::nsize_t i, const uint16_t v) noexcept
Definition: OctetTypes.hpp:289
direct_bt::AttFindInfoRsp::getElementHandle
uint16_t getElementHandle(const jau::nsize_t elementIdx) const
Definition: ATTPDUTypes.hpp:1456
direct_bt::BTGattHandler::writeDescriptorValue
bool writeDescriptorValue(const BTGattDesc &cd)
BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.3 Write Characteristic Descriptors.
Definition: BTGattHandler.cpp:835
direct_bt::BTGattHandler::removeAllCharListener
int removeAllCharListener() noexcept
Remove all event listener from the list.
Definition: BTGattHandler.cpp:166
direct_bt::AttPDUMsg::toString
virtual std::string toString() const noexcept
Definition: ATTPDUTypes.hpp:541
direct_bt::AttExchangeMTU
ATT Protocol PDUs Vol 3, Part F 3.4.2.2.
Definition: ATTPDUTypes.hpp:637
_FIRMWARE_REVISION_STRING
static const uuid16_t _FIRMWARE_REVISION_STRING(GattCharacteristicType::FIRMWARE_REVISION_STRING)
direct_bt::BTGattHandler::getDeviceInformation
std::shared_ptr< GattDeviceInformationSvc > getDeviceInformation(jau::darray< BTGattServiceRef > &primServices)
Definition: BTGattHandler.cpp:1096
BTGattHandler.hpp
IRQ_PRINT
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE: '.
Definition: debug.hpp:138
direct_bt::L2CAPComm::getStateString
static std::string getStateString(bool isConnected, bool hasIOError)
Definition: L2CAPComm.hpp:120
direct_bt::AttReadByGroupTypeRsp::getElementTotalSize
constexpr_cxx20 jau::nsize_t getElementTotalSize() const noexcept override
Returns size of each element, i.e.
Definition: ATTPDUTypes.hpp:1293
direct_bt::AttHandleValueRcv::getHandle
constexpr uint16_t getHandle() const noexcept
Definition: ATTPDUTypes.hpp:980
direct_bt::TROOctets::getSize
constexpr jau::nsize_t getSize() const noexcept
Returns the used memory size for read and write operations, may be zero.
Definition: OctetTypes.hpp:118
jau::for_each_fidelity
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
Definition: basic_algos.hpp:215
direct_bt::BTGattHandler::setSendIndicationConfirmation
void setSendIndicationConfirmation(const bool v)
Enable or disable sending an immediate confirmation for received indication events from the device.
Definition: BTGattHandler.cpp:172
direct_bt::AttReadByTypeRsp
ATT Protocol PDUs Vol 3, Part F 3.4.4.2.
Definition: ATTPDUTypes.hpp:1157
direct_bt::AttFindInfoReq
ATT Protocol PDUs Vol 3, Part F 3.4.3.1.
Definition: ATTPDUTypes.hpp:1350
direct_bt::BTGattHandler::readDescriptorValue
bool readDescriptorValue(BTGattDesc &cd, int expectedLength=-1)
BT Core Spec v5.2: Vol 3, Part G GATT: 4.12.1 Read Characteristic Descriptor.
Definition: BTGattHandler.cpp:743
direct_bt::TOctets::get_wptr
uint8_t * get_wptr() noexcept
Definition: OctetTypes.hpp:383
ERR_PRINT2
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE: '.
Definition: debug.hpp:135
direct_bt::BTGattCharListener::match
virtual bool match(const BTGattChar &characteristic) noexcept
Custom filter for all event methods, which will not be called if this method returns false.
Definition: BTGattChar.hpp:448
direct_bt::HCIStatusCode::REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF
@ REMOTE_DEVICE_TERMINATED_CONNECTION_POWER_OFF
direct_bt::BTGattChar::getServiceUnchecked
std::shared_ptr< BTGattService > getServiceUnchecked() const noexcept
Definition: BTGattChar.hpp:209
jau::call_on_release
Call on release allows the user to pass a function to be called at destruction of this instance.
Definition: basic_algos.hpp:58
direct_bt::AttPDUMsg::pdu
POctets pdu
actual received PDU
Definition: ATTPDUTypes.hpp:401
direct_bt::AttPDUMsg::Opcode::READ_BY_TYPE_RSP
@ READ_BY_TYPE_RSP
basic_algos.hpp
jau::RuntimeException::what
virtual const char * what() const noexcept override
Definition: basic_types.hpp:88
BTManager.hpp
DEVICE_NAME
@ DEVICE_NAME
Definition: test_datatype02.hpp:66
direct_bt::AttReadByTypeRsp::getElementCount
constexpr_cxx20 jau::nsize_t getElementCount() const noexcept override
Definition: ATTPDUTypes.hpp:1211
jau::IllegalArgumentException
Definition: basic_types.hpp:111
direct_bt::AttPDUMsg::Opcode::ERROR_RSP
@ ERROR_RSP
direct_bt::BTGattHandler::BTGattHandler
BTGattHandler(const std::shared_ptr< BTDevice > &device, L2CAPComm &l2cap_att) noexcept
Constructing a new BTGattHandler instance with its opened and connected L2CAP channel.
Definition: BTGattHandler.cpp:277
direct_bt::AttExchangeMTU::getMTUSize
constexpr uint16_t getMTUSize() const noexcept
Definition: ATTPDUTypes.hpp:652
direct_bt::TROOctets::toString
std::string toString() const noexcept
Definition: OctetTypes.hpp:244
direct_bt::AttPDUMsg::Opcode::WRITE_RSP
@ WRITE_RSP
direct_bt::BTGattHandler::findCharacterisicsByValueHandle
BTGattCharRef findCharacterisicsByValueHandle(const uint16_t charValueHandle) noexcept
Find and return the BTGattChar within internal primary services via given characteristic value handle...
Definition: BTGattHandler.cpp:489
direct_bt::AttPDUMsg::getPDUValueSize
constexpr_cxx20 jau::nsize_t getPDUValueSize() const noexcept
Returns the octet size of the value attributes in this PDI, i.e.
Definition: ATTPDUTypes.hpp:525
direct_bt
Definition: ATTPDUTypes.hpp:171
_PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS
static const uuid16_t _PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS(GattCharacteristicType::PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS)
direct_bt::AttElementList::getElementPDUOffset
jau::nsize_t getElementPDUOffset(const jau::nsize_t elementIdx) const
Definition: ATTPDUTypes.hpp:1057
direct_bt::AttPDUMsg::Opcode::READ_BLOB_RSP
@ READ_BLOB_RSP
direct_bt::BTGattDesc::toString
virtual std::string toString() const noexcept override
Definition: BTGattDesc.cpp:82
direct_bt::BTGattChar
Definition: BTGattChar.hpp:75
direct_bt::BTGattDesc::value
POctets value
Definition: BTGattDesc.hpp:113
PERF3_TS_TD
#define PERF3_TS_TD(m)
Definition: debug.hpp:117
_DEVICE_NAME
static const uuid16_t _DEVICE_NAME(GattCharacteristicType::DEVICE_NAME)
direct_bt::BTGattEnv::DEBUG_DATA
const bool DEBUG_DATA
Debug all GATT Data communication.
Definition: BTGattHandler.hpp:112
direct_bt::AttErrorRsp::getErrorCode
constexpr ErrorCode getErrorCode() const noexcept
Definition: ATTPDUTypes.hpp:613
direct_bt::BTGattServiceRef
std::shared_ptr< BTGattService > BTGattServiceRef
Definition: BTGattChar.hpp:60
direct_bt::BTGattDesc::getGattCharChecked
std::shared_ptr< BTGattChar > getGattCharChecked() const
Definition: BTGattDesc.cpp:48
direct_bt::uuid16_t
Definition: UUID.hpp:98
direct_bt::L2CAPComm
Read/Write L2CAP communication channel.
Definition: L2CAPComm.hpp:113
direct_bt::TROOctets::get_ptr
constexpr uint8_t const * get_ptr() const noexcept
Definition: OctetTypes.hpp:228
jau
Definition: basic_algos.hpp:34
GENERIC_ACCESS
@ GENERIC_ACCESS
This service contains generic information about the device.
Definition: test_datatype02.hpp:46
PERF3_TS_T0
#define PERF3_TS_T0()
Definition: debug.hpp:116
_HARDWARE_REVISION_STRING
static const uuid16_t _HARDWARE_REVISION_STRING(GattCharacteristicType::HARDWARE_REVISION_STRING)
direct_bt::BTGattHandler::writeValue
bool writeValue(const uint16_t handle, const TROOctets &value, const bool withResponse)
Generic write GATT value and long value.
Definition: BTGattHandler.cpp:865
direct_bt::BTException
Definition: BTTypes0.hpp:43
_SERIAL_NUMBER_STRING
static const uuid16_t _SERIAL_NUMBER_STRING(GattCharacteristicType::SERIAL_NUMBER_STRING)
direct_bt::BTGattService
Representing a complete [Primary] Service Declaration including its list of Characteristic Declaratio...
Definition: BTGattService.hpp:67
direct_bt::BTGattHandler::discoverDescriptors
bool discoverDescriptors(BTGattServiceRef &service)
BT Core Spec v5.2: Vol 3, Part G GATT: 4.7.1 Discover All Characteristic Descriptors.
Definition: BTGattHandler.cpp:657
jau::cow_darray::size
constexpr_atomic size_type size() const noexcept
Like std::vector::size().
Definition: cow_darray.hpp:716
HARDWARE_REVISION_STRING
@ HARDWARE_REVISION_STRING
Definition: test_datatype02.hpp:96
_PNP_ID
static const uuid16_t _PNP_ID(GattCharacteristicType::PNP_ID)
direct_bt::BTGattHandler::readCharacteristicValue
bool readCharacteristicValue(const BTGattChar &c, POctets &res, int expectedLength=-1)
BT Core Spec v5.2: Vol 3, Part G GATT: 4.8.1 Read Characteristic Value.
Definition: BTGattHandler.cpp:753
direct_bt::AttErrorRsp::ErrorCode::UNSUPPORTED_REQUEST
@ UNSUPPORTED_REQUEST
direct_bt::AttPDUMsg::getOpcode
constexpr Opcode getOpcode() const noexcept
ATT PDU Format Vol 3, Part F 3.3.1.
Definition: ATTPDUTypes.hpp:437
jau::to_string
PRAGMA_DISABLE_WARNING_POP constexpr_cxx20 std::string to_string(const endian &v) noexcept
Return std::string representation of the given jau::endian.
Definition: byte_util.hpp:198
direct_bt::AttPDUMsg::Opcode::HANDLE_VALUE_IND
@ HANDLE_VALUE_IND
REGULATORY_CERT_DATA_LIST
@ REGULATORY_CERT_DATA_LIST
Definition: test_datatype02.hpp:99
_REGULATORY_CERT_DATA_LIST
static const uuid16_t _REGULATORY_CERT_DATA_LIST(GattCharacteristicType::REGULATORY_CERT_DATA_LIST)
SERIAL_NUMBER_STRING
@ SERIAL_NUMBER_STRING
Definition: test_datatype02.hpp:94
direct_bt::TROOctets::get_uint8
uint8_t get_uint8(const jau::nsize_t i) const
Definition: OctetTypes.hpp:120
direct_bt::BTGattDesc::isClientCharConfig
bool isClientCharConfig() const noexcept
Definition: BTGattDesc.hpp:137
direct_bt::HCIStatusCode
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:124
E_FILE_LINE
#define E_FILE_LINE
Definition: basic_types.hpp:64
direct_bt::AttErrorRsp
ATT Protocol PDUs Vol 3, Part F 3.4.1.1.
Definition: ATTPDUTypes.hpp:574
jau::darray< BTGattServiceRef >
direct_bt::BTGattHandler::readValue
bool readValue(const uint16_t handle, POctets &res, int expectedLength=-1)
Generic read GATT value and long value.
Definition: BTGattHandler.cpp:762
jau::darray::push_back
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: darray.hpp:991
direct_bt::AttReadByGroupTypeRsp
ATT Protocol PDUs Vol 3, Part F 3.4.4.10.
Definition: ATTPDUTypes.hpp:1256
direct_bt::PRIMARY_SERVICE
@ PRIMARY_SERVICE
Definition: GattTypes.hpp:65
direct_bt::BTGattHandler::getDeviceChecked
std::shared_ptr< BTDevice > getDeviceChecked() const
Definition: BTGattHandler.cpp:75
direct_bt::BTGattCharRef
std::shared_ptr< BTGattChar > BTGattCharRef
Definition: BTGattChar.hpp:409
direct_bt::AttPDUMsg
Handles the Attribute Protocol (ATT) using Protocol Data Unit (PDU) encoded messages over L2CAP chann...
Definition: ATTPDUTypes.hpp:313
APPEARANCE
@ APPEARANCE
Definition: test_datatype02.hpp:67
direct_bt::BTGattEnv::GATT_WRITE_COMMAND_REPLY_TIMEOUT
const int32_t GATT_WRITE_COMMAND_REPLY_TIMEOUT
Timeout for GATT write command replies, defaults to 500ms.
Definition: BTGattHandler.hpp:88
BTAdapter.hpp
direct_bt::AttPDUMsg::ts_creation
const uint64_t ts_creation
creation timestamp in milliseconds
Definition: ATTPDUTypes.hpp:404
direct_bt::BTGattCharListener
BTGattChar event listener for notification and indication events.
Definition: BTGattChar.hpp:435
PERF2_TS_T0
#define PERF2_TS_T0()
Definition: debug.hpp:109
direct_bt::L2CAPComm::hasIOError
bool hasIOError() const
Definition: L2CAPComm.hpp:177
direct_bt::AttReadByTypeRsp::getElementHandle
uint16_t getElementHandle(const jau::nsize_t elementIdx) const
Definition: ATTPDUTypes.hpp:1219
direct_bt::AttHandleValueCfm
ATT Protocol PDUs Vol 3, Part F 3.4.7.3.
Definition: ATTPDUTypes.hpp:1015
direct_bt::AttPDUMsg::getSpecialized
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.
Definition: ATTPDUTypes.cpp:118
direct_bt::AttReadBlobRsp::getValue
constexpr TOctetSlice const & getValue() const noexcept
Definition: ATTPDUTypes.hpp:815
direct_bt::BTGattHandler::Defaults::MAX_ATT_MTU
@ MAX_ATT_MTU
WARN_PRINT
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE: '.
Definition: debug.hpp:146
direct_bt::POctets::resize
POctets & resize(const jau::nsize_t newSize, const jau::nsize_t newCapacity)
Definition: OctetTypes.hpp:599
jau::ordered_atomic::load
CXX_ALWAYS_INLINE _Tp load() const noexcept
Definition: ordered_atomic.hpp:139
direct_bt::TROOctets::get_uint16
uint16_t get_uint16(const jau::nsize_t i) const
Definition: OctetTypes.hpp:136
equal_comparator
static bool equal_comparator(const value_type &a, const value_type &b)
Definition: test_cow_darray_perf01.cpp:229
direct_bt::BTDevice::disconnect
HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept
Disconnect the LE or BREDR peer's GATT and HCI connection.
Definition: BTDevice.cpp:1701
direct_bt::number
constexpr uint8_t number(const BDAddressType rhs) noexcept
Definition: BTAddress.hpp:67
direct_bt::AttFindInfoRsp::getElementValue
std::unique_ptr< const uuid_t > getElementValue(const jau::nsize_t elementIdx) const
Definition: ATTPDUTypes.hpp:1460
_characteristicListenerRefEqComparator
static jau::cow_darray< std::shared_ptr< BTGattCharListener > >::equal_comparator _characteristicListenerRefEqComparator
Definition: BTGattHandler.cpp:102
direct_bt::BTGattHandler::ping
bool ping()
Issues a ping to the device, validating whether it is still reachable.
Definition: BTGattHandler.cpp:994
direct_bt::L2CAPComm::read
jau::snsize_t read(uint8_t *buffer, const jau::nsize_t capacity)
Generic read, w/o locking suitable for a unique ringbuffer sink.
Definition: L2CAPComm.cpp:374
jau::darray::begin
constexpr iterator begin() noexcept
Definition: darray.hpp:606
_GENERIC_ACCESS
static const uuid16_t _GENERIC_ACCESS(GattServiceType::GENERIC_ACCESS)
jau::darray::size
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:668
jau::INFO_PRINT
void INFO_PRINT(const char *format,...) noexcept
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
Definition: debug.cpp:166
direct_bt::AttFindInfoRsp::getElementCount
jau::nsize_t getElementCount() const override
Definition: ATTPDUTypes.hpp:1448
direct_bt::TOctetSlice::getSize
constexpr jau::nsize_t getSize() const noexcept
Definition: OctetTypes.hpp:414
direct_bt::BTGattChar::PropertyBitVal
PropertyBitVal
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.1.1 Characteristic Properties.
Definition: BTGattChar.hpp:86
direct_bt::BTGattService::type
std::unique_ptr< const uuid_t > type
Service type UUID.
Definition: BTGattService.hpp:94
direct_bt::BTGattHandler::Defaults::MIN_ATT_MTU
@ MIN_ATT_MTU
jau::darray::end
constexpr iterator end() noexcept
Definition: darray.hpp:612
jau::cow_darray
Implementation of a Copy-On-Write (CoW) using jau::darray as the underlying storage,...
Definition: cow_darray.hpp:130
direct_bt::L2CAPComm::write
jau::snsize_t write(const uint8_t *buffer, const jau::nsize_t length)
Generic write, locking mutex_write().
Definition: L2CAPComm.cpp:441
direct_bt::AttReadRsp::getValue
constexpr TOctetSlice const & getValue() const noexcept
Definition: ATTPDUTypes.hpp:732
PERF2_TS_TD
#define PERF2_TS_TD(m)
Definition: debug.hpp:110
direct_bt::BTGattChar::toString
std::string toString() const noexcept override
Definition: BTGattChar.cpp:106
direct_bt::AttFindInfoRsp
ATT Protocol PDUs Vol 3, Part F 3.4.3.2.
Definition: ATTPDUTypes.hpp:1396
_SYSTEM_ID
static const uuid16_t _SYSTEM_ID(GattCharacteristicType::SYSTEM_ID)
direct_bt::AppearanceCat
AppearanceCat
Definition: BTTypes0.hpp:637
direct_bt::BTGattHandler::writeCharacteristicValueNoResp
bool writeCharacteristicValueNoResp(const BTGattChar &c, const TROOctets &value)
BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.1 Write Characteristic Value Without Response.
Definition: BTGattHandler.cpp:859
direct_bt::BTGattHandler::addCharListener
bool addCharListener(std::shared_ptr< BTGattCharListener > l)
Add the given listener to the list if not already present.
Definition: BTGattHandler.cpp:105
direct_bt::AttPDUMsg::Opcode::READ_RSP
@ READ_RSP
direct_bt::BTGattHandler
A thread safe GATT handler associated to one device via one L2CAP connection.
Definition: BTGattHandler.hpp:140
MODEL_NUMBER_STRING
@ MODEL_NUMBER_STRING
Definition: test_datatype02.hpp:93
debug.hpp
direct_bt::BTGattDesc::handle
const uint16_t handle
Characteristic Descriptor Handle.
Definition: BTGattDesc.hpp:110
direct_bt::AttReadByGroupTypeRsp::getElementEndHandle
uint16_t getElementEndHandle(const jau::nsize_t elementIdx) const
Definition: ATTPDUTypes.hpp:1319
direct_bt::TROOctets
Transient read only octet data, i.e.
Definition: OctetTypes.hpp:59
direct_bt::BTGattHandler::~BTGattHandler
~BTGattHandler() noexcept
Destructor closing this instance including L2CAP channel, see disconnect().
Definition: BTGattHandler.cpp:334
direct_bt::AttReadBlobReq
ATT Protocol PDUs Vol 3, Part F 3.4.4.5.
Definition: ATTPDUTypes.hpp:755
direct_bt::AttPDUMsg::Opcode::FIND_INFORMATION_RSP
@ FIND_INFORMATION_RSP
direct_bt::BTGattHandler::getStateString
std::string getStateString() const noexcept
Definition: BTGattHandler.hpp:255
jau::IllegalStateException
Definition: basic_types.hpp:117
PNP_ID
@ PNP_ID
Definition: test_datatype02.hpp:100
direct_bt::AttReadByNTypeReq
ATT Protocol PDUs Vol 3, Part F 3.4.4.1.
Definition: ATTPDUTypes.hpp:1096
direct_bt::AttPDUMsg::Opcode::HANDLE_VALUE_NTF
@ HANDLE_VALUE_NTF
direct_bt::AttWriteCmd
ATT Protocol PDUs Vol 3, Part F 3.4.5.3.
Definition: ATTPDUTypes.hpp:913
jau::to_hexstring
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
Definition: string_util.hpp:104
jau::snsize_t
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:56
jau::nsize_t
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:44
direct_bt::TOctetSlice
Definition: OctetTypes.hpp:399
direct_bt::GattNameToString
std::string GattNameToString(const TROOctets &v) noexcept
Converts a GATT Name (not null-terminated) UTF8 to a null-terminated C++ string.
Definition: GATTNumbers.cpp:349
jau::cow_darray::clear
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: cow_darray.hpp:751
ERR_PRINT
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE: '.
Definition: debug.hpp:132
_SOFTWARE_REVISION_STRING
static const uuid16_t _SOFTWARE_REVISION_STRING(GattCharacteristicType::SOFTWARE_REVISION_STRING)
direct_bt::BTGattHandler::getSendIndicationConfirmation
bool getSendIndicationConfirmation() noexcept
Returns whether sending an immediate confirmation for received indication events from the device is e...
Definition: BTGattHandler.cpp:176
direct_bt::BTGattHandler::getGenericAccess
std::shared_ptr< GattGenericAccessSvc > getGenericAccess() noexcept
Returns the internal kept shared GattGenericAccessSvc instance.
Definition: BTGattHandler.hpp:322
direct_bt::AttErrorRsp::ErrorCode::ATTRIBUTE_NOT_LONG
@ ATTRIBUTE_NOT_LONG
GattNumbers.hpp
direct_bt::TOctetSlice::get_ptr_nc
constexpr uint8_t const * get_ptr_nc(const jau::nsize_t i) const noexcept
Definition: OctetTypes.hpp:435
direct_bt::AttHandleValueRcv
ATT Protocol PDUs Vol 3, Part F 3.4.7.1 and 3.4.7.2.
Definition: ATTPDUTypes.hpp:965
direct_bt::L2CAPComm::isOpen
bool isOpen() const
Definition: L2CAPComm.hpp:169
direct_bt::AttReadBlobRsp
ATT Protocol PDUs Vol 3, Part F 3.4.4.6.
Definition: ATTPDUTypes.hpp:796
direct_bt::AttReadByTypeRsp::getElementTotalSize
constexpr_cxx20 jau::nsize_t getElementTotalSize() const noexcept override
Returns size of each element, i.e.
Definition: ATTPDUTypes.hpp:1197
direct_bt::AttPDUMsg::Opcode::EXCHANGE_MTU_RSP
@ EXCHANGE_MTU_RSP
direct_bt::AppearanceCat::UNKNOWN
@ UNKNOWN
direct_bt::TROOctets::get_uuid
std::unique_ptr< const uuid_t > get_uuid(const jau::nsize_t i, const uuid_t::TypeSize tsize) const
Definition: OctetTypes.hpp:223
direct_bt::BTGattChar::value_type
std::unique_ptr< const uuid_t > value_type
Definition: BTGattChar.hpp:189
direct_bt::BTGattHandler::writeCharacteristicValue
bool writeCharacteristicValue(const BTGattChar &c, const TROOctets &value)
BT Core Spec v5.2: Vol 3, Part G GATT: 4.9.3 Write Characteristic Value.
Definition: BTGattHandler.cpp:849
_APPEARANCE
static const uuid16_t _APPEARANCE(GattCharacteristicType::APPEARANCE)
DEVICE_INFORMATION
@ DEVICE_INFORMATION
This service exposes manufacturer and/or vendor information about a device.
Definition: test_datatype02.hpp:56
direct_bt::BTGattHandler::removeAllAssociatedCharListener
int removeAllAssociatedCharListener(std::shared_ptr< BTGattChar > associatedChar) noexcept
Remove all BTGattCharListener from the list, which are associated to the given BTGattChar.
Definition: BTGattHandler.cpp:137
direct_bt::BTGattHandler::discoverPrimaryServices
bool discoverPrimaryServices(std::shared_ptr< BTGattHandler > shared_this, jau::darray< BTGattServiceRef > &result)
Discover all primary services only.
Definition: BTGattHandler.cpp:528
direct_bt::BTGattHandler::disconnect
bool disconnect(const bool disconnectDevice, const bool ioErrorCause) noexcept
Disconnect this BTGattHandler and optionally the associated device.
Definition: BTGattHandler.cpp:341
direct_bt::GattPnP_ID::get
static std::shared_ptr< GattPnP_ID > get(const TROOctets &source) noexcept
Definition: GATTNumbers.cpp:391
direct_bt::BTGattChar::value_handle
const uint16_t value_handle
Characteristics Value Handle.
Definition: BTGattChar.hpp:186
direct_bt::POctets
Persistent octet data, i.e.
Definition: OctetTypes.hpp:451
_MANUFACTURER_NAME_STRING
static const uuid16_t _MANUFACTURER_NAME_STRING(GattCharacteristicType::MANUFACTURER_NAME_STRING)
MANUFACTURER_NAME_STRING
@ MANUFACTURER_NAME_STRING
Definition: test_datatype02.hpp:98
direct_bt::AttHandleValueRcv::getValue
TOctetSlice const & getValue() const noexcept
Definition: ATTPDUTypes.hpp:984
direct_bt::BTGattEnv::GATT_READ_COMMAND_REPLY_TIMEOUT
const int32_t GATT_READ_COMMAND_REPLY_TIMEOUT
Timeout for GATT read command replies, defaults to 500ms.
Definition: BTGattHandler.hpp:80
direct_bt::BTGattHandler::configNotificationIndication
bool configNotificationIndication(BTGattDesc &cd, const bool enableNotification, const bool enableIndication)
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3.3 Client Characteristic Configuration.
Definition: BTGattHandler.cpp:908
WORDY_PRINT
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:91
direct_bt::AttPDUMsg::Opcode
Opcode
ATT Opcode Summary Vol 3, Part F 3.4.8.
Definition: ATTPDUTypes.hpp:316
direct_bt::AttPDUMsg::Opcode::READ_BY_GROUP_TYPE_RSP
@ READ_BY_GROUP_TYPE_RSP
direct_bt::HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION
@ REMOTE_USER_TERMINATED_CONNECTION
SOFTWARE_REVISION_STRING
@ SOFTWARE_REVISION_STRING
Definition: test_datatype02.hpp:97
direct_bt::AttReadByGroupTypeRsp::getElementCount
constexpr_cxx20 jau::nsize_t getElementCount() const noexcept override
Definition: ATTPDUTypes.hpp:1307
direct_bt::AttWriteReq
ATT Protocol PDUs Vol 3, Part F 3.4.5.1.
Definition: ATTPDUTypes.hpp:841
PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS
@ PERIPHERAL_PREFERRED_CONNECTION_PARAMETERS
Definition: test_datatype02.hpp:70
direct_bt::BTGattHandler::number
static constexpr int number(const Defaults d)
Definition: BTGattHandler.hpp:149
direct_bt::GattPeriphalPreferredConnectionParameters::get
static std::shared_ptr< GattPeriphalPreferredConnectionParameters > get(const TROOctets &source) noexcept
Definition: GATTNumbers.cpp:366
_DEVICE_INFORMATION
static const uuid16_t _DEVICE_INFORMATION(GattServiceType::DEVICE_INFORMATION)
PERF_TS_TD
#define PERF_TS_TD(m)
Definition: debug.hpp:103
FIRMWARE_REVISION_STRING
@ FIRMWARE_REVISION_STRING
Definition: test_datatype02.hpp:95
BTDevice.hpp
COND_PRINT
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
Definition: debug.hpp:165
SYSTEM_ID
@ SYSTEM_ID
Mandatory: uint40.
Definition: test_datatype02.hpp:92
direct_bt::AttReadReq
ATT Protocol PDUs Vol 3, Part F 3.4.4.3.
Definition: ATTPDUTypes.hpp:675
direct_bt::uuid_t::toTypeSize
static TypeSize toTypeSize(const jau::nsize_t size)
Definition: UUID.cpp:38
direct_bt::BTGattDesc
BT Core Spec v5.2: Vol 3, Part G GATT: 3.3.3 Characteristic Descriptor.
Definition: BTGattDesc.hpp:62
direct_bt::BTGattHandler::discoverCompletePrimaryServices
jau::darray< BTGattServiceRef > & discoverCompletePrimaryServices(std::shared_ptr< BTGattHandler > shared_this)
Discover all primary services and all its characteristics declarations including their client config.
Definition: BTGattHandler.cpp:513
direct_bt::AttPDUMsg::getMaxPDUValueSize
constexpr_cxx20 jau::nsize_t getMaxPDUValueSize(const jau::nsize_t mtu) const noexcept
Returns the theoretical maximum value size of a PDU.
Definition: ATTPDUTypes.hpp:533
jau::darray::clear
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: darray.hpp:796
direct_bt::AttPDUMsg::Opcode::MULTIPLE_HANDLE_VALUE_NTF
@ MULTIPLE_HANDLE_VALUE_NTF
DBG_PRINT
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:78
jau::cow_darray::push_back_unique
constexpr_atomic bool push_back_unique(const value_type &x, equal_comparator comparator)
Like std::vector::push_back(), but only if the newly added element does not yet exist.
Definition: cow_darray.hpp:948
PERF_TS_T0
#define PERF_TS_T0()
Definition: debug.hpp:102
direct_bt::BTGattHandler::removeCharListener
bool removeCharListener(std::shared_ptr< BTGattCharListener > l) noexcept
Remove the given listener from the list.
Definition: BTGattHandler.cpp:112
direct_bt::BTGattHandler::discoverCharacteristics
bool discoverCharacteristics(BTGattServiceRef &service)
Discover all characteristics of a service and declaration attributes only.
Definition: BTGattHandler.cpp:592
direct_bt::CHARACTERISTIC
@ CHARACTERISTIC
Definition: GattTypes.hpp:69