Direct-BT  2.3.1
Direct-BT - Direct Bluetooth Programming.
BTAdapter.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 #include <jau/debug.hpp>
35 
36 #include <jau/basic_algos.hpp>
37 
38 #include "BTIoctl.hpp"
39 #include "HCIIoctl.hpp"
40 #include "HCIComm.hpp"
41 
42 #include "BTAdapter.hpp"
43 #include "BTManager.hpp"
44 
45 extern "C" {
46  #include <inttypes.h>
47  #include <unistd.h>
48  #include <poll.h>
49 }
50 
51 using namespace direct_bt;
52 
53 constexpr static const bool _print_device_lists = false;
54 
55 std::shared_ptr<BTDevice> BTAdapter::findDevice(device_list_t & devices, const EUI48 & address, const BDAddressType addressType) noexcept {
56  const jau::nsize_t size = devices.size();
57  for (jau::nsize_t i = 0; i < size; ++i) {
58  std::shared_ptr<BTDevice> & e = devices[i];
59  if ( nullptr != e && address == e->getAddressAndType().address && addressType == e->getAddressAndType().type) {
60  return e;
61  }
62  }
63  return nullptr;
64 }
65 
66 std::shared_ptr<BTDevice> BTAdapter::findDevice(device_list_t & devices, BTDevice const & device) noexcept {
67  const jau::nsize_t size = devices.size();
68  for (jau::nsize_t i = 0; i < size; ++i) {
69  std::shared_ptr<BTDevice> & e = devices[i];
70  if ( nullptr != e && device == *e ) {
71  return e;
72  }
73  }
74  return nullptr;
75 }
76 
77 bool BTAdapter::addConnectedDevice(const std::shared_ptr<BTDevice> & device) noexcept {
78  const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
79  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
80  if( nullptr != findDevice(connectedDevices, *device) ) {
81  return false;
82  }
83  connectedDevices.push_back(device);
84  return true;
85 }
86 
87 bool BTAdapter::removeConnectedDevice(const BTDevice & device) noexcept {
88  const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
89  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
90  auto end = connectedDevices.end();
91  for (auto it = connectedDevices.begin(); it != end; ++it) {
92  if ( nullptr != *it && device == **it ) {
93  connectedDevices.erase(it);
94  return true;
95  }
96  }
97  return false;
98 }
99 
100 int BTAdapter::disconnectAllDevices(const HCIStatusCode reason) noexcept {
101  device_list_t devices;
102  {
103  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
104  devices = connectedDevices; // copy!
105  }
106  const int count = devices.size();
107  auto end = devices.end();
108  for (auto it = devices.begin(); it != end; ++it) {
109  if( nullptr != *it ) {
110  (*it)->disconnect(reason); // will erase device from list via removeConnectedDevice(..) above
111  }
112  }
113  return count;
114 }
115 
116 std::shared_ptr<BTDevice> BTAdapter::findConnectedDevice (const EUI48 & address, const BDAddressType & addressType) noexcept {
117  const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
118  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
119  return findDevice(connectedDevices, address, addressType);
120 }
121 
122 // *************************************************
123 // *************************************************
124 // *************************************************
125 
126 bool BTAdapter::updateDataFromHCI() noexcept {
127  HCILocalVersion version;
128  HCIStatusCode status = hci.getLocalVersion(version);
129  if( HCIStatusCode::SUCCESS != status ) {
130  ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: POWERED, LocalVersion failed %s - %s",
131  dev_id, to_string(status).c_str(), adapterInfo.toString().c_str());
132  return false;
133  }
134  LE_Features le_ll_feats;
135  if( HCIStatusCode::SUCCESS != hci.le_read_local_features(le_ll_feats) ) {
136  ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: le_read_local_features failed %s - %s",
137  dev_id, to_string(status).c_str(), adapterInfo.toString().c_str());
138  return false;
139  }
140  le_features = le_ll_feats;
141  hci_uses_ext_scan = hci.use_ext_scan();
142  hci_uses_ext_conn = hci.use_ext_conn();
143 
144  WORDY_PRINT("BTAdapter::updateDataFromHCI: Adapter[%d]: POWERED, %s - %s, hci_ext[scan %d, conn %d], features: %s",
145  dev_id, version.toString().c_str(), adapterInfo.toString().c_str(),
146  hci_uses_ext_scan, hci_uses_ext_conn,
147  direct_bt::to_string(le_ll_feats).c_str());
148  return true;
149 }
150 
151 bool BTAdapter::validateDevInfo() noexcept {
152  bool ok = false;
153  currentMetaScanType = ScanType::NONE;
154  keep_le_scan_alive = false;
155 
156  if( !mgmt.isOpen() ) {
157  ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: Manager not open", dev_id);
158  goto errout0;
159  }
160  if( !hci.isOpen() ) {
161  ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: HCIHandler closed", dev_id);
162  goto errout0;
163  }
164 
165  old_settings = adapterInfo.getCurrentSettingMask();
166 
167  btMode = getBTMode();
168  if( BTMode::NONE == btMode ) {
169  ERR_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: BTMode invalid, BREDR nor LE set: %s", dev_id, adapterInfo.toString().c_str());
170  return false;
171  }
172  hci.setBTMode(btMode);
173 
175  if( !updateDataFromHCI() ) {
176  return false;
177  }
178  } else {
179  WORDY_PRINT("BTAdapter::validateDevInfo: Adapter[%d]: Not POWERED: %s", dev_id, adapterInfo.toString().c_str());
180  }
181 
182  ok = true;
183  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DISCOVERING, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceDiscoveringMgmt)) && ok;
184  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_SETTINGS, jau::bindMemberFunc(this, &BTAdapter::mgmtEvNewSettingsMgmt)) && ok;
185  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::LOCAL_NAME_CHANGED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvLocalNameChangedMgmt)) && ok;
186  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::PIN_CODE_REQUEST, jau::bindMemberFunc(this, &BTAdapter::mgmtEvPinCodeRequestMgmt));
187  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::USER_CONFIRM_REQUEST, jau::bindMemberFunc(this, &BTAdapter::mgmtEvUserConfirmRequestMgmt));
188  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::USER_PASSKEY_REQUEST, jau::bindMemberFunc(this, &BTAdapter::mgmtEvUserPasskeyRequestMgmt));
189  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::AUTH_FAILED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvAuthFailedMgmt));
190  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_UNPAIRED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceUnpairedMgmt));
191  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::PAIR_DEVICE_COMPLETE, jau::bindMemberFunc(this, &BTAdapter::mgmtEvPairDeviceCompleteMgmt));
192  ok = mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_LONG_TERM_KEY, jau::bindMemberFunc(this, &BTAdapter::mgmtEvNewLongTermKeyMgmt));
193 
194  if( !ok ) {
195  ERR_PRINT("Could not add all required MgmtEventCallbacks to DBTManager: %s", toString().c_str());
196  return false;
197  }
198 
199 #if 0
200  mgmt.addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_DISCONNECTED, bindMemberFunc(this, &BTAdapter::mgmtEvDeviceDisconnectedMgmt));
201 #endif
202 
203  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DISCOVERING, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceDiscoveringHCI)) && ok;
204  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_CONNECTED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceConnectedHCI)) && ok;
205  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::CONNECT_FAILED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvConnectFailedHCI)) && ok;
206  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceDisconnectedHCI)) && ok;
207  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_FOUND, jau::bindMemberFunc(this, &BTAdapter::mgmtEvDeviceFoundHCI)) && ok;
208  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_REMOTE_USR_FEATURES, jau::bindMemberFunc(this, &BTAdapter::mgmtEvHCILERemoteUserFeaturesHCI)) && ok;
209  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_CHANGED, jau::bindMemberFunc(this, &BTAdapter::mgmtEvHCIEncryptionChangedHCI)) && ok;
210  ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_KEY_REFRESH_COMPLETE, jau::bindMemberFunc(this, &BTAdapter::mgmtEvHCIEncryptionKeyRefreshCompleteHCI)) && ok;
211 
212  if( !ok ) {
213  ERR_PRINT("Could not add all required MgmtEventCallbacks to HCIHandler: %s of %s", hci.toString().c_str(), toString().c_str());
214  return false; // dtor local HCIHandler w/ closing
215  }
216  hci.addSMPMsgCallback(jau::bindMemberFunc(this, &BTAdapter::hciSMPMsgCallback));
217 
218  return true;
219 
220 errout0:
221  return false;
222 }
223 
224 BTAdapter::BTAdapter(const BTAdapter::ctor_cookie& cc, BTManager& mgmt_, const AdapterInfo& adapterInfo_) noexcept
225 : debug_event(jau::environment::getBooleanProperty("direct_bt.debug.adapter.event", false)),
226  debug_lock(jau::environment::getBooleanProperty("direct_bt.debug.adapter.lock", false)),
227  mgmt( mgmt_ ),
228  adapterInfo( adapterInfo_ ),
229  le_features( LE_Features::NONE ),
230  visibleAddressAndType( adapterInfo_.addressAndType ),
231  dev_id( adapterInfo.dev_id ),
232  hci( dev_id )
233 {
234  (void)cc;
235  valid = validateDevInfo();
236 }
237 
239  if( !isValid() ) {
240  DBG_PRINT("BTAdapter::dtor: dev_id %d, invalid, %p", dev_id, this);
241  mgmt.removeAdapter(this); // remove this instance from manager
242  return;
243  }
244  DBG_PRINT("BTAdapter::dtor: ... %p %s", this, toString().c_str());
245  close();
246 
247  mgmt.removeAdapter(this); // remove this instance from manager
248 
249  DBG_PRINT("BTAdapter::dtor: XXX");
250 }
251 
252 void BTAdapter::close() noexcept {
253  if( !isValid() ) {
254  // Native user app could have destroyed this instance already from
255  DBG_PRINT("BTAdapter::close: dev_id %d, invalid, %p", dev_id, this);
256  return;
257  }
258  DBG_PRINT("BTAdapter::close: ... %p %s", this, toString().c_str());
259  keep_le_scan_alive = false;
260 
261  // mute all listener first
262  {
263  int count = mgmt.removeMgmtEventCallback(dev_id);
264  DBG_PRINT("BTAdapter::close removeMgmtEventCallback: %d callbacks", count);
265  }
266  statusListenerList.clear();
267 
268  poweredOff();
269 
270  DBG_PRINT("BTAdapter::close: closeHCI: ...");
271  hci.close();
272  DBG_PRINT("BTAdapter::close: closeHCI: XXX");
273 
274  {
275  const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
276  discoveredDevices.clear();
277  }
278  {
279  const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
280  connectedDevices.clear();;
281  }
282  {
283  const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
284  sharedDevices.clear();
285  }
286  valid = false;
287  DBG_PRINT("BTAdapter::close: XXX");
288 }
289 
290 void BTAdapter::poweredOff() noexcept {
291  if( !isValid() ) {
292  DBG_PRINT("BTAdapter::poweredOff: dev_id %d, invalid, %p", dev_id, this);
293  return;
294  }
295  DBG_PRINT("BTAdapter::poweredOff: ... %p %s", this, toString().c_str());
296  keep_le_scan_alive = false;
297 
298  stopDiscovery();
299 
300  // Removes all device references from the lists: connectedDevices, discoveredDevices, sharedDevices
301  disconnectAllDevices();
303 
305  currentMetaScanType = ScanType::NONE;
306 
307  // ensure all hci states are reset.
308  hci.resetAllStates(false);
309 
310  unlockConnectAny();
311 
312  DBG_PRINT("BTAdapter::poweredOff: XXX");
313 }
314 
315 void BTAdapter::printDeviceList(const std::string& prefix, const BTAdapter::device_list_t& list) noexcept {
316  const size_t sz = list.size();
317  jau::PLAIN_PRINT(true, "- BTAdapter::%s: %zu elements", prefix.c_str(), sz);
318  int idx = 0;
319  for (auto it = list.begin(); it != list.end(); ++idx, ++it) {
320  jau::PLAIN_PRINT(true, " - %d / %zu: %s, name '%s'", (idx+1), sz,
321  (*it)->getAddressAndType().toString().c_str(),
322  (*it)->getName().c_str() );
323  }
324 }
326  device_list_t _sharedDevices, _discoveredDevices, _connectedDevices;
327  {
328  jau::sc_atomic_critical sync(sync_data); // lock-free simple cache-load 'snapshot'
329  // Using an expensive copy here: debug mode only
330  _sharedDevices = sharedDevices;
331  _discoveredDevices = discoveredDevices;
332  _connectedDevices = connectedDevices;
333  }
334  printDeviceList("SharedDevices ", _sharedDevices);
335  printDeviceList("ConnectedDevices ", _connectedDevices);
336  printDeviceList("DiscoveredDevices ", _discoveredDevices);
338 }
339 
341  auto begin = statusListenerList.begin(); // lock mutex and copy_store
342  jau::PLAIN_PRINT(true, "- BTAdapter::StatusListener : %zu elements", (size_t)begin.size());
343  for(int ii=0; !begin.is_end(); ++ii, ++begin ) {
344  jau::PLAIN_PRINT(true, " - %d / %zu: %s", (ii+1), (size_t)begin.size(), begin->listener->toString().c_str());
345  }
346 }
347 
348 std::shared_ptr<NameAndShortName> BTAdapter::setLocalName(const std::string &name, const std::string &short_name) noexcept {
349  return mgmt.setLocalName(dev_id, name, short_name);
350 }
351 
352 bool BTAdapter::setDiscoverable(bool value) noexcept {
353  AdapterSetting current_settings { AdapterSetting::NONE } ;
354  return MgmtStatus::SUCCESS == mgmt.setDiscoverable(dev_id, value ? 0x01 : 0x00, 10 /* timeout seconds */, current_settings);
355 }
356 
357 bool BTAdapter::setBondable(bool value) noexcept {
358  AdapterSetting current_settings { AdapterSetting::NONE } ;
359  return mgmt.setMode(dev_id, MgmtCommand::Opcode::SET_BONDABLE, value ? 1 : 0, current_settings);
360 }
361 
362 bool BTAdapter::setPowered(bool value) noexcept {
363  AdapterSetting current_settings { AdapterSetting::NONE } ;
364  return mgmt.setMode(dev_id, MgmtCommand::Opcode::SET_POWERED, value ? 1 : 0, current_settings);
365 }
366 
367 bool BTAdapter::lockConnect(const BTDevice & device, const bool wait, const SMPIOCapability io_cap) noexcept {
368  std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
369  const uint32_t timeout_ms = 10000; // FIXME: Configurable?
370 
371  if( nullptr != single_conn_device_ptr ) {
372  if( device == *single_conn_device_ptr ) {
373  COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: Already locked, same device: %s", device.toString().c_str());
374  return true; // already set, same device: OK, locked
375  }
376  if( wait ) {
377  while( nullptr != single_conn_device_ptr ) {
378  std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
379  std::cv_status s = cv_single_conn_device.wait_until(lock, t0 + std::chrono::milliseconds(timeout_ms));
380  if( std::cv_status::timeout == s && nullptr != single_conn_device_ptr ) {
381  if( debug_lock ) {
382  jau::PLAIN_PRINT(true, "BTAdapter::lockConnect: Failed: Locked (waited)");
383  jau::PLAIN_PRINT(true, " - locked-by-other-device %s", single_conn_device_ptr->toString().c_str());
384  jau::PLAIN_PRINT(true, " - lock-failed-for %s", device.toString().c_str());
385  }
386  return false;
387  }
388  }
389  // lock was released
390  } else {
391  if( debug_lock ) {
392  jau::PLAIN_PRINT(true, "BTAdapter::lockConnect: Failed: Locked (no-wait)");
393  jau::PLAIN_PRINT(true, " - locked-by-other-device %s", single_conn_device_ptr->toString().c_str());
394  jau::PLAIN_PRINT(true, " - lock-failed-for %s", device.toString().c_str());
395  }
396  return false; // already set, not waiting, blocked
397  }
398  }
399  single_conn_device_ptr = &device;
400 
401  if( SMPIOCapability::UNSET != io_cap ) {
402 #if USE_LINUX_BT_SECURITY
404  const bool res_iocap = mgmt.setIOCapability(dev_id, io_cap, pre_io_cap);
405  if( res_iocap ) {
406  iocap_defaultval = pre_io_cap;
407  COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: New lock, setIOCapability[%s -> %s], %s",
408  to_string(pre_io_cap).c_str(), to_string(io_cap).c_str(),
409  device.toString().c_str());
410  return true;
411  } else {
412  // failed, unlock and exit
413  COND_PRINT(debug_lock, "BTAdapter::lockConnect: Failed: setIOCapability[%s], %s",
414  to_string(io_cap).c_str(), device.toString().c_str());
415  single_conn_device_ptr = nullptr;
416  cv_single_conn_device.notify_all(); // notify waiting getter
417  return false;
418  }
419 #else
420  COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: New lock, ignored io-cap: %s, %s",
421  to_string(io_cap).c_str()
422  device.toString().c_str());
423  return true;
424 #endif
425  } else {
426  COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: New lock, no io-cap: %s", device.toString().c_str());
427  return true;
428  }
429 }
430 
431 bool BTAdapter::unlockConnect(const BTDevice & device) noexcept {
432  std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
433 
434  if( nullptr != single_conn_device_ptr && device == *single_conn_device_ptr ) {
435  const SMPIOCapability v = iocap_defaultval;
436  iocap_defaultval = SMPIOCapability::UNSET;
437  if( SMPIOCapability::UNSET != v ) {
438  // Unreachable: !USE_LINUX_BT_SECURITY
439  SMPIOCapability o;
440  const bool res = mgmt.setIOCapability(dev_id, v, o);
441  COND_PRINT(debug_lock, "BTAdapter::unlockConnect: Success: setIOCapability[res %d: %s -> %s], %s",
442  res, to_string(o).c_str(), to_string(v).c_str(),
443  single_conn_device_ptr->toString().c_str());
444  } else {
445  COND_PRINT(debug_lock, "BTAdapter::unlockConnect: Success: %s",
446  single_conn_device_ptr->toString().c_str());
447  }
448  single_conn_device_ptr = nullptr;
449  cv_single_conn_device.notify_all(); // notify waiting getter
450  return true;
451  } else {
452  if( debug_lock ) {
453  const std::string other_device_str = nullptr != single_conn_device_ptr ? single_conn_device_ptr->toString() : "null";
454  jau::PLAIN_PRINT(true, "BTAdapter::unlockConnect: Not locked:");
455  jau::PLAIN_PRINT(true, " - locked-by-other-device %s", other_device_str.c_str());
456  jau::PLAIN_PRINT(true, " - unlock-failed-for %s", device.toString().c_str());
457  }
458  return false;
459  }
460 }
461 
462 bool BTAdapter::unlockConnectAny() noexcept {
463  std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
464 
465  if( nullptr != single_conn_device_ptr ) {
466  const SMPIOCapability v = iocap_defaultval;
467  iocap_defaultval = SMPIOCapability::UNSET;
468  if( SMPIOCapability::UNSET != v ) {
469  // Unreachable: !USE_LINUX_BT_SECURITY
470  SMPIOCapability o;
471  const bool res = mgmt.setIOCapability(dev_id, v, o);
472  COND_PRINT(debug_lock, "BTAdapter::unlockConnectAny: Success: setIOCapability[res %d: %s -> %s]; %s",
473  res, to_string(o).c_str(), to_string(v).c_str(),
474  single_conn_device_ptr->toString().c_str());
475  } else {
476  COND_PRINT(debug_lock, "BTAdapter::unlockConnectAny: Success: %s",
477  single_conn_device_ptr->toString().c_str());
478  }
479  single_conn_device_ptr = nullptr;
480  cv_single_conn_device.notify_all(); // notify waiting getter
481  return true;
482  } else {
483  iocap_defaultval = SMPIOCapability::UNSET;
484  COND_PRINT(debug_lock, "BTAdapter::unlockConnectAny: Not locked");
485  return false;
486  }
487 }
488 
490  if( !isValid() ) {
491  ERR_PRINT("BTAdapter::reset(): Adapter invalid: %s, %s", to_hexstring(this).c_str(), toString().c_str());
493  }
494  if( !hci.isOpen() ) {
495  ERR_PRINT("BTAdapter::reset(): HCI closed: %s, %s", to_hexstring(this).c_str(), toString().c_str());
497  }
498 #if 0
499  const bool wasPowered = isPowered();
500  // Adapter will be reset, close connections and cleanup off-thread.
501  preReset();
502 
503  HCIStatusCode status = hci->reset();
504  if( HCIStatusCode::SUCCESS != status ) {
505  ERR_PRINT("BTAdapter::reset: reset failed: %s", to_string(status).c_str());
506  } else if( wasPowered ) {
507  if( !setPowered(true) ) {
508  ERR_PRINT("BTAdapter::reset: setPowered(true) failed");
510  }
511  }
512  return status;
513 #else
514  return hci.resetAdapter();
515 #endif
516 }
517 
518 bool BTAdapter::isDeviceWhitelisted(const BDAddressAndType & addressAndType) noexcept {
519  return mgmt.isDeviceWhitelisted(dev_id, addressAndType);
520 }
521 
523  const uint16_t conn_interval_min, const uint16_t conn_interval_max,
524  const uint16_t conn_latency, const uint16_t timeout) {
525  if( !isPowered() ) {
526  ERR_PRINT("BTAdapter::startDiscovery: Adapter not powered: %s", toString().c_str());
527  return false;
528  }
529  if( mgmt.isDeviceWhitelisted(dev_id, addressAndType) ) {
530  ERR_PRINT("BTAdapter::addDeviceToWhitelist: device already listed: dev_id %d, address%s", dev_id, addressAndType.toString().c_str());
531  return true;
532  }
533 
534  if( !mgmt.uploadConnParam(dev_id, addressAndType, conn_interval_min, conn_interval_max, conn_latency, timeout) ) {
535  ERR_PRINT("BTAdapter::addDeviceToWhitelist: uploadConnParam(dev_id %d, address%s, interval[%u..%u], latency %u, timeout %u): Failed",
536  dev_id, addressAndType.toString().c_str(), conn_interval_min, conn_interval_max, conn_latency, timeout);
537  }
538  return mgmt.addDeviceToWhitelist(dev_id, addressAndType, ctype);
539 }
540 
542  return mgmt.removeDeviceFromWhitelist(dev_id, addressAndType);
543 }
544 
546  [](const impl::StatusListenerPair &a, const impl::StatusListenerPair &b) -> bool { return *a.listener == *b.listener; };
547 
548 bool BTAdapter::addStatusListener(std::shared_ptr<AdapterStatusListener> l) {
549  if( nullptr == l ) {
550  throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE);
551  }
552  const bool added = statusListenerList.push_back_unique(impl::StatusListenerPair{l, std::weak_ptr<BTDevice>{} },
554  if( added ) {
555  sendAdapterSettingsInitial(*l, jau::getCurrentMilliseconds());
556  }
557  if( _print_device_lists || jau::environment::get().verbose ) {
558  jau::PLAIN_PRINT(true, "BTAdapter::addStatusListener.1: added %d, %s", added, toString().c_str());
560  }
561  return added;
562 }
563 
564 bool BTAdapter::addStatusListener(const BTDevice& d, std::shared_ptr<AdapterStatusListener> l) {
565  if( nullptr == l ) {
566  throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE);
567  }
568 
569  std::shared_ptr<BTDevice> sd = getSharedDevice(d);
570  if( nullptr == sd ) {
571  throw jau::IllegalArgumentException("Device not shared: "+d.toString(), E_FILE_LINE);
572  }
573  const bool added = statusListenerList.push_back_unique(impl::StatusListenerPair{l, sd},
575  if( added ) {
576  sendAdapterSettingsInitial(*l, jau::getCurrentMilliseconds());
577  }
578  if( _print_device_lists || jau::environment::get().verbose ) {
579  jau::PLAIN_PRINT(true, "BTAdapter::addStatusListener.2: added %d, %s", added, toString().c_str());
581  }
582  return added;
583 }
584 
585 bool BTAdapter::removeStatusListener(std::shared_ptr<AdapterStatusListener> l) {
586  if( nullptr == l ) {
587  throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE);
588  }
589  const int count = statusListenerList.erase_matching(impl::StatusListenerPair{l, std::weak_ptr<BTDevice>{}},
590  false /* all_matching */,
592  if( _print_device_lists || jau::environment::get().verbose ) {
593  jau::PLAIN_PRINT(true, "BTAdapter::removeStatusListener.1: res %d, %s", count>0, toString().c_str());
595  }
596  return count > 0;
597 }
598 
600  if( nullptr == l ) {
601  throw jau::IllegalArgumentException("AdapterStatusListener ref is null", E_FILE_LINE);
602  }
603  bool res = false;
604  {
605  auto begin = statusListenerList.begin(); // lock mutex and copy_store
606  while ( !begin.is_end() ) {
607  if ( *begin->listener == *l ) {
608  begin.erase();
609  res = true;
610  break;
611  } else {
612  ++begin;
613  }
614  }
615  if( res ) {
616  begin.write_back();
617  }
618  }
619  if( _print_device_lists || jau::environment::get().verbose ) {
620  jau::PLAIN_PRINT(true, "BTAdapter::removeStatusListener.2: res %d, %s", res, toString().c_str());
622  }
623  return res;
624 }
625 
627  int count = 0;
628 
629  int res = statusListenerList.size();
630  if( 0 < res ) {
631  auto begin = statusListenerList.begin();
632  auto it = begin.end();
633  do {
634  --it;
635  std::shared_ptr<BTDevice> sda = it->wbr_device.lock();
636  if ( nullptr != sda && *sda == d ) {
637  it.erase();
638  ++count;
639  }
640  } while( it != begin );
641 
642  if( 0 < count ) {
643  begin.write_back();
644  }
645  }
646  return count;
647 }
648 
650  int count = statusListenerList.size();
651  statusListenerList.clear();
652  return count;
653 }
654 
655 void BTAdapter::checkDiscoveryState() noexcept {
656  const ScanType currentNativeScanType = hci.getCurrentScanType();
657  // Check LE scan state
658  if( keep_le_scan_alive == false ) {
659  if( hasScanType(currentMetaScanType, ScanType::LE) != hasScanType(currentNativeScanType, ScanType::LE) ) {
660  std::string msg("Invalid DiscoveryState: keepAlive "+std::to_string(keep_le_scan_alive.load())+
661  ", currentScanType*[native "+
662  to_string(currentNativeScanType)+" != meta "+
663  to_string(currentMetaScanType)+"]");
664  ERR_PRINT(msg.c_str());
665  // ABORT?
666  }
667  } else {
668  if( !hasScanType(currentMetaScanType, ScanType::LE) && hasScanType(currentNativeScanType, ScanType::LE) ) {
669  std::string msg("Invalid DiscoveryState: keepAlive "+std::to_string(keep_le_scan_alive.load())+
670  ", currentScanType*[native "+
671  to_string(currentNativeScanType)+", meta "+
672  to_string(currentMetaScanType)+"]");
673  ERR_PRINT(msg.c_str());
674  // ABORT?
675  }
676  }
677 }
678 
679 HCIStatusCode BTAdapter::startDiscovery(const bool keepAlive, const bool le_scan_active,
680  const uint16_t le_scan_interval, const uint16_t le_scan_window,
681  const uint8_t filter_policy)
682 {
683  // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to setup BREDR, LE or DUAL scanning!
684  // ERR_PRINT("Test");
685  // throw jau::RuntimeException("Test", E_FILE_LINE);
686 
687  if( !isPowered() ) {
688  WARN_PRINT("BTAdapter::startDiscovery: Adapter not powered: %s", toString(true).c_str());
690  }
691  const std::lock_guard<std::mutex> lock(mtx_discovery); // RAII-style acquire and relinquish via destructor
692 
693  const ScanType currentNativeScanType = hci.getCurrentScanType();
694 
695  if( hasScanType(currentNativeScanType, ScanType::LE) ) {
697  if( keep_le_scan_alive == keepAlive ) {
698  DBG_PRINT("BTAdapter::startDiscovery: Already discovering, unchanged keepAlive %d -> %d, currentScanType[native %s, meta %s] ...\n- %s",
699  keep_le_scan_alive.load(), keepAlive,
700  to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(), toString(true).c_str());
701  } else {
702  DBG_PRINT("BTAdapter::startDiscovery: Already discovering, changed keepAlive %d -> %d, currentScanType[native %s, meta %s] ...\n- %s",
703  keep_le_scan_alive.load(), keepAlive,
704  to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(), toString(true).c_str());
705  keep_le_scan_alive = keepAlive;
706  }
707  checkDiscoveryState();
708  return HCIStatusCode::SUCCESS;
709  }
710 
711  if( _print_device_lists || jau::environment::get().verbose ) {
712  jau::PLAIN_PRINT(true, "BTAdapter::startDiscovery: Start: keepAlive %d -> %d, currentScanType[native %s, meta %s] ...\n- %s",
713  keep_le_scan_alive.load(), keepAlive,
714  to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(), toString().c_str());
715  }
716 
718  keep_le_scan_alive = keepAlive;
719 
720  // TODO: Potential changing adapter address mode to random and updating 'visibleAddressAndType'!
721  const BDAddressType usedAddrType = BDAddressType::BDADDR_LE_PUBLIC;
722  const HCILEOwnAddressType own_mac_type=to_HCILEOwnAddressType(usedAddrType);
723  // if le_enable_scan(..) is successful, it will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType.
724  const HCIStatusCode status = hci.le_start_scan(true /* filter_dup */, le_scan_active, own_mac_type,
726 
727  if( _print_device_lists || jau::environment::get().verbose ) {
728  jau::PLAIN_PRINT(true, "BTAdapter::startDiscovery: End: Result %s, keepAlive %d -> %d, currentScanType[native %s, meta %s] ...\n- %s",
729  to_string(status).c_str(), keep_le_scan_alive.load(), keepAlive,
730  to_string(hci.getCurrentScanType()).c_str(), to_string(currentMetaScanType).c_str(), toString().c_str());
732  }
733 
734  checkDiscoveryState();
735 
736  return status;
737 }
738 
739 void BTAdapter::startDiscoveryBackground() noexcept {
740  // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to setup BREDR, LE or DUAL scanning!
741  if( !isPowered() ) {
742  WARN_PRINT("BTAdapter::startDiscoveryBackground: Adapter not powered: %s", toString(true).c_str());
743  return;
744  }
745  const std::lock_guard<std::mutex> lock(mtx_discovery); // RAII-style acquire and relinquish via destructor
746  if( !hasScanType(hci.getCurrentScanType(), ScanType::LE) && keep_le_scan_alive ) { // still?
747  // if le_enable_scan(..) is successful, it will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType.
748  const HCIStatusCode status = hci.le_enable_scan(true /* enable */);
749  if( HCIStatusCode::SUCCESS != status ) {
750  ERR_PRINT("BTAdapter::startDiscoveryBackground: le_enable_scan failed: %s", to_string(status).c_str());
751  }
752  checkDiscoveryState();
753  }
754 }
755 
757  // We allow !isEnabled, to utilize method for adjusting discovery state and notifying listeners
758  // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to stop BREDR, LE or DUAL scanning!
759  const std::lock_guard<std::mutex> lock(mtx_discovery); // RAII-style acquire and relinquish via destructor
760  /**
761  * Need to send mgmtEvDeviceDiscoveringMgmt(..)
762  * as manager/hci won't produce such event having temporarily disabled discovery.
763  * + --+-------+--------+-----------+----------------------------------------------------+
764  * | # | meta | native | keepAlive | Note
765  * +---+-------+--------+-----------+----------------------------------------------------+
766  * | 1 | true | true | false | -
767  * | 2 | false | false | false | -
768  * +---+-------+--------+-----------+----------------------------------------------------+
769  * | 3 | true | true | true | -
770  * | 4 | true | false | true | temporarily disabled -> startDiscoveryBackground()
771  * | 5 | false | false | true | [4] -> [5] requires manual DISCOVERING event
772  * +---+-------+--------+-----------+----------------------------------------------------+
773  * [4] current -> [5] post stopDiscovery == sendEvent
774  */
775  const ScanType currentNativeScanType = hci.getCurrentScanType();
776  const bool le_scan_temp_disabled = hasScanType(currentMetaScanType, ScanType::LE) && // true
777  !hasScanType(currentNativeScanType, ScanType::LE) && // false
778  keep_le_scan_alive; // true
779 
780  DBG_PRINT("BTAdapter::stopDiscovery: Start: keepAlive %d, currentScanType[native %s, meta %s], le_scan_temp_disabled %d ...",
781  keep_le_scan_alive.load(),
782  to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(),
783  le_scan_temp_disabled);
784 
785  keep_le_scan_alive = false;
786  if( !hasScanType(currentMetaScanType, ScanType::LE) ) {
787  DBG_PRINT("BTAdapter::stopDiscovery: Already disabled, keepAlive %d, currentScanType[native %s, meta %s] ...",
788  keep_le_scan_alive.load(),
789  to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str());
790  checkDiscoveryState();
791  return HCIStatusCode::SUCCESS;
792  }
793 
794  HCIStatusCode status;
795  if( !adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED) ) {
796  WARN_PRINT("BTAdapter::stopDiscovery: Powered off: %s", toString(true).c_str());
798  currentMetaScanType = ScanType::NONE;
800  goto exit;
801  }
802  if( !hci.isOpen() ) {
803  ERR_PRINT("BTAdapter::stopDiscovery: HCI closed: %s", toString(true).c_str());
805  goto exit;
806  }
807 
808  if( le_scan_temp_disabled ) {
809  // meta state transition [4] -> [5], w/o native disabling
810  // Will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType
811  // currentMetaScanType = changeScanType(currentMetaScanType, false, ScanType::LE);
812  status = HCIStatusCode::SUCCESS; // send event: discoveryTempDisabled
813  } else {
814  // if le_enable_scan(..) is successful, it will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType.
815  status = hci.le_enable_scan(false /* enable */);
816  if( HCIStatusCode::SUCCESS != status ) {
817  ERR_PRINT("BTAdapter::stopDiscovery: le_enable_scan failed: %s", to_string(status).c_str());
818  }
819  }
820 
821 exit:
822  if( le_scan_temp_disabled || HCIStatusCode::SUCCESS != status ) {
823  // In case of discoveryTempDisabled, power-off, le_enable_scane failure
824  // or already closed HCIHandler, send the event directly.
825  const MgmtEvtDiscovering e(dev_id, ScanType::LE, false);
826  mgmtEvDeviceDiscoveringHCI( e );
827  }
828  if( _print_device_lists || jau::environment::get().verbose ) {
829  jau::PLAIN_PRINT(true, "BTAdapter::stopDiscovery: End: Result %s, keepAlive %d, currentScanType[native %s, meta %s], le_scan_temp_disabled %d ...\n- %s",
830  to_string(status).c_str(), keep_le_scan_alive.load(),
831  to_string(hci.getCurrentScanType()).c_str(), to_string(currentMetaScanType).c_str(), le_scan_temp_disabled,
832  toString().c_str());
834  }
835 
836  checkDiscoveryState();
837 
838  return status;
839 }
840 
841 // *************************************************
842 
843 std::shared_ptr<BTDevice> BTAdapter::findDiscoveredDevice (const EUI48 & address, const BDAddressType addressType) noexcept {
844  const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
845  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
846  return findDevice(discoveredDevices, address, addressType);
847 }
848 
849 bool BTAdapter::addDiscoveredDevice(std::shared_ptr<BTDevice> const &device) noexcept {
850  const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
851  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
852  if( nullptr != findDevice(discoveredDevices, *device) ) {
853  // already discovered
854  return false;
855  }
856  discoveredDevices.push_back(device);
857  return true;
858 }
859 
860 bool BTAdapter::removeDiscoveredDevice(const BDAddressAndType & addressAndType) noexcept {
861  const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
862  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
863  for (auto it = discoveredDevices.begin(); it != discoveredDevices.end(); ++it) {
864  const BTDevice& device = **it;
865  if ( nullptr != *it && addressAndType == device.addressAndType ) {
866  if( nullptr == getSharedDevice( device ) ) {
867  removeAllStatusListener( device );
868  }
869  discoveredDevices.erase(it);
870  return true;
871  }
872  }
873  return false;
874 }
875 
876 
878  const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
879  jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc
880 
881  int res = discoveredDevices.size();
882  if( 0 < res ) {
883  auto it = discoveredDevices.end();
884  do {
885  --it;
886  const BTDevice& device = **it;
887  if( nullptr == getSharedDevice( device ) ) {
888  removeAllStatusListener( device );
889  }
890  discoveredDevices.erase(it);
891  } while( it != discoveredDevices.begin() );
892  }
893 
894  if( _print_device_lists || jau::environment::get().verbose ) {
895  jau::PLAIN_PRINT(true, "BTAdapter::removeDiscoveredDevices: End: %d, %s", res, toString().c_str());
897  }
898  return res;
899 }
900 
902  jau::sc_atomic_critical sync(sync_data); // lock-free simple cache-load 'snapshot'
903  device_list_t res = discoveredDevices;
904  return res;
905 }
906 
907 bool BTAdapter::addSharedDevice(std::shared_ptr<BTDevice> const &device) noexcept {
908  const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
909  if( nullptr != findDevice(sharedDevices, *device) ) {
910  // already shared
911  return false;
912  }
913  sharedDevices.push_back(device);
914  return true;
915 }
916 
917 std::shared_ptr<BTDevice> BTAdapter::getSharedDevice(const BTDevice & device) noexcept {
918  const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
919  return findDevice(sharedDevices, device);
920 }
921 
922 void BTAdapter::removeSharedDevice(const BTDevice & device) noexcept {
923  const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
924  for (auto it = sharedDevices.begin(); it != sharedDevices.end(); ) {
925  if ( nullptr != *it && device == **it ) {
926  it = sharedDevices.erase(it);
927  return; // unique set
928  } else {
929  ++it;
930  }
931  }
932 }
933 
934 std::shared_ptr<BTDevice> BTAdapter::findSharedDevice (const EUI48 & address, const BDAddressType addressType) noexcept {
935  const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
936  return findDevice(sharedDevices, address, addressType);
937 }
938 
939 void BTAdapter::removeDevice(BTDevice & device) noexcept {
940  WORDY_PRINT("DBTAdapter::removeDevice: Start %s", toString().c_str());
941  removeAllStatusListener(device);
942 
943  const HCIStatusCode status = device.disconnect(HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION);
944  WORDY_PRINT("BTAdapter::removeDevice: disconnect %s, %s", to_string(status).c_str(), toString().c_str());
945  unlockConnect(device);
946  removeConnectedDevice(device); // usually done in BTAdapter::mgmtEvDeviceDisconnectedHCI
947  removeDiscoveredDevice(device.addressAndType); // usually done in BTAdapter::mgmtEvDeviceDisconnectedHCI
948  removeSharedDevice(device);
949 
950  if( _print_device_lists || jau::environment::get().verbose ) {
951  jau::PLAIN_PRINT(true, "BTAdapter::removeDevice: End %s, %s", device.getAddressAndType().toString().c_str(), toString().c_str());
952  printDeviceLists();
953  }
954 }
955 
956 std::string BTAdapter::toString(bool includeDiscoveredDevices) const noexcept {
957  std::string random_address_info = adapterInfo.addressAndType != visibleAddressAndType ? " ("+visibleAddressAndType.toString()+")" : "";
958  std::string out("Adapter[BTMode "+to_string(btMode)+", "+adapterInfo.addressAndType.toString()+random_address_info+
959  ", '"+getName()+"', id "+std::to_string(dev_id)+
960  ", curSettings"+to_string(adapterInfo.getCurrentSettingMask())+
961  ", scanType[native "+to_string(hci.getCurrentScanType())+", meta "+to_string(currentMetaScanType)+"]"
962  ", valid "+std::to_string(isValid())+
963  ", hci_ext[scan "+std::to_string(hci_uses_ext_scan)+", conn "+std::to_string(hci_uses_ext_conn)+
964  "], open[mgmt, "+std::to_string(mgmt.isOpen())+", hci "+std::to_string(hci.isOpen())+
965  "], "+javaObjectToString()+"]");
966  if( includeDiscoveredDevices ) {
967  device_list_t devices = getDiscoveredDevices();
968  if( devices.size() > 0 ) {
969  out.append("\n");
970  for(auto it = devices.begin(); it != devices.end(); it++) {
971  std::shared_ptr<BTDevice> p = *it;
972  if( nullptr != p ) {
973  out.append(" ").append(p->toString()).append("\n");
974  }
975  }
976  }
977  }
978  return out;
979 }
980 
981 // *************************************************
982 
983 void BTAdapter::sendAdapterSettingsChanged(const AdapterSetting old_settings_, const AdapterSetting current_settings, AdapterSetting changes,
984  const uint64_t timestampMS) noexcept
985 {
986  int i=0;
987  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
988  try {
989  p.listener->adapterSettingsChanged(*this, old_settings_, current_settings, changes, timestampMS);
990  } catch (std::exception &e) {
991  ERR_PRINT("BTAdapter:CB:NewSettings-CBs %d/%zd: %s of %s: Caught exception %s",
992  i+1, statusListenerList.size(),
993  p.listener->toString().c_str(), toString().c_str(), e.what());
994  }
995  i++;
996  });
997 }
998 
999 void BTAdapter::sendAdapterSettingsInitial(AdapterStatusListener & asl, const uint64_t timestampMS) noexcept
1000 {
1001  const AdapterSetting current_settings = adapterInfo.getCurrentSettingMask();
1002  COND_PRINT(debug_event, "BTAdapter::sendAdapterSettingsInitial: NONE -> %s, changes NONE: %s",
1003  to_string(current_settings).c_str(), toString().c_str() );
1004  try {
1005  asl.adapterSettingsChanged(*this, AdapterSetting::NONE, current_settings, AdapterSetting::NONE, timestampMS);
1006  } catch (std::exception &e) {
1007  ERR_PRINT("BTAdapter::sendAdapterSettingsChanged-CB: %s of %s: Caught exception %s",
1008  asl.toString().c_str(), toString().c_str(), e.what());
1009  }
1010 }
1011 
1012 void BTAdapter::sendDeviceUpdated(std::string cause, std::shared_ptr<BTDevice> device, uint64_t timestamp, EIRDataType updateMask) noexcept {
1013  int i=0;
1014  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1015  try {
1016  if( p.listener->matchDevice(*device) ) {
1017  p.listener->deviceUpdated(device, updateMask, timestamp);
1018  }
1019  } catch (std::exception &e) {
1020  ERR_PRINT("BTAdapter::sendDeviceUpdated-CBs (%s) %d/%zd: %s of %s: Caught exception %s",
1021  cause.c_str(), i+1, statusListenerList.size(),
1022  p.listener->toString().c_str(), device->toString().c_str(), e.what());
1023  }
1024  i++;
1025  });
1026 }
1027 
1028 // *************************************************
1029 
1030 bool BTAdapter::mgmtEvDeviceDiscoveringHCI(const MgmtEvent& e) noexcept {
1031  return mgmtEvDeviceDiscoveringAny(e, true /* hciSourced */ );
1032 }
1033 
1034 bool BTAdapter::mgmtEvDeviceDiscoveringMgmt(const MgmtEvent& e) noexcept {
1035  return mgmtEvDeviceDiscoveringAny(e, false /* hciSourced */ );
1036 }
1037 
1038 bool BTAdapter::mgmtEvDeviceDiscoveringAny(const MgmtEvent& e, const bool hciSourced) noexcept {
1039  const std::string srctkn = hciSourced ? "hci" : "mgmt";
1040  const MgmtEvtDiscovering &event = *static_cast<const MgmtEvtDiscovering *>(&e);
1041  const ScanType eventScanType = event.getScanType();
1042  const bool eventEnabled = event.getEnabled();
1043  ScanType currentNativeScanType = hci.getCurrentScanType();
1044 
1045  // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to setup BREDR, LE or DUAL scanning!
1046  //
1047  // Also catches case where discovery changes w/o user interaction [start/stop]Discovery(..)
1048  // if sourced from mgmt channel (!hciSourced)
1049 
1050  ScanType nextMetaScanType;
1051  if( eventEnabled ) {
1052  // enabled eventScanType
1053  nextMetaScanType = changeScanType(currentMetaScanType, eventScanType, true);
1054  } else {
1055  // disabled eventScanType
1056  if( hasScanType(eventScanType, ScanType::LE) && keep_le_scan_alive ) {
1057  // Unchanged meta for disabled-LE && keep_le_scan_alive
1058  nextMetaScanType = currentMetaScanType;
1059  } else {
1060  nextMetaScanType = changeScanType(currentMetaScanType, eventScanType, false);
1061  }
1062  }
1063 
1064  if( !hciSourced ) {
1065  // update HCIHandler's currentNativeScanType from other source
1066  const ScanType nextNativeScanType = changeScanType(currentNativeScanType, eventScanType, eventEnabled);
1067  DBG_PRINT("BTAdapter:%s:DeviceDiscovering: dev_id %d, keepDiscoveringAlive %d: scanType[native %s -> %s, meta %s -> %s]): %s",
1068  srctkn.c_str(), dev_id, keep_le_scan_alive.load(),
1069  to_string(currentNativeScanType).c_str(), to_string(nextNativeScanType).c_str(),
1070  to_string(currentMetaScanType).c_str(), to_string(nextMetaScanType).c_str(),
1071  event.toString().c_str());
1072  currentNativeScanType = nextNativeScanType;
1073  hci.setCurrentScanType(currentNativeScanType);
1074  } else {
1075  DBG_PRINT("BTAdapter:%s:DeviceDiscovering: dev_id %d, keepDiscoveringAlive %d: scanType[native %s, meta %s -> %s]): %s",
1076  srctkn.c_str(), dev_id, keep_le_scan_alive.load(),
1077  to_string(currentNativeScanType).c_str(),
1078  to_string(currentMetaScanType).c_str(), to_string(nextMetaScanType).c_str(),
1079  event.toString().c_str());
1080  }
1081  currentMetaScanType = nextMetaScanType;
1082 
1083  checkDiscoveryState();
1084 
1085  int i=0;
1086  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1087  try {
1088  p.listener->discoveringChanged(*this, currentMetaScanType, eventScanType, eventEnabled, keep_le_scan_alive, event.getTimestamp());
1089  } catch (std::exception &except) {
1090  ERR_PRINT("BTAdapter:%s:DeviceDiscovering-CBs %d/%zd: %s of %s: Caught exception %s",
1091  srctkn.c_str(), i+1, statusListenerList.size(),
1092  p.listener->toString().c_str(), toString().c_str(), except.what());
1093  }
1094  i++;
1095  });
1096 
1097  if( !hasScanType(currentNativeScanType, ScanType::LE) && keep_le_scan_alive ) {
1098  std::thread bg(&BTAdapter::startDiscoveryBackground, this); // @suppress("Invalid arguments")
1099  bg.detach();
1100  }
1101  return true;
1102 }
1103 
1104 bool BTAdapter::mgmtEvNewSettingsMgmt(const MgmtEvent& e) noexcept {
1105  COND_PRINT(debug_event, "BTAdapter:mgmt:NewSettings: %s", e.toString().c_str());
1106  const MgmtEvtNewSettings &event = *static_cast<const MgmtEvtNewSettings *>(&e);
1107  const AdapterSetting new_settings = adapterInfo.setCurrentSettingMask(event.getSettings()); // probably done by mgmt callback already
1108  {
1109  const BTMode _btMode = getAdapterSettingsBTMode(new_settings);
1110  if( BTMode::NONE != _btMode ) {
1111  btMode = _btMode;
1112  }
1113  }
1114  const AdapterSetting old_settings_ = old_settings;
1115 
1116  const AdapterSetting changes = getAdapterSettingMaskDiff(new_settings, old_settings_);
1117 
1118  const bool justPoweredOn = isAdapterSettingBitSet(changes, AdapterSetting::POWERED) &&
1120 
1121  const bool justPoweredOff = isAdapterSettingBitSet(changes, AdapterSetting::POWERED) &&
1123 
1124  old_settings = new_settings;
1125 
1126  COND_PRINT(debug_event, "BTAdapter::mgmt:NewSettings: %s -> %s, changes %s: %s",
1127  to_string(old_settings_).c_str(),
1128  to_string(new_settings).c_str(),
1129  to_string(changes).c_str(), toString().c_str() );
1130 
1131  if( justPoweredOn ) {
1132  // Adapter has been powered on, ensure all hci states are reset.
1133  hci.resetAllStates(true);
1134  updateDataFromHCI();
1135  }
1136  sendAdapterSettingsChanged(old_settings_, new_settings, changes, event.getTimestamp());
1137 
1138  if( justPoweredOff ) {
1139  // Adapter has been powered off, close connections and cleanup off-thread.
1140  std::thread bg(&BTAdapter::poweredOff, this); // @suppress("Invalid arguments")
1141  bg.detach();
1142  }
1143 
1144  return true;
1145 }
1146 
1147 bool BTAdapter::mgmtEvLocalNameChangedMgmt(const MgmtEvent& e) noexcept {
1148  COND_PRINT(debug_event, "BTAdapter:mgmt:LocalNameChanged: %s", e.toString().c_str());
1149  const MgmtEvtLocalNameChanged &event = *static_cast<const MgmtEvtLocalNameChanged *>(&e);
1150  std::string old_name = localName.getName();
1151  std::string old_shortName = localName.getShortName();
1152  bool nameChanged = old_name != event.getName();
1153  bool shortNameChanged = old_shortName != event.getShortName();
1154  if( nameChanged ) {
1155  localName.setName(event.getName());
1156  }
1157  if( shortNameChanged ) {
1158  localName.setShortName(event.getShortName());
1159  }
1160  COND_PRINT(debug_event, "BTAdapter:mgmt:LocalNameChanged: Local name: %d: '%s' -> '%s'; short_name: %d: '%s' -> '%s'",
1161  nameChanged, old_name.c_str(), localName.getName().c_str(),
1162  shortNameChanged, old_shortName.c_str(), localName.getShortName().c_str());
1163  (void)nameChanged;
1164  (void)shortNameChanged;
1165  return true;
1166 }
1167 
1168 bool BTAdapter::mgmtEvDeviceConnectedHCI(const MgmtEvent& e) noexcept {
1169  COND_PRINT(debug_event, "BTAdapter:hci:DeviceConnected(dev_id %d): %s", dev_id, e.toString().c_str());
1170  const MgmtEvtDeviceConnected &event = *static_cast<const MgmtEvtDeviceConnected *>(&e);
1171  EInfoReport ad_report;
1172  {
1174  ad_report.setTimestamp(event.getTimestamp());
1175  ad_report.setAddressType(event.getAddressType());
1176  ad_report.setAddress( event.getAddress() );
1177  ad_report.read_data(event.getData(), event.getDataSize());
1178  }
1179  int new_connect = 0;
1180  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1181  if( nullptr == device ) {
1182  device = findDiscoveredDevice(event.getAddress(), event.getAddressType());
1183  if( nullptr != device ) {
1184  addSharedDevice(device); // connected devices must be in shared + discovered list
1185  new_connect = 1;
1186  }
1187  }
1188  if( nullptr == device ) {
1189  device = findSharedDevice(event.getAddress(), event.getAddressType());
1190  if( nullptr != device ) {
1191  addDiscoveredDevice(device); // connected devices must be in shared + discovered list
1192  new_connect = 2;
1193  }
1194  }
1195  if( nullptr == device ) {
1196  // a whitelist auto-connect w/o previous discovery
1197  device = BTDevice::make_shared(*this, ad_report);
1198  addDiscoveredDevice(device);
1199  addSharedDevice(device);
1200  new_connect = 3;
1201  }
1202 
1203  const SMPIOCapability io_cap_conn = mgmt.getIOCapability(dev_id);
1204 
1205  EIRDataType updateMask = device->update(ad_report);
1206  if( 0 == new_connect ) {
1207  WARN_PRINT("BTAdapter::EventHCI:DeviceConnected(dev_id %d, already connected, updated %s): %s, handle %s -> %s,\n %s,\n -> %s",
1208  dev_id, to_string(updateMask).c_str(), event.toString().c_str(),
1209  jau::to_hexstring(device->getConnectionHandle()).c_str(), jau::to_hexstring(event.getHCIHandle()).c_str(),
1210  ad_report.toString().c_str(),
1211  device->toString().c_str());
1212  } else {
1213  addConnectedDevice(device); // track device, if not done yet
1214  if( 2 <= new_connect ) {
1215  device->ts_last_discovery = ad_report.getTimestamp();
1216  }
1217  COND_PRINT(debug_event, "BTAdapter::EventHCI:DeviceConnected(dev_id %d, new_connect %d, updated %s): %s, handle %s -> %s,\n %s,\n -> %s",
1218  dev_id, new_connect, to_string(updateMask).c_str(), event.toString().c_str(),
1219  jau::to_hexstring(device->getConnectionHandle()).c_str(), jau::to_hexstring(event.getHCIHandle()).c_str(),
1220  ad_report.toString().c_str(),
1221  device->toString().c_str());
1222  }
1223  device->notifyConnected(device, event.getHCIHandle(), io_cap_conn);
1224 
1225  if( device->isConnSecurityAutoEnabled() ) {
1226  new_connect = 0; // disable deviceConnected() events
1227  }
1228  int i=0;
1229  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1230  try {
1231  if( p.listener->matchDevice(*device) ) {
1232  if( EIRDataType::NONE != updateMask ) {
1233  p.listener->deviceUpdated(device, updateMask, ad_report.getTimestamp());
1234  }
1235  if( 0 < new_connect ) {
1236  p.listener->deviceConnected(device, event.getHCIHandle(), event.getTimestamp());
1237  }
1238  }
1239  } catch (std::exception &except) {
1240  ERR_PRINT("BTAdapter::EventHCI:DeviceConnected-CBs %d/%zd: %s of %s: Caught exception %s",
1241  i+1, statusListenerList.size(),
1242  p.listener->toString().c_str(), device->toString().c_str(), except.what());
1243  }
1244  i++;
1245  });
1246  return true;
1247 }
1248 
1249 bool BTAdapter::mgmtEvConnectFailedHCI(const MgmtEvent& e) noexcept {
1250  COND_PRINT(debug_event, "BTAdapter::EventHCI:ConnectFailed: %s", e.toString().c_str());
1251  const MgmtEvtDeviceConnectFailed &event = *static_cast<const MgmtEvtDeviceConnectFailed *>(&e);
1252 
1253  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1254  if( nullptr != device ) {
1255  const uint16_t handle = device->getConnectionHandle();
1256  COND_PRINT(debug_event, "BTAdapter::EventHCI:ConnectFailed(dev_id %d): %s, handle %s -> zero,\n -> %s",
1257  dev_id, event.toString().c_str(), jau::to_hexstring(handle).c_str(),
1258  device->toString().c_str());
1259 
1260  unlockConnect(*device);
1261  device->notifyDisconnected();
1262  removeConnectedDevice(*device);
1263 
1264  if( !device->isConnSecurityAutoEnabled() ) {
1265  int i=0;
1266  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1267  try {
1268  if( p.listener->matchDevice(*device) ) {
1269  p.listener->deviceDisconnected(device, event.getHCIStatus(), handle, event.getTimestamp());
1270  }
1271  } catch (std::exception &except) {
1272  ERR_PRINT("BTAdapter::EventHCI:DeviceDisconnected-CBs %d/%zd: %s of %s: Caught exception %s",
1273  i+1, statusListenerList.size(),
1274  p.listener->toString().c_str(), device->toString().c_str(), except.what());
1275  }
1276  i++;
1277  });
1278  removeDiscoveredDevice(device->addressAndType); // ensure device will cause a deviceFound event after disconnect
1279  }
1280  } else {
1281  WORDY_PRINT("BTAdapter::EventHCI:DeviceDisconnected(dev_id %d): Device not tracked: %s",
1282  dev_id, event.toString().c_str());
1283  }
1284  return true;
1285 }
1286 
1287 bool BTAdapter::mgmtEvHCIEncryptionChangedHCI(const MgmtEvent& e) noexcept {
1288  const MgmtEvtHCIEncryptionChanged &event = *static_cast<const MgmtEvtHCIEncryptionChanged *>(&e);
1289 
1290  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1291  if( nullptr != device ) {
1292  // BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.8 HCIEventType::ENCRYPT_CHANGE
1293  const HCIStatusCode evtStatus = event.getHCIStatus();
1294  const bool ok = HCIStatusCode::SUCCESS == evtStatus && 0 != event.getEncEnabled();
1295  const SMPPairingState pstate = ok ? SMPPairingState::COMPLETED : SMPPairingState::FAILED;
1296  device->updatePairingState(device, e, evtStatus, pstate);
1297  } else {
1298  WORDY_PRINT("BTAdapter::EventHCI:EncryptionChanged(dev_id %d): Device not tracked: %s",
1299  dev_id, event.toString().c_str());
1300  }
1301  return true;
1302 }
1303 bool BTAdapter::mgmtEvHCIEncryptionKeyRefreshCompleteHCI(const MgmtEvent& e) noexcept {
1304  const MgmtEvtHCIEncryptionKeyRefreshComplete &event = *static_cast<const MgmtEvtHCIEncryptionKeyRefreshComplete *>(&e);
1305 
1306  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1307  if( nullptr != device ) {
1308  // BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.39 HCIEventType::ENCRYPT_KEY_REFRESH_COMPLETE
1309  const HCIStatusCode evtStatus = event.getHCIStatus();
1310  // const bool ok = HCIStatusCode::SUCCESS == evtStatus;
1311  // const SMPPairingState pstate = ok ? SMPPairingState::COMPLETED : SMPPairingState::FAILED;
1312  const SMPPairingState pstate = SMPPairingState::NONE;
1313  device->updatePairingState(device, e, evtStatus, pstate);
1314  } else {
1315  WORDY_PRINT("BTAdapter::EventHCI:EncryptionKeyRefreshComplete(dev_id %d): Device not tracked: %s",
1316  dev_id, event.toString().c_str());
1317  }
1318  return true;
1319 }
1320 
1321 bool BTAdapter::mgmtEvHCILERemoteUserFeaturesHCI(const MgmtEvent& e) noexcept {
1322  const MgmtEvtHCILERemoteUserFeatures &event = *static_cast<const MgmtEvtHCILERemoteUserFeatures *>(&e);
1323 
1324  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1325  if( nullptr != device ) {
1326  COND_PRINT(debug_event, "BTAdapter::EventHCI:LERemoteUserFeatures(dev_id %d): %s, %s",
1327  dev_id, event.toString().c_str(), device->toString().c_str());
1328 
1329  device->notifyLEFeatures(device, event.getFeatures());
1330 
1331  } else {
1332  WORDY_PRINT("BTAdapter::EventHCI:LERemoteUserFeatures(dev_id %d): Device not tracked: %s",
1333  dev_id, event.toString().c_str());
1334  }
1335  return true;
1336 }
1337 
1338 bool BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept {
1339  const MgmtEvtDeviceDisconnected &event = *static_cast<const MgmtEvtDeviceDisconnected *>(&e);
1340 
1341  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1342  if( nullptr != device ) {
1343  if( device->getConnectionHandle() != event.getHCIHandle() ) {
1344  WORDY_PRINT("BTAdapter::EventHCI:DeviceDisconnected(dev_id %d): ConnHandle mismatch %s\n -> %s",
1345  dev_id, event.toString().c_str(), device->toString().c_str());
1346  return true;
1347  }
1348  COND_PRINT(debug_event, "BTAdapter::EventHCI:DeviceDisconnected(dev_id %d): %s, handle %s -> zero,\n -> %s",
1349  dev_id, event.toString().c_str(), jau::to_hexstring(event.getHCIHandle()).c_str(),
1350  device->toString().c_str());
1351 
1352  unlockConnect(*device);
1353  device->notifyDisconnected();
1354  removeConnectedDevice(*device);
1355 
1356  if( !device->isConnSecurityAutoEnabled() ) {
1357  int i=0;
1358  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1359  try {
1360  if( p.listener->matchDevice(*device) ) {
1361  p.listener->deviceDisconnected(device, event.getHCIReason(), event.getHCIHandle(), event.getTimestamp());
1362  }
1363  } catch (std::exception &except) {
1364  ERR_PRINT("BTAdapter::EventHCI:DeviceDisconnected-CBs %d/%zd: %s of %s: Caught exception %s",
1365  i+1, statusListenerList.size(),
1366  p.listener->toString().c_str(), device->toString().c_str(), except.what());
1367  }
1368  i++;
1369  });
1370  removeDiscoveredDevice(device->addressAndType); // ensure device will cause a deviceFound event after disconnect
1371  }
1372  } else {
1373  WORDY_PRINT("BTAdapter::EventHCI:DeviceDisconnected(dev_id %d): Device not tracked: %s",
1374  dev_id, event.toString().c_str());
1375  }
1376  return true;
1377 }
1378 
1379 bool BTAdapter::mgmtEvDeviceDisconnectedMgmt(const MgmtEvent& e) noexcept {
1380  COND_PRINT(debug_event, "BTAdapter:mgmt:DeviceDisconnected: %s", e.toString().c_str());
1381  const MgmtEvtDeviceDisconnected &event = *static_cast<const MgmtEvtDeviceDisconnected *>(&e);
1382  (void)event;
1383  return true;
1384 }
1385 
1386 bool BTAdapter::mgmtEvPairDeviceCompleteMgmt(const MgmtEvent& e) noexcept {
1387  const MgmtEvtPairDeviceComplete &event = *static_cast<const MgmtEvtPairDeviceComplete *>(&e);
1388 
1389  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1390  if( nullptr != device ) {
1391  const HCIStatusCode evtStatus = to_HCIStatusCode( event.getStatus() );
1392  const bool ok = HCIStatusCode::ALREADY_PAIRED == evtStatus;
1393  const SMPPairingState pstate = ok ? SMPPairingState::COMPLETED : SMPPairingState::NONE;
1394  device->updatePairingState(device, e, evtStatus, pstate);
1395  } else {
1396  WORDY_PRINT("BTAdapter::mgmt:PairDeviceComplete(dev_id %d): Device not tracked: %s",
1397  dev_id, event.toString().c_str());
1398  }
1399  return true;
1400 }
1401 
1402 bool BTAdapter::mgmtEvNewLongTermKeyMgmt(const MgmtEvent& e) noexcept {
1403  const MgmtEvtNewLongTermKey& event = *static_cast<const MgmtEvtNewLongTermKey *>(&e);
1404  const MgmtLongTermKeyInfo& ltk_info = event.getLongTermKey();
1405  std::shared_ptr<BTDevice> device = findConnectedDevice(ltk_info.address, ltk_info.address_type);
1406  if( nullptr != device ) {
1407  const bool ok = ltk_info.enc_size > 0 && ltk_info.key_type != MgmtLTKType::NONE;
1408  if( ok ) {
1409  device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::COMPLETED);
1410  } else {
1411  WORDY_PRINT("BTAdapter::mgmt:NewLongTermKey(dev_id %d): Invalid LTK: %s",
1412  dev_id, event.toString().c_str());
1413  }
1414  } else {
1415  WORDY_PRINT("BTAdapter::mgmt:NewLongTermKey(dev_id %d): Device not tracked: %s",
1416  dev_id, event.toString().c_str());
1417  }
1418  return true;
1419 }
1420 
1421 bool BTAdapter::mgmtEvDeviceFoundHCI(const MgmtEvent& e) noexcept {
1422  // COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(dev_id %d): %s", dev_id, e.toString().c_str());
1423  const MgmtEvtDeviceFound &deviceFoundEvent = *static_cast<const MgmtEvtDeviceFound *>(&e);
1424 
1425  const EInfoReport* eir = deviceFoundEvent.getEIR();
1426  if( nullptr == eir ) {
1427  // Sourced from Linux Mgmt, which we don't support
1428  ABORT("BTAdapter:hci:DeviceFound: Not sourced from LE_ADVERTISING_REPORT: %s", deviceFoundEvent.toString().c_str());
1429  } // else: Sourced from HCIHandler via LE_ADVERTISING_REPORT (default!)
1430 
1431  /**
1432  * <pre>
1433  * + ------+------------+----------+----------+-------------------------------------------+
1434  * | # | discovered | shared | update |
1435  * +-------+------------+----------+----------+-------------------------------------------+
1436  * | 1.1 | false | false | ignored | New undiscovered/unshared -> deviceFound(..)
1437  * | 1.2 | false | true | ignored | Undiscovered but shared -> deviceFound(..) [deviceUpdated(..)]
1438  * | 2.1.1 | true | false | name | Discovered but unshared, name changed -> deviceFound(..)
1439  * | 2.1.2 | true | false | !name | Discovered but unshared, no name change -> Drop(1)
1440  * | 2.2.1 | true | true | any | Discovered and shared, updated -> deviceUpdated(..)
1441  * | 2.2.2 | true | true | none | Discovered and shared, not-updated -> Drop(2)
1442  * +-------+------------+----------+-----------------------------------------------------+
1443  * </pre>
1444  *
1445  */
1446  std::shared_ptr<BTDevice> dev_discovered = findDiscoveredDevice(eir->getAddress(), eir->getAddressType());
1447  std::shared_ptr<BTDevice> dev_shared = findSharedDevice(eir->getAddress(), eir->getAddressType());
1448  if( nullptr == dev_discovered ) {
1449  if( nullptr == dev_shared ) {
1450  //
1451  // All new discovered device
1452  //
1453  dev_shared = BTDevice::make_shared(*this, *eir);
1454  addDiscoveredDevice(dev_shared);
1455  addSharedDevice(dev_shared);
1456  COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(1.1, dev_id %d): New undiscovered/unshared %s -> deviceFound(..) %s",
1457  dev_id, dev_shared->getAddressAndType().toString().c_str(), eir->toString().c_str());
1458 
1459  int i=0;
1460  bool device_used = false;
1461  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1462  try {
1463  if( p.listener->matchDevice(*dev_shared) ) {
1464  device_used = p.listener->deviceFound(dev_shared, eir->getTimestamp()) || device_used;
1465  }
1466  } catch (std::exception &except) {
1467  ERR_PRINT("BTAdapter:hci:DeviceFound-CBs %d/%zd: %s of %s: Caught exception %s",
1468  i+1, statusListenerList.size(),
1469  p.listener->toString().c_str(), dev_shared->toString().c_str(), except.what());
1470  }
1471  i++;
1472  });
1473  if( !device_used ) {
1474  // keep to avoid duplicate finds: removeDiscoveredDevice(dev_discovered->addressAndType);
1475  // and still allowing usage, as connecting will re-add to shared list
1476  removeSharedDevice(*dev_shared); // pending dtor if discovered is flushed
1477  }
1478  } else { // nullptr != dev_shared
1479  //
1480  // Active shared device, but flushed from discovered devices
1481  // - update device
1482  // - issue deviceFound(..), allowing receivers to recognize the re-discovered device
1483  // - issue deviceUpdate(..), if at least one deviceFound(..) returned true and data has changed, allowing receivers to act upon
1484  // - removeSharedDevice(..), if non deviceFound(..) returned true
1485  //
1486  EIRDataType updateMask = dev_shared->update(*eir);
1487  addDiscoveredDevice(dev_shared); // re-add to discovered devices!
1488  dev_shared->ts_last_discovery = eir->getTimestamp();
1489  COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(1.2, dev_id %d): Undiscovered but shared %s -> deviceFound(..) [deviceUpdated(..)] %s",
1490  dev_id, dev_shared->getAddressAndType().toString().c_str(), eir->toString().c_str());
1491 
1492  int i=0;
1493  bool device_used = false;
1494  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1495  try {
1496  if( p.listener->matchDevice(*dev_shared) ) {
1497  device_used = p.listener->deviceFound(dev_shared, eir->getTimestamp()) || device_used;
1498  }
1499  } catch (std::exception &except) {
1500  ERR_PRINT("BTAdapter:hci:DeviceFound: %d/%zd: %s of %s: Caught exception %s",
1501  i+1, statusListenerList.size(),
1502  p.listener->toString().c_str(), dev_shared->toString().c_str(), except.what());
1503  }
1504  i++;
1505  });
1506  if( !device_used ) {
1507  // keep to avoid duplicate finds: removeDiscoveredDevice(dev->addressAndType);
1508  // and still allowing usage, as connecting will re-add to shared list
1509  removeSharedDevice(*dev_shared); // pending dtor until discovered is flushed
1510  } else if( EIRDataType::NONE != updateMask ) {
1511  sendDeviceUpdated("SharedDeviceFound", dev_shared, eir->getTimestamp(), updateMask);
1512  }
1513  }
1514  } else { // nullptr != dev_discovered
1515  //
1516  // Already discovered device
1517  //
1518  const EIRDataType updateMask = dev_discovered->update(*eir);
1519  if( nullptr == dev_shared ) {
1520  //
1521  // Discovered but not a shared device,
1522  // i.e. all user deviceFound(..) returned false - no interest/rejected.
1523  //
1524  if( EIRDataType::NONE != ( updateMask & EIRDataType::NAME ) ) {
1525  // Name got updated, send out deviceFound(..) again
1526  COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(2.1.1, dev_id %d): Discovered but unshared %s, name changed %s -> deviceFound(..) %s",
1527  dev_id, dev_discovered->getAddressAndType().toString().c_str(),
1528  direct_bt::to_string(updateMask).c_str(), eir->toString().c_str());
1529  addSharedDevice(dev_discovered); // re-add to shared devices!
1530  int i=0;
1531  bool device_used = false;
1532  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1533  try {
1534  if( p.listener->matchDevice(*dev_discovered) ) {
1535  device_used = p.listener->deviceFound(dev_discovered, eir->getTimestamp()) || device_used;
1536  }
1537  } catch (std::exception &except) {
1538  ERR_PRINT("BTAdapter:hci:DeviceFound: %d/%zd: %s of %s: Caught exception %s",
1539  i+1, statusListenerList.size(),
1540  p.listener->toString().c_str(), dev_discovered->toString().c_str(), except.what());
1541  }
1542  i++;
1543  });
1544  if( !device_used ) {
1545  // keep to avoid duplicate finds: removeDiscoveredDevice(dev_discovered->addressAndType);
1546  // and still allowing usage, as connecting will re-add to shared list
1547  removeSharedDevice(*dev_discovered); // pending dtor if discovered is flushed
1548  }
1549  } else {
1550  // Drop: NAME didn't change
1551  COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(2.1.2, dev_id %d): Discovered but unshared %s, no name change -> Drop(1) %s",
1552  dev_id, dev_discovered->getAddressAndType().toString().c_str(), eir->toString().c_str());
1553  }
1554  } else { // nullptr != dev_shared
1555  //
1556  // Discovered and shared device,
1557  // i.e. at least one deviceFound(..) returned true - interest/picked.
1558  //
1559  if( EIRDataType::NONE != updateMask ) {
1560  COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(2.2.1, dev_id %d): Discovered and shared %s, updated %s -> deviceUpdated(..) %s",
1561  dev_id, dev_shared->getAddressAndType().toString().c_str(),
1562  direct_bt::to_string(updateMask).c_str(), eir->toString().c_str());
1563  sendDeviceUpdated("DiscoveredDeviceFound", dev_shared, eir->getTimestamp(), updateMask);
1564  } else {
1565  // Drop: No update
1566  COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(2.2.2, dev_id %d): Discovered and shared %s, not-updated -> Drop(2) %s",
1567  dev_id, dev_shared->getAddressAndType().toString().c_str(), eir->toString().c_str());
1568  }
1569  }
1570  }
1571  return true;
1572 }
1573 
1574 bool BTAdapter::mgmtEvDeviceUnpairedMgmt(const MgmtEvent& e) noexcept {
1575  const MgmtEvtDeviceUnpaired &event = *static_cast<const MgmtEvtDeviceUnpaired *>(&e);
1576  DBG_PRINT("BTAdapter:mgmt:DeviceUnpaired: %s", event.toString().c_str());
1577  return true;
1578 }
1579 bool BTAdapter::mgmtEvPinCodeRequestMgmt(const MgmtEvent& e) noexcept {
1580  const MgmtEvtPinCodeRequest &event = *static_cast<const MgmtEvtPinCodeRequest *>(&e);
1581  DBG_PRINT("BTAdapter:mgmt:PinCodeRequest: %s", event.toString().c_str());
1582  return true;
1583 }
1584 bool BTAdapter::mgmtEvAuthFailedMgmt(const MgmtEvent& e) noexcept {
1585  const MgmtEvtAuthFailed &event = *static_cast<const MgmtEvtAuthFailed *>(&e);
1586 
1587  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1588  if( nullptr == device ) {
1589  WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
1590  dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
1591  event.toString().c_str());
1592  return true;
1593  }
1594  const HCIStatusCode evtStatus = to_HCIStatusCode( event.getStatus() );
1595  device->updatePairingState(device, e, evtStatus, SMPPairingState::FAILED);
1596  return true;
1597 }
1598 bool BTAdapter::mgmtEvUserConfirmRequestMgmt(const MgmtEvent& e) noexcept {
1599  const MgmtEvtUserConfirmRequest &event = *static_cast<const MgmtEvtUserConfirmRequest *>(&e);
1600 
1601  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1602  if( nullptr == device ) {
1603  WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
1604  dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
1605  event.toString().c_str());
1606  return true;
1607  }
1608  // FIXME: Pass confirm_hint and value?
1609  device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::NUMERIC_COMPARE_EXPECTED);
1610  return true;
1611 }
1612 bool BTAdapter::mgmtEvUserPasskeyRequestMgmt(const MgmtEvent& e) noexcept {
1613  const MgmtEvtUserPasskeyRequest &event = *static_cast<const MgmtEvtUserPasskeyRequest *>(&e);
1614 
1615  std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
1616  if( nullptr == device ) {
1617  WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
1618  dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
1619  event.toString().c_str());
1620  return true;
1621  }
1622  device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::PASSKEY_EXPECTED);
1623  return true;
1624 }
1625 
1626 bool BTAdapter::hciSMPMsgCallback(const BDAddressAndType & addressAndType,
1627  const SMPPDUMsg& msg, const HCIACLData::l2cap_frame& source) noexcept {
1628  std::shared_ptr<BTDevice> device = findConnectedDevice(addressAndType.address, addressAndType.type);
1629  if( nullptr == device ) {
1630  WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address%s: %s, %s",
1631  dev_id, addressAndType.toString().c_str(),
1632  msg.toString().c_str(), source.toString().c_str());
1633  return true;
1634  }
1635  if( device->getConnectionHandle() != source.handle ) {
1636  WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: ConnHandle mismatch address%s: %s, %s\n -> %s",
1637  dev_id, addressAndType.toString().c_str(),
1638  msg.toString().c_str(), source.toString().c_str(), device->toString().c_str());
1639  return true;
1640  }
1641 
1642  device->hciSMPMsgCallback(device, msg, source);
1643 
1644  return true;
1645 }
1646 
1647 void BTAdapter::sendDevicePairingState(std::shared_ptr<BTDevice> device, const SMPPairingState state, const PairingMode mode, uint64_t timestamp) noexcept
1648 {
1649  int i=0;
1650  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1651  try {
1652  if( p.listener->matchDevice(*device) ) {
1653  p.listener->devicePairingState(device, state, mode, timestamp);
1654  }
1655  } catch (std::exception &except) {
1656  ERR_PRINT("BTAdapter::sendDevicePairingState: %d/%zd: %s of %s: Caught exception %s",
1657  i+1, statusListenerList.size(),
1658  p.listener->toString().c_str(), device->toString().c_str(), except.what());
1659  }
1660  i++;
1661  });
1662 }
1663 
1664 void BTAdapter::sendDeviceReady(std::shared_ptr<BTDevice> device, uint64_t timestamp) noexcept {
1665  int i=0;
1666  jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) {
1667  try {
1668  // Only issue if valid && received connected confirmation (HCI) && not have called disconnect yet.
1669  if( device->isValid() && device->getConnected() && device->allowDisconnect ) {
1670  if( p.listener->matchDevice(*device) ) {
1671  p.listener->deviceReady(device, timestamp);
1672  }
1673  }
1674  } catch (std::exception &except) {
1675  ERR_PRINT("BTAdapter::sendDeviceReady: %d/%zd: %s of %s: Caught exception %s",
1676  i+1, statusListenerList.size(),
1677  p.listener->toString().c_str(), device->toString().c_str(), except.what());
1678  }
1679  i++;
1680  });
1681 }
direct_bt::SMPIOCapability::UNSET
@ UNSET
Denoting unset value, i.e.
direct_bt::HCIHandler::resetAdapter
HCIStatusCode resetAdapter()
Reset the adapter.
Definition: HCIHandler.cpp:868
direct_bt::BTAdapter::stopDiscovery
HCIStatusCode stopDiscovery() noexcept
Closes the discovery session.
Definition: BTAdapter.cpp:756
jau::environment::get
static environment & get(const std::string root_prefix_domain="jau") noexcept
Static singleton initialization of this project's environment with the given global root prefix_domai...
Definition: environment.hpp:234
direct_bt::AdapterSetting
AdapterSetting
Adapter Setting Bits.
Definition: BTTypes1.hpp:136
direct_bt::MgmtEvent::Opcode::NEW_LONG_TERM_KEY
@ NEW_LONG_TERM_KEY
jau::cow_rw_iterator::end
constexpr cow_rw_iterator end() const noexcept
Returns a new iterator pointing to the element following the last element, aka end.
Definition: cow_iterator.hpp:265
direct_bt::MgmtEvent::Opcode::DEVICE_DISCONNECTED
@ DEVICE_DISCONNECTED
direct_bt::EInfoReport::setSource
void setSource(Source s) noexcept
Definition: BTTypes0.hpp:864
direct_bt::BTAdapter::isDeviceWhitelisted
bool isDeviceWhitelisted(const BDAddressAndType &addressAndType) noexcept
Returns true, if the adapter's device is already whitelisted.
Definition: BTAdapter.cpp:518
direct_bt::LE_Features::NONE
@ NONE
NONE.
_adapterStatusListenerRefEqComparator
static jau::cow_darray< impl::StatusListenerPair >::equal_comparator _adapterStatusListenerRefEqComparator
Definition: BTAdapter.cpp:545
direct_bt::MgmtEvtHCIEncryptionChanged
mgmt_addr_info { EUI48, uint8_t type }, HCIStatusCode status (1 Octet) uint8_t enc_enabled (1 Octet)
Definition: MgmtTypes.hpp:2097
direct_bt::MgmtEvent::Opcode::USER_PASSKEY_REQUEST
@ USER_PASSKEY_REQUEST
direct_bt::BTAdapter::removeAllStatusListener
int removeAllStatusListener()
Remove all status listener from the list.
Definition: BTAdapter.cpp:649
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::MgmtEvtNewSettings
uint32_t settings
Definition: MgmtTypes.hpp:1371
jau::cow_darray::begin
constexpr iterator begin()
Returns an jau::cow_rw_iterator to the first element of this CoW storage.
Definition: cow_darray.hpp:659
le_scan_interval
static const uint16_t le_scan_interval
Definition: dbt_scanner10.cpp:633
direct_bt::HCIACLData::l2cap_frame
Representing ACL Datas' L2CAP Frame.
Definition: HCITypes.hpp:672
direct_bt::EIRDataType
EIRDataType
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
Definition: BTTypes0.hpp:752
direct_bt::BTAdapter::reset
HCIStatusCode reset() noexcept
Reset the adapter.
Definition: BTAdapter.cpp:489
jau::cow_darray::erase_matching
constexpr_atomic int erase_matching(const value_type &x, const bool all_matching, equal_comparator comparator)
Erase either the first matching element or all matching elements.
Definition: cow_darray.hpp:986
direct_bt::MgmtEvtPairDeviceComplete
mgmt_addr_info { EUI48, uint8_t type }, MgmtStatus (1 octet)
Definition: MgmtTypes.hpp:2042
direct_bt::EInfoReport
Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) or 'Extended Inquiry Respons...
Definition: BTTypes0.hpp:803
direct_bt::MgmtEvent::toString
std::string toString() const noexcept override
Definition: MgmtTypes.hpp:1222
direct_bt::HCIHandler::use_ext_conn
bool use_ext_conn() const noexcept
Use extended connection if HCI_LE_Extended_Create_Connection is supported (Bluetooth 5....
Definition: HCIHandler.hpp:385
direct_bt::BTAdapter::getBTMode
BTMode getBTMode() const noexcept
Returns the current BTMode of this adapter.
Definition: BTAdapter.hpp:487
direct_bt::HCIHandler::setCurrentScanType
void setCurrentScanType(const ScanType v) noexcept
Definition: HCIHandler.hpp:390
basic_algos.hpp
direct_bt::MgmtEvtDiscovering
Definition: MgmtTypes.hpp:1818
direct_bt::changeScanType
constexpr ScanType changeScanType(const ScanType current, const ScanType changeType, const bool changeEnable) noexcept
Definition: BTTypes0.hpp:321
direct_bt::getAdapterSettingMaskDiff
constexpr AdapterSetting getAdapterSettingMaskDiff(const AdapterSetting setting_a, const AdapterSetting setting_b) noexcept
Definition: BTTypes1.hpp:175
BTManager.hpp
direct_bt::HCIStatusCode::NOT_POWERED
@ NOT_POWERED
jau::IllegalArgumentException
Definition: basic_types.hpp:111
direct_bt::EInfoReport::toString
std::string toString(const bool includeServices=true) const noexcept
Definition: BTTypes0.cpp:911
direct_bt
Definition: ATTPDUTypes.hpp:171
direct_bt::BTMode::NONE
@ NONE
Zero mode, neither DUAL, BREDR nor LE.
direct_bt::MgmtLongTermKeyInfo::enc_size
uint8_t enc_size
Encryption Size.
Definition: MgmtTypes.hpp:267
direct_bt::MgmtEvent::Opcode::USER_CONFIRM_REQUEST
@ USER_CONFIRM_REQUEST
direct_bt::AdapterStatusListener
BTAdapter status listener for BTDevice discovery events: Added, updated and removed; as well as for c...
Definition: BTAdapter.hpp:67
direct_bt::BTAdapter::removeDiscoveredDevices
int removeDiscoveredDevices() noexcept
Discards all discovered devices.
Definition: BTAdapter.cpp:877
direct_bt::MgmtCommand::Opcode::SET_BONDABLE
@ SET_BONDABLE
direct_bt::HCIWhitelistConnectType
HCIWhitelistConnectType
HCI Whitelist connection type.
Definition: BTTypes0.hpp:415
direct_bt::BTAdapter::setBondable
bool setBondable(bool value) noexcept
Set the bondable (aka pairable) state of the adapter.
Definition: BTAdapter.cpp:357
direct_bt::HCIHandler::toString
std::string toString() const noexcept
Definition: HCIHandler.cpp:814
direct_bt::BTManager::removeMgmtEventCallback
int removeMgmtEventCallback(const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept
Returns count of removed given MgmtEventCallback from the named MgmtEvent::Opcode list.
Definition: BTManager.cpp:1126
direct_bt::BTObject::isValid
bool isValid() const noexcept
Returns whether the object's reference is valid and in a general operational state.
Definition: BTTypes1.hpp:64
direct_bt::MgmtLongTermKeyInfo::key_type
MgmtLTKType key_type
Describing type of key, i.e.
Definition: MgmtTypes.hpp:267
direct_bt::BTAdapter::setPowered
bool setPowered(bool value) noexcept
Set the power state of the adapter.
Definition: BTAdapter.cpp:362
direct_bt::BDAddressAndType::toString
std::string toString() const noexcept
Definition: BTTypes0.cpp:333
direct_bt::BTAdapter::printDeviceLists
void printDeviceLists() noexcept
Print the internally maintained BTDevice lists to stderr:
Definition: BTAdapter.cpp:325
direct_bt::MgmtEvtLocalNameChanged
uint8_t name[MGMT_MAX_NAME_LENGTH]; uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
Definition: MgmtTypes.hpp:1397
direct_bt::BTAdapter::close
void close() noexcept
Closes this instance, usually being called by destructor or when this adapter is being removed as rec...
Definition: BTAdapter.cpp:252
direct_bt::MgmtEvtDeviceConnected
mgmt_addr_info { EUI48, uint8_t type }, uint32_t flags, uint16_t eir_len; uint8_t *eir
Definition: MgmtTypes.hpp:1492
direct_bt::MgmtEvtDeviceFound::getEIR
const EInfoReport * getEIR() const noexcept
Returns reference to the immutable EInfoReport, assuming creation occurred via HCIHandler.
Definition: MgmtTypes.hpp:1803
direct_bt::BTAdapter::getDiscoveredDevices
jau::darray< std::shared_ptr< BTDevice > > getDiscoveredDevices() const noexcept
Returns discovered devices from the last discovery.
Definition: BTAdapter.cpp:901
direct_bt::HCIHandler::getCurrentScanType
ScanType getCurrentScanType() const noexcept
Definition: HCIHandler.hpp:389
direct_bt::HCIHandler::le_start_scan
HCIStatusCode le_start_scan(const bool filter_dup=true, const bool le_scan_active=false, const HCILEOwnAddressType own_mac_type=HCILEOwnAddressType::PUBLIC, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint8_t filter_policy=0x00) noexcept
Start LE scanning, i.e.
Definition: HCIHandler.cpp:1045
direct_bt::ScanType
ScanType
Meta ScanType as derived from BTMode, with defined value mask consisting of BDAddressType bits.
Definition: BTTypes0.hpp:294
jau::cow_darray::size
constexpr_atomic size_type size() const noexcept
Like std::vector::size().
Definition: cow_darray.hpp:716
direct_bt::BTAdapter::removeDiscoveredDevice
bool removeDiscoveredDevice(const BDAddressAndType &addressAndType) noexcept
Discards matching discovered devices.
Definition: BTAdapter.cpp:860
direct_bt::BTDevice::toString
std::string toString() const noexcept override
Definition: BTDevice.hpp:303
direct_bt::BTDevice::getAddressAndType
constexpr BDAddressAndType const & getAddressAndType() const noexcept
Returns the unique device EUI48 address and BDAddressType type.
Definition: BTDevice.hpp:278
direct_bt::MgmtEvtUserConfirmRequest
mgmt_addr_info { EUI48, uint8_t type }, uint8_t confirm_hint uint32_t value
Definition: MgmtTypes.hpp:1696
direct_bt::MgmtEvent::Opcode::PIN_CODE_REQUEST
@ PIN_CODE_REQUEST
direct_bt::EInfoReport::read_data
int read_data(uint8_t const *data, uint8_t const data_length) noexcept
Reads the Extended Inquiry Response (EIR) or (Extended) Advertising Data (EAD or AD) segments and ret...
Definition: BTTypes0.cpp:993
direct_bt::BTAdapter::dev_id
const uint16_t dev_id
Adapter's internal temporary device id.
Definition: BTAdapter.hpp:273
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::EInfoReport::getAddressType
BDAddressType getAddressType() const noexcept
Definition: BTTypes0.hpp:940
direct_bt::MgmtEvent::Opcode::DISCOVERING
@ DISCOVERING
direct_bt::BTAdapter::isPowered
bool isPowered() const noexcept
Returns whether the adapter is valid, plugged in and powered.
Definition: BTAdapter.hpp:437
direct_bt::MgmtEvent
uint16_t opcode, uint16_t dev-id, uint16_t param_size
Definition: MgmtTypes.hpp:1083
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
direct_bt::BTAdapter::removeStatusListener
bool removeStatusListener(std::shared_ptr< AdapterStatusListener > l)
Remove the given listener from the list.
Definition: BTAdapter.cpp:585
direct_bt::BTAdapter::findSharedDevice
std::shared_ptr< BTDevice > findSharedDevice(const EUI48 &address, const BDAddressType addressType) noexcept
Returns shared BTDevice if found, otherwise nullptr.
Definition: BTAdapter.cpp:934
E_FILE_LINE
#define E_FILE_LINE
Definition: basic_types.hpp:64
direct_bt::MgmtEvtAuthFailed
mgmt_addr_info { EUI48, uint8_t type }, uint8_t status
Definition: MgmtTypes.hpp:1738
ABORT
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ FILE:LINE: '...
Definition: debug.hpp:124
jau::darray< std::shared_ptr< BTDevice > >
direct_bt::BTObject::valid
std::atomic_bool valid
Definition: BTTypes1.hpp:50
direct_bt::MgmtEvtDeviceUnpaired
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:1875
direct_bt::MgmtEvent::Opcode::AUTH_FAILED
@ AUTH_FAILED
direct_bt::BTAdapter::printStatusListenerList
void printStatusListenerList() noexcept
Definition: BTAdapter.cpp:340
direct_bt::BTManager::isDeviceWhitelisted
bool isDeviceWhitelisted(const uint16_t dev_id, const BDAddressAndType &addressAndType) noexcept
Returns true, if the adapter's device is already whitelisted.
Definition: BTManager.cpp:962
direct_bt::LE_Features
LE_Features
HCI Supported Commands.
Definition: BTTypes0.hpp:106
direct_bt::MgmtLongTermKeyInfo::address_type
BDAddressType address_type
0 reserved, 1 le public, 2 le static random address.
Definition: MgmtTypes.hpp:267
BTAdapter.hpp
direct_bt::AdapterInfo::getCurrentSettingMask
AdapterSetting getCurrentSettingMask() const noexcept
Definition: BTTypes1.hpp:272
direct_bt::getAdapterSettingsBTMode
BTMode getAdapterSettingsBTMode(const AdapterSetting settingMask) noexcept
Maps the given AdapterSetting to BTMode.
Definition: BTTypes1.cpp:99
le_scan_window
static const uint16_t le_scan_window
Definition: dbt_scanner10.cpp:634
direct_bt::BDAddressAndType::type
BDAddressType type
Definition: BTAddress.hpp:429
direct_bt::hasScanType
constexpr bool hasScanType(const ScanType current, const ScanType testType) noexcept
Definition: BTTypes0.hpp:324
direct_bt::BTAdapter::toString
std::string toString() const noexcept override
Definition: BTAdapter.hpp:784
jau::environment::getBooleanProperty
static bool getBooleanProperty(const std::string &name, const bool default_value) noexcept
Returns the boolean value of the environment's variable 'name', or the 'default_value' if the environ...
Definition: environment.cpp:80
direct_bt::BTManager::addDeviceToWhitelist
bool addDeviceToWhitelist(const uint16_t dev_id, const BDAddressAndType &addressAndType, const HCIWhitelistConnectType ctype) noexcept
Add the given device to the adapter's autoconnect whitelist.
Definition: BTManager.cpp:973
direct_bt::BTManager::setIOCapability
bool setIOCapability(const uint16_t dev_id, const SMPIOCapability io_cap, SMPIOCapability &pre_io_cap) noexcept
Definition: BTManager.cpp:691
direct_bt::HCILocalVersion::toString
std::string toString() noexcept
Definition: HCITypes.cpp:336
WARN_PRINT
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE: '.
Definition: debug.hpp:146
direct_bt::BTManager::removeDeviceFromWhitelist
bool removeDeviceFromWhitelist(const uint16_t dev_id, const BDAddressAndType &addressAndType) noexcept
Remove the given device from the adapter's autoconnect whitelist.
Definition: BTManager.cpp:1019
direct_bt::HCIHandler::reset
HCIStatusCode reset() noexcept
HCI Reset Command.
Definition: HCIHandler.cpp:884
direct_bt::to_HCILEOwnAddressType
HCILEOwnAddressType to_HCILEOwnAddressType(const BDAddressType addrType) noexcept
Definition: BTTypes0.cpp:89
direct_bt::BTManager::isOpen
bool isOpen() const noexcept
Returns true if this mgmt instance is open and hence valid, otherwise false.
Definition: BTManager.hpp:349
direct_bt::HCIHandler::use_ext_scan
bool use_ext_scan() const noexcept
Use extended scanning if HCI_LE_Set_Extended_Scan_Parameters and HCI_LE_Set_Extended_Scan_Enable is s...
Definition: HCIHandler.hpp:379
direct_bt::MgmtEvtLocalNameChanged::getName
const std::string getName() const noexcept
Definition: MgmtTypes.hpp:1419
direct_bt::MgmtEvent::Opcode::LOCAL_NAME_CHANGED
@ LOCAL_NAME_CHANGED
direct_bt::MgmtEvent::Opcode::CONNECT_FAILED
@ CONNECT_FAILED
jau::sc_atomic_critical
This class provides a RAII-style Sequentially Consistent (SC) data race free (DRF) critical block.
Definition: ordered_atomic.hpp:314
direct_bt::SMPPDUMsg
Handles the Security Manager Protocol (SMP) using Protocol Data Unit (PDU) encoded messages over L2CA...
Definition: SMPTypes.hpp:644
direct_bt::EInfoReport::getAddress
EUI48 const & getAddress() const noexcept
Definition: BTTypes0.hpp:941
_print_device_lists
constexpr static const bool _print_device_lists
Definition: BTAdapter.cpp:53
jau::bindMemberFunc
jau::FunctionDef< R, A... > bindMemberFunc(C *base, R(C::*mfunc)(A...)) noexcept
Definition: function_def.hpp:361
jau::darray::begin
constexpr iterator begin() noexcept
Definition: darray.hpp:606
direct_bt::MgmtEvtNewLongTermKey
uint8_t store_hint, MgmtLongTermKeyInfo key
Definition: MgmtTypes.hpp:1460
jau::darray::size
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:668
direct_bt::EInfoReport::Source::EIR
@ EIR
Extended Inquiry Response (EIR)
direct_bt::BTAdapter::findDiscoveredDevice
std::shared_ptr< BTDevice > findDiscoveredDevice(const EUI48 &address, const BDAddressType addressType) noexcept
Returns shared BTDevice if found, otherwise nullptr.
Definition: BTAdapter.cpp:843
direct_bt::PairingMode
PairingMode
Bluetooth secure pairing mode.
Definition: BTTypes0.hpp:261
direct_bt::MgmtEvtUserPasskeyRequest
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:1726
direct_bt::BTManager
A thread safe singleton handler of the Linux Kernel's BlueZ manager control channel.
Definition: BTManager.hpp:201
direct_bt::EInfoReport::setAddressType
void setAddressType(BDAddressType at) noexcept
Definition: BTTypes0.cpp:859
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::BTAdapter::addStatusListener
bool addStatusListener(std::shared_ptr< AdapterStatusListener > l)
Add the given listener to the list if not already present.
Definition: BTAdapter.cpp:548
direct_bt::impl::StatusListenerPair::listener
std::shared_ptr< AdapterStatusListener > listener
The actual listener.
Definition: BTAdapter.hpp:234
direct_bt::BTDevice::addressAndType
const BDAddressAndType addressAndType
Device's unique mac address and type tuple.
Definition: BTDevice.hpp:222
le_scan_active
static bool le_scan_active
Definition: dbt_scanner10.cpp:632
direct_bt::HCIHandler::addMgmtEventCallback
bool addMgmtEventCallback(const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept
MgmtEventCallback handling
Definition: HCIHandler.cpp:1558
direct_bt::BTAdapter::BTAdapter
BTAdapter(const BTAdapter::ctor_cookie &cc, BTManager &mgmt_, const AdapterInfo &adapterInfo_) noexcept
Private ctor for private BTAdapter::make_shared() intended for friends.
Definition: BTAdapter.cpp:224
direct_bt::HCIHandler::getLocalVersion
HCIStatusCode getLocalVersion(HCILocalVersion &version) noexcept
Definition: HCIHandler.cpp:904
direct_bt::BTDevice::isConnSecurityAutoEnabled
bool isConnSecurityAutoEnabled() const noexcept
Returns true if automatic security negotiation has been enabled via setConnSecurityAuto(),...
Definition: BTDevice.cpp:1299
debug.hpp
direct_bt::MgmtStatus::SUCCESS
@ SUCCESS
direct_bt::MgmtEvtHCIEncryptionKeyRefreshComplete
mgmt_addr_info { EUI48, uint8_t type }, HCIStatusCode status (1 Octet)
Definition: MgmtTypes.hpp:2137
direct_bt::HCIHandler::isOpen
bool isOpen() const noexcept
Returns true if this mgmt instance is open, connected and hence valid, otherwise false.
Definition: HCIHandler.hpp:374
direct_bt::MgmtEvtHCILERemoteUserFeatures
mgmt_addr_info { EUI48, uint8_t type }, uint64_t features (8 Octets)
Definition: MgmtTypes.hpp:2171
direct_bt::MgmtEvtDeviceDisconnected
mgmt_addr_info { EUI48, uint8_t type }, uint8_t reason
Definition: MgmtTypes.hpp:1539
direct_bt::BTAdapter::setLocalName
std::shared_ptr< NameAndShortName > setLocalName(const std::string &name, const std::string &short_name) noexcept
Sets the local friendly name.
Definition: BTAdapter.cpp:348
direct_bt::to_string
std::string to_string(const BDAddressType type) noexcept
Definition: BTTypes0.cpp:129
direct_bt::BTAdapter::~BTAdapter
~BTAdapter() noexcept
Releases this instance.
Definition: BTAdapter.cpp:238
direct_bt::HCIStatusCode::UNSPECIFIED_ERROR
@ UNSPECIFIED_ERROR
direct_bt::ScanType::LE
@ LE
direct_bt::HCIHandler::resetAllStates
void resetAllStates(const bool powered_on) noexcept
Reset all internal states, i.e.
Definition: HCIHandler.cpp:168
direct_bt::MgmtEvent::Opcode::PAIR_DEVICE_COMPLETE
@ PAIR_DEVICE_COMPLETE
jau::getCurrentMilliseconds
uint64_t getCurrentMilliseconds() noexcept
Returns current monotonic time in milliseconds.
Definition: basic_types.cpp:47
direct_bt::HCIHandler::close
void close() noexcept
Definition: HCIHandler.cpp:766
direct_bt::MgmtEvent::Opcode::DEVICE_CONNECTED
@ DEVICE_CONNECTED
direct_bt::BDAddressType
BDAddressType
BT Core Spec v5.2: Vol 3, Part C Generic Access Profile (GAP): 15.1.1.1 Public Bluetooth address.
Definition: BTAddress.hpp:51
direct_bt::AdapterSetting::NONE
@ NONE
direct_bt::MgmtEvtPinCodeRequest
mgmt_addr_info { EUI48, uint8_t type }, uint8_t secure
Definition: MgmtTypes.hpp:1664
jau::PLAIN_PRINT
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition: debug.cpp:176
direct_bt::EUI48
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: BTAddress.hpp:388
direct_bt::BDAddressAndType::address
EUI48 address
Definition: BTAddress.hpp:428
direct_bt::EInfoReport::getTimestamp
uint64_t getTimestamp() const noexcept
Definition: BTTypes0.hpp:932
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::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::AdapterInfo::isCurrentSettingBitSet
bool isCurrentSettingBitSet(const AdapterSetting bit) const noexcept
Definition: BTTypes1.hpp:273
direct_bt::SMPIOCapability
SMPIOCapability
Vol 3, Part H, 2.3.2 IO capabilities.
Definition: SMPTypes.hpp:185
direct_bt::BTAdapter::isValid
bool isValid() const noexcept
Returns whether the adapter is valid, i.e.
Definition: BTAdapter.hpp:480
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
direct_bt::BTAdapter::setDiscoverable
bool setDiscoverable(bool value) noexcept
Set the discoverable state of the adapter.
Definition: BTAdapter.cpp:352
direct_bt::HCIHandler::addSMPMsgCallback
void addSMPMsgCallback(const HCISMPMsgCallback &l)
Definition: HCIHandler.cpp:1597
direct_bt::MgmtLongTermKeyInfo::address
EUI48 address
Definition: MgmtTypes.hpp:267
direct_bt::HCILocalVersion
Definition: HCITypes.hpp:1108
direct_bt::HCIStatusCode::SUCCESS
@ SUCCESS
direct_bt::BTManager::uploadConnParam
bool uploadConnParam(const uint16_t dev_id, const BDAddressAndType &addressAndType, const uint16_t conn_interval_min=12, const uint16_t conn_interval_max=12, const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 15)) noexcept
Uploads given connection parameter for given device to the kernel.
Definition: BTManager.cpp:806
direct_bt::EInfoReport::setTimestamp
void setTimestamp(uint64_t ts) noexcept
Definition: BTTypes0.hpp:865
direct_bt::BTAdapter::addDeviceToWhitelist
bool addDeviceToWhitelist(const BDAddressAndType &addressAndType, const HCIWhitelistConnectType ctype, const uint16_t conn_interval_min=12, const uint16_t conn_interval_max=12, const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 15))
Add the given device to the adapter's autoconnect whitelist.
Definition: BTAdapter.cpp:522
direct_bt::HCIHandler::setBTMode
void setBTMode(const BTMode mode) noexcept
Definition: HCIHandler.hpp:371
direct_bt::MgmtCommand::Opcode::SET_POWERED
@ SET_POWERED
direct_bt::MgmtEvtDiscovering::getScanType
ScanType getScanType() const noexcept
Definition: MgmtTypes.hpp:1841
direct_bt::BTDevice::getConnected
bool getConnected() noexcept
Return true if the device has been successfully connected, otherwise false.
Definition: BTDevice.hpp:359
direct_bt::MgmtEvent::Opcode::HCI_ENC_KEY_REFRESH_COMPLETE
@ HCI_ENC_KEY_REFRESH_COMPLETE
direct_bt::AdapterInfo::toString
std::string toString() const noexcept
Definition: BTTypes1.hpp:282
HCIComm.hpp
filter_policy
static const uint8_t filter_policy
Definition: dbt_scanner10.cpp:635
direct_bt::to_HCIStatusCode
HCIStatusCode to_HCIStatusCode(const MgmtStatus mstatus) noexcept
Definition: MgmtTypes.cpp:543
direct_bt::BTManager::addMgmtEventCallback
bool addMgmtEventCallback(const int dev_id, const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept
MgmtEventCallback handling
Definition: BTManager.cpp:1117
direct_bt::MgmtEvtDeviceFound
mgmt_addr_info { EUI48, uint8_t type }, int8_t rssi, uint32_t flags, uint16_t eir_len; uint8_t *eir
Definition: MgmtTypes.hpp:1770
direct_bt::BTAdapter::startDiscovery
HCIStatusCode startDiscovery(const bool keepAlive=true, const bool le_scan_active=false, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint8_t filter_policy=0x00)
Starts a new discovery session.
Definition: BTAdapter.cpp:679
direct_bt::MgmtEvent::Opcode::DEVICE_FOUND
@ DEVICE_FOUND
WORDY_PRINT
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:91
direct_bt::AdapterSetting::POWERED
@ POWERED
direct_bt::AdapterInfo
Definition: BTTypes1.hpp:188
direct_bt::SMPPairingState
SMPPairingState
SMP Pairing Process state definition.
Definition: SMPTypes.hpp:101
direct_bt::HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION
@ REMOTE_USER_TERMINATED_CONNECTION
jau::cow_rw_iterator::erase
constexpr void erase()
Erases the element at the current position.
Definition: cow_iterator.hpp:488
jau::darray::erase
constexpr iterator erase(const_iterator pos)
Like std::vector::erase(), removes the elements at pos.
Definition: darray.hpp:832
direct_bt::HCIHandler::le_enable_scan
HCIStatusCode le_enable_scan(const bool enable, const bool filter_dup=true) noexcept
Starts or stops LE scanning.
Definition: HCIHandler.cpp:1001
direct_bt::isAdapterSettingBitSet
constexpr bool isAdapterSettingBitSet(const AdapterSetting mask, const AdapterSetting bit) noexcept
Definition: BTTypes1.hpp:177
direct_bt::BDAddressType::BDADDR_LE_PUBLIC
@ BDADDR_LE_PUBLIC
Bluetooth LE public address.
direct_bt::BDAddressAndType
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:417
direct_bt::BTDevice
Definition: BTDevice.hpp:57
COND_PRINT
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
Definition: debug.hpp:165
direct_bt::impl::StatusListenerPair
Definition: BTAdapter.hpp:232
direct_bt::MgmtEvent::Opcode::DEVICE_UNPAIRED
@ DEVICE_UNPAIRED
direct_bt::MgmtLongTermKeyInfo
Used for MgmtLoadLongTermKeyCmd and MgmtEvtNewLongTermKey.
Definition: MgmtTypes.hpp:267
direct_bt::MgmtEvtDeviceConnectFailed
mgmt_addr_info { EUI48, uint8_t type }, uint8_t status
Definition: MgmtTypes.hpp:1621
direct_bt::MgmtEvent::Opcode::HCI_LE_REMOTE_USR_FEATURES
@ HCI_LE_REMOTE_USR_FEATURES
direct_bt::BTMode
BTMode
Bluetooth adapter operating mode.
Definition: BTTypes0.hpp:56
direct_bt::EInfoReport::setAddress
void setAddress(EUI48 const &a) noexcept
Definition: BTTypes0.hpp:870
direct_bt::MgmtEvent::Opcode::NEW_SETTINGS
@ NEW_SETTINGS
jau::darray::clear
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: darray.hpp:796
direct_bt::BTAdapter::removeDeviceFromWhitelist
bool removeDeviceFromWhitelist(const BDAddressAndType &addressAndType)
Remove the given device from the adapter's autoconnect whitelist.
Definition: BTAdapter.cpp:541
DBG_PRINT
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:78
direct_bt::ScanType::NONE
@ NONE
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
direct_bt::HCIHandler::le_read_local_features
HCIStatusCode le_read_local_features(LE_Features &res) noexcept
Request and return LE_Features for the controller.
Definition: HCIHandler.cpp:927
direct_bt::BTDevice::getConnectionHandle
uint16_t getConnectionHandle() const noexcept
Return the HCI connection handle to the LE or BREDR peer, zero if not connected.
Definition: BTDevice.hpp:466
direct_bt::HCILEOwnAddressType
HCILEOwnAddressType
Definition: BTAddress.hpp:135
direct_bt::MgmtEvent::Opcode::HCI_ENC_CHANGED
@ HCI_ENC_CHANGED