Direct-BT v3.3.0-1-gc2d430c
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#include <random>
34
35#include <jau/debug.hpp>
36
37#include <jau/basic_algos.hpp>
38
39#include "BTIoctl.hpp"
40#include "HCIIoctl.hpp"
41#include "HCIComm.hpp"
42
43#include "BTAdapter.hpp"
44#include "BTManager.hpp"
45#include "DBTConst.hpp"
46
47extern "C" {
48 #include <inttypes.h>
49 #include <unistd.h>
50 #include <poll.h>
51}
52
53using namespace direct_bt;
54using namespace jau::fractions_i64_literals;
55
56constexpr static const bool _print_device_lists = false;
57
58std::string direct_bt::to_string(const DiscoveryPolicy v) noexcept {
59 switch(v) {
60 case DiscoveryPolicy::AUTO_OFF: return "AUTO_OFF";
61 case DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_DISCONNECTED: return "PAUSE_CONNECTED_UNTIL_DISCONNECTED";
62 case DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY: return "PAUSE_CONNECTED_UNTIL_READY";
63 case DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_PAIRED: return "PAUSE_CONNECTED_UNTIL_PAIRED";
64 case DiscoveryPolicy::ALWAYS_ON: return "ALWAYS_ON";
65 }
66 return "Unknown DiscoveryPolicy "+jau::to_hexstring(number(v));
67}
68
69BTDeviceRef BTAdapter::findDevice(HCIHandler& hci, device_list_t & devices, const EUI48 & address, const BDAddressType addressType) noexcept {
70 BDAddressAndType rpa(address, addressType);
71 const jau::nsize_t size = devices.size();
72 for (jau::nsize_t i = 0; i < size; ++i) {
73 BTDeviceRef & e = devices[i];
74 if ( nullptr != e &&
75 (
76 ( address == e->getAddressAndType().address &&
77 ( addressType == e->getAddressAndType().type || addressType == BDAddressType::BDADDR_UNDEFINED )
78 ) ||
79 ( address == e->getVisibleAddressAndType().address &&
80 ( addressType == e->getVisibleAddressAndType().type || addressType == BDAddressType::BDADDR_UNDEFINED )
81 )
82 )
83 )
84 {
85 if( !rpa.isIdentityAddress() ) {
86 e->updateVisibleAddress(rpa);
87 hci.setResolvHCIConnectionAddr(rpa, e->getAddressAndType());
88 }
89 return e;
90 }
91 }
92 if( !rpa.isIdentityAddress() ) {
93 for (jau::nsize_t i = 0; i < size; ++i) {
94 BTDeviceRef & e = devices[i];
95 if ( nullptr != e && e->matches_irk(rpa) ) {
96 e->updateVisibleAddress(rpa);
97 hci.setResolvHCIConnectionAddr(rpa, e->getAddressAndType());
98 return e;
99 }
100 }
101 }
102 return nullptr;
103}
104
105BTDeviceRef BTAdapter::findDevice(device_list_t & devices, BTDevice const & device) noexcept {
106 const jau::nsize_t size = devices.size();
107 for (jau::nsize_t i = 0; i < size; ++i) {
108 BTDeviceRef & e = devices[i];
109 if ( nullptr != e && device == *e ) {
110 return e;
111 }
112 }
113 return nullptr;
114}
115
116BTDeviceRef BTAdapter::findWeakDevice(weak_device_list_t & devices, const EUI48 & address, const BDAddressType addressType) noexcept {
117 auto end = devices.end();
118 for (auto it = devices.begin(); it != end; ) {
119 std::weak_ptr<BTDevice> & w = *it;
120 BTDeviceRef e = w.lock();
121 if( nullptr == e ) {
122 devices.erase(it); // erase and move it to next element
123 } else if ( ( address == e->getAddressAndType().address &&
124 ( addressType == e->getAddressAndType().type || addressType == BDAddressType::BDADDR_UNDEFINED )
125 ) ||
126 ( address == e->getVisibleAddressAndType().address &&
127 ( addressType == e->getVisibleAddressAndType().type || addressType == BDAddressType::BDADDR_UNDEFINED )
128 )
129 )
130 {
131 return e;
132 } else {
133 ++it; // move it to next element
134 }
135 }
136 return nullptr;
137}
138
139BTDeviceRef BTAdapter::findWeakDevice(weak_device_list_t & devices, BTDevice const & device) noexcept {
140 auto end = devices.end();
141 for (auto it = devices.begin(); it != end; ) {
142 std::weak_ptr<BTDevice> & w = *it;
143 BTDeviceRef e = w.lock();
144 if( nullptr == e ) {
145 devices.erase(it); // erase and move it to next element
146 } else if ( device == *e ) {
147 return e;
148 } else {
149 ++it; // move it to next element
150 }
151 }
152 return nullptr;
153}
154
155BTDeviceRef BTAdapter::findDevicePausingDiscovery (const EUI48 & address, const BDAddressType & addressType) noexcept {
156 const std::lock_guard<std::mutex> lock(mtx_pausingDiscoveryDevices); // RAII-style acquire and relinquish via destructor
157 return findWeakDevice(pausing_discovery_devices, address, addressType);
158}
159
160bool BTAdapter::addDevicePausingDiscovery(const BTDeviceRef & device) noexcept {
161 bool added_first = false;
162 {
163 const std::lock_guard<std::mutex> lock(mtx_pausingDiscoveryDevices); // RAII-style acquire and relinquish via destructor
164 if( nullptr != findWeakDevice(pausing_discovery_devices, *device) ) {
165 return false;
166 }
167 added_first = 0 == pausing_discovery_devices.size();
168 pausing_discovery_devices.push_back(device);
169 }
170 if( added_first ) {
171 if constexpr ( SCAN_DISABLED_POST_CONNECT ) {
172 updateDeviceDiscoveringState(ScanType::LE, false /* eventEnabled */);
173 } else {
174 std::thread bg(&BTAdapter::stopDiscoveryImpl, this, false /* forceDiscoveringEvent */, true /* temporary */); // @suppress("Invalid arguments")
175 bg.detach();
176 }
177 return true;
178 } else {
179 return false;
180 }
181}
182
183bool BTAdapter::removeDevicePausingDiscovery(const BTDevice & device) noexcept {
184 bool removed_last = false;
185 {
186 const std::lock_guard<std::mutex> lock(mtx_pausingDiscoveryDevices); // RAII-style acquire and relinquish via destructor
187 auto end = pausing_discovery_devices.end();
188 for (auto it = pausing_discovery_devices.begin(); it != end; ) {
189 std::weak_ptr<BTDevice> & w = *it;
190 BTDeviceRef e = w.lock();
191 if( nullptr == e ) {
192 pausing_discovery_devices.erase(it); // erase and move it to next element
193 } else if ( device == *e ) {
194 pausing_discovery_devices.erase(it);
195 removed_last = 0 == pausing_discovery_devices.size();
196 break; // done
197 } else {
198 ++it; // move it to next element
199 }
200 }
201 }
202 if( removed_last ) {
203 discovery_service.start();
204 return true;
205 } else {
206 return false;
207 }
208}
209
210void BTAdapter::clearDevicesPausingDiscovery() noexcept {
211 const std::lock_guard<std::mutex> lock(mtx_pausingDiscoveryDevices); // RAII-style acquire and relinquish via destructor
212 pausing_discovery_devices.clear();
213}
214
215jau::nsize_t BTAdapter::getDevicesPausingDiscoveryCount() noexcept {
216 const std::lock_guard<std::mutex> lock(mtx_pausingDiscoveryDevices); // RAII-style acquire and relinquish via destructor
217 return pausing_discovery_devices.size();
218}
219
220bool BTAdapter::addConnectedDevice(const BTDeviceRef & device) noexcept {
221 const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
222 if( nullptr != findDevice(connectedDevices, *device) ) {
223 return false;
224 }
225 connectedDevices.push_back(device);
226 return true;
227}
228
229bool BTAdapter::removeConnectedDevice(const BTDevice & device) noexcept {
230 const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
231 auto end = connectedDevices.end();
232 for (auto it = connectedDevices.begin(); it != end; ++it) {
233 if ( nullptr != *it && device == **it ) {
234 connectedDevices.erase(it);
235 return true;
236 }
237 }
238 return false;
239}
240
241BTAdapter::size_type BTAdapter::disconnectAllDevices(const HCIStatusCode reason) noexcept {
242 device_list_t devices;
243 {
244 jau::sc_atomic_critical sync(sync_data); // SC-DRF via atomic acquire & release
245 devices = connectedDevices; // copy!
246 }
247 const size_type count = devices.size();
248 auto end = devices.end();
249 for (auto it = devices.begin(); it != end; ++it) {
250 if( nullptr != *it ) {
251 BTDevice& dev = **it;
252 dev.disconnect(reason); // will erase device from list via removeConnectedDevice(..) above, if successful
253 removeConnectedDevice(dev); // just in case disconnect didn't went through, e.g. power-off
254 }
255 }
256 return count;
257}
258
259BTDeviceRef BTAdapter::findConnectedDevice (const EUI48 & address, const BDAddressType & addressType) noexcept {
260 const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
261 return findDevice(hci, connectedDevices, address, addressType);
262}
263
264jau::nsize_t BTAdapter::getConnectedDeviceCount() const noexcept {
265 jau::sc_atomic_critical sync(sync_data); // SC-DRF via atomic acquire & release
266 return connectedDevices.size();
267}
268
269// *************************************************
270// *************************************************
271// *************************************************
272
273bool BTAdapter::updateDataFromHCI() noexcept {
274 HCILocalVersion version;
275 HCIStatusCode status = hci.getLocalVersion(version);
276 if( HCIStatusCode::SUCCESS != status ) {
277 ERR_PRINT("Adapter[%d]: POWERED, LocalVersion failed %s - %s",
278 dev_id, to_string(status).c_str(), adapterInfo.toString().c_str());
279 return false;
280 }
281 le_features = hci.le_get_local_features();
282 hci_uses_ext_scan = hci.use_ext_scan();
283 hci_uses_ext_conn = hci.use_ext_conn();
284 hci_uses_ext_adv = hci.use_ext_adv();
285
286 status = hci.le_set_addr_resolv_enable(true);
287 if( HCIStatusCode::SUCCESS != status ) {
288 jau::INFO_PRINT("Adapter[%d]: ENABLE RESOLV LIST: %s", dev_id, to_string(status).c_str());
289 }
290 status = hci.le_clear_resolv_list();
291 if( HCIStatusCode::SUCCESS != status ) {
292 jau::INFO_PRINT("Adapter[%d]: CLEAR RESOLV LIST: %s", dev_id, to_string(status).c_str());
293 }
294
295 WORDY_PRINT("BTAdapter::updateDataFromHCI: Adapter[%d]: POWERED, %s - %s, hci_ext[scan %d, conn %d], features: %s",
296 dev_id, version.toString().c_str(), adapterInfo.toString().c_str(),
297 hci_uses_ext_scan, hci_uses_ext_conn,
298 direct_bt::to_string(le_features).c_str());
299 return true;
300}
301
302bool BTAdapter::updateDataFromAdapterInfo() noexcept {
304 if( BTMode::NONE == btMode ) {
305 WARN_PRINT("Adapter[%d]: BTMode invalid, BREDR nor LE set: %s", dev_id, adapterInfo.toString().c_str());
306 return false;
307 }
308 hci.setBTMode(btMode);
309 return true;
310}
311
312bool BTAdapter::initialSetup() noexcept {
313 if( !mgmt->isOpen() ) {
314 ERR_PRINT("Adapter[%d]: Manager not open", dev_id);
315 return false;
316 }
317 if( !hci.isOpen() ) {
318 ERR_PRINT("Adapter[%d]: HCIHandler closed", dev_id);
319 return false;
320 }
321
322 old_settings = adapterInfo.getCurrentSettingMask();
323
324 if( !updateDataFromAdapterInfo() ) {
325 return false;
326 }
327
329 if( !hci.resetAllStates(true) ) {
330 return false;
331 }
332 if( !updateDataFromHCI() ) {
333 return false;
334 }
335 } else {
336 hci.resetAllStates(false);
337 WORDY_PRINT("BTAdapter::initialSetup: Adapter[%d]: Not POWERED: %s", dev_id, adapterInfo.toString().c_str());
338 }
339 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
340 WORDY_PRINT("BTAdapter::initialSetup: Adapter[%d]: Done: %s - %s", dev_id, adapterInfo.toString().c_str(), toString().c_str());
341
342 return true;
343}
344
345bool BTAdapter::enableListening(const bool enable) noexcept {
346 if( enable ) {
347 if( !mgmt->isOpen() ) {
348 ERR_PRINT("Adapter[%d]: Manager not open", dev_id);
349 return false;
350 }
351 if( !hci.isOpen() ) {
352 ERR_PRINT("Adapter[%d]: HCIHandler closed", dev_id);
353 return false;
354 }
355
356 // just be sure ..
357 mgmt->removeMgmtEventCallback(dev_id);
358 hci.clearAllCallbacks();
359
360 bool ok = true;
361 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DISCOVERING, jau::bind_member(this, &BTAdapter::mgmtEvDeviceDiscoveringMgmt)) && ok;
362 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_SETTINGS, jau::bind_member(this, &BTAdapter::mgmtEvNewSettingsMgmt)) && ok;
363 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::LOCAL_NAME_CHANGED, jau::bind_member(this, &BTAdapter::mgmtEvLocalNameChangedMgmt)) && ok;
364 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::PIN_CODE_REQUEST, jau::bind_member(this, &BTAdapter::mgmtEvPinCodeRequestMgmt)) && ok;
365 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::USER_CONFIRM_REQUEST, jau::bind_member(this, &BTAdapter::mgmtEvUserConfirmRequestMgmt)) && ok;
366 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::USER_PASSKEY_REQUEST, jau::bind_member(this, &BTAdapter::mgmtEvUserPasskeyRequestMgmt)) && ok;
367 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::PASSKEY_NOTIFY, jau::bind_member(this, &BTAdapter::mgmtEvPasskeyNotifyMgmt)) && ok;
368 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::AUTH_FAILED, jau::bind_member(this, &BTAdapter::mgmtEvAuthFailedMgmt)) && ok;
369 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_UNPAIRED, jau::bind_member(this, &BTAdapter::mgmtEvDeviceUnpairedMgmt)) && ok;
370 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::PAIR_DEVICE_COMPLETE, jau::bind_member(this, &BTAdapter::mgmtEvPairDeviceCompleteMgmt)) && ok;
371 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_LONG_TERM_KEY, jau::bind_member(this, &BTAdapter::mgmtEvNewLongTermKeyMgmt)) && ok;
372 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_LINK_KEY, jau::bind_member(this, &BTAdapter::mgmtEvNewLinkKeyMgmt)) && ok;
373 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::NEW_IRK, jau::bind_member(this, &BTAdapter::mgmtEvNewIdentityResolvingKeyMgmt)) && ok;
374
375 if( debug_event || jau::environment::get().debug ) {
376 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_CONNECTED, jau::bind_member(this, &BTAdapter::mgmtEvDeviceConnectedMgmt)) && ok;
377 ok = mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bind_member(this, &BTAdapter::mgmtEvMgmtAnyMgmt)) && ok;
378 }
379
380 if( !ok ) {
381 ERR_PRINT("Could not add all required MgmtEventCallbacks to DBTManager: %s", toString().c_str());
382 return false;
383 }
384
385 #if 0
386 mgmt->addMgmtEventCallback(dev_id, MgmtEvent::Opcode::DEVICE_DISCONNECTED, bind_member(this, &BTAdapter::mgmtEvDeviceDisconnectedMgmt));
387 #endif
388
389 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DISCOVERING, jau::bind_member(this, &BTAdapter::mgmtEvDeviceDiscoveringHCI)) && ok;
390 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_CONNECTED, jau::bind_member(this, &BTAdapter::mgmtEvDeviceConnectedHCI)) && ok;
391 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::CONNECT_FAILED, jau::bind_member(this, &BTAdapter::mgmtEvConnectFailedHCI)) && ok;
392 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bind_member(this, &BTAdapter::mgmtEvDeviceDisconnectedHCI)) && ok;
393 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_FOUND, jau::bind_member(this, &BTAdapter::mgmtEvDeviceFoundHCI)) && ok;
394 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_REMOTE_FEATURES, jau::bind_member(this, &BTAdapter::mgmtEvHCILERemoteUserFeaturesHCI)) && ok;
395 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_PHY_UPDATE_COMPLETE, jau::bind_member(this, &BTAdapter::mgmtEvHCILEPhyUpdateCompleteHCI)) && ok;
396
397 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_CHANGED, jau::bind_member(this, &BTAdapter::mgmtEvHCIEncryptionChangedHCI)) && ok;
398 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_KEY_REFRESH_COMPLETE, jau::bind_member(this, &BTAdapter::mgmtEvHCIEncryptionKeyRefreshCompleteHCI)) && ok;
399 if constexpr ( CONSIDER_HCI_CMD_FOR_SMP_STATE ) {
400 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_LTK_REQUEST, jau::bind_member(this, &BTAdapter::mgmtEvLELTKReqEventHCI)) && ok;
401 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_LTK_REPLY_ACK, jau::bind_member(this, &BTAdapter::mgmtEvLELTKReplyAckCmdHCI)) && ok;
402 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_LTK_REPLY_REJ, jau::bind_member(this, &BTAdapter::mgmtEvLELTKReplyRejCmdHCI)) && ok;
403 ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_ENABLE_ENC, jau::bind_member(this, &BTAdapter::mgmtEvLEEnableEncryptionCmdHCI)) && ok;
404 }
405 if( !ok ) {
406 ERR_PRINT("Could not add all required MgmtEventCallbacks to HCIHandler: %s of %s", hci.toString().c_str(), toString().c_str());
407 return false; // dtor local HCIHandler w/ closing
408 }
409 hci.addSMPMsgCallback(jau::bind_member(this, &BTAdapter::hciSMPMsgCallback));
410 } else {
411 mgmt->removeMgmtEventCallback(dev_id);
412 hci.clearAllCallbacks();
413 }
414 WORDY_PRINT("BTAdapter::enableListening: Adapter[%d]: Done: %s - %s", dev_id, adapterInfo.toString().c_str(), toString().c_str());
415
416 return true;
417}
418
419BTAdapter::BTAdapter(const BTAdapter::ctor_cookie& cc, BTManagerRef mgmt_, AdapterInfo adapterInfo_) noexcept
420: debug_event(jau::environment::getBooleanProperty("direct_bt.debug.adapter.event", false)),
421 debug_lock(jau::environment::getBooleanProperty("direct_bt.debug.adapter.lock", false)),
422 mgmt( std::move(mgmt_) ),
423 adapterInfo( std::move(adapterInfo_) ),
424 adapter_initialized( false ), adapter_poweredoff_at_init( true ),
425 le_features( LE_Features::NONE ),
426 hci_uses_ext_scan( false ), hci_uses_ext_conn( false ), hci_uses_ext_adv( false ),
427 visibleAddressAndType( adapterInfo_.addressAndType ),
428 visibleMACType( HCILEOwnAddressType::PUBLIC ),
429 privacyIRK(),
430 dev_id( adapterInfo.dev_id ),
431 btRole ( BTRole::Master ),
432 hci( dev_id ),
433 currentMetaScanType( ScanType::NONE ),
434 discovery_policy ( DiscoveryPolicy::AUTO_OFF ),
435 scan_filter_dup( true ),
436 smp_watchdog("adapter"+std::to_string(dev_id)+"_smp_watchdog", THREAD_SHUTDOWN_TIMEOUT_MS),
437 l2cap_att_srv(dev_id, adapterInfo_.addressAndType, L2CAP_PSM::UNDEFINED, L2CAP_CID::ATT),
438 l2cap_service("BTAdapter::l2capServer", THREAD_SHUTDOWN_TIMEOUT_MS,
439 jau::bind_member(this, &BTAdapter::l2capServerWork),
440 jau::bind_member(this, &BTAdapter::l2capServerInit),
441 jau::bind_member(this, &BTAdapter::l2capServerEnd)),
442 discovery_service("BTAdapter::discoveryServer", 400_ms,
443 jau::bind_member(this, &BTAdapter::discoveryServerWork))
444
445{
446 (void)cc;
447
448 adapter_operational = initialSetup();
449 if( isValid() ) {
450 const bool r = smp_watchdog.start(SMP_NEXT_EVENT_TIMEOUT_MS, jau::bind_member(this, &BTAdapter::smp_timeoutfunc));
451 DBG_PRINT("BTAdapter::ctor: dev_id %d: smp_watchdog.smp_timeoutfunc started %d", dev_id, r);
452 }
453}
454
456 if( !isValid() ) {
457 DBG_PRINT("BTAdapter::dtor: dev_id %d, invalid, %p", dev_id, this);
458 smp_watchdog.stop();
459 mgmt->removeAdapter(this); // remove this instance from manager
460 hci.clearAllCallbacks();
461 return;
462 }
463 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
464 DBG_PRINT("BTAdapter::dtor: ... %p %s", this, toString().c_str());
465 close();
466
467 mgmt->removeAdapter(this); // remove this instance from manager
468
469 DBG_PRINT("BTAdapter::dtor: XXX");
470}
471
472void BTAdapter::close() noexcept {
473 smp_watchdog.stop();
474 if( !isValid() ) {
475 // Native user app could have destroyed this instance already from
476 DBG_PRINT("BTAdapter::close: dev_id %d, invalid, %p", dev_id, this);
477 return;
478 }
479 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
480 DBG_PRINT("BTAdapter::close: ... %p %s", this, toString().c_str());
481 discovery_policy = DiscoveryPolicy::AUTO_OFF;
482
483 // mute all listener first
484 {
485 size_type count = mgmt->removeMgmtEventCallback(dev_id);
486 DBG_PRINT("BTAdapter::close removeMgmtEventCallback: %zu callbacks", (size_t)count);
487 }
488 hci.clearAllCallbacks();
489 statusListenerList.clear();
490
491 poweredOff(true /* active */, "close");
492
493 if( adapter_poweredoff_at_init && isPowered() ) {
494 setPowered(false);
495 }
496
497 DBG_PRINT("BTAdapter::close: close[HCI, l2cap_srv]: ...");
498 hci.close();
499 l2cap_service.stop();
500 l2cap_att_srv.close();
501 discovery_service.stop();
502 DBG_PRINT("BTAdapter::close: close[HCI, l2cap_srv, discovery_srv]: XXX");
503
504 {
505 const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
506 discoveredDevices.clear();
507 }
508 {
509 const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
510 connectedDevices.clear();;
511 }
512 {
513 const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
514 sharedDevices.clear();
515 }
516 {
517 const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor
518 key_list.clear();
519 key_path.clear();
520 }
521 adapter_operational = false;
522 DBG_PRINT("BTAdapter::close: XXX");
523}
524
525void BTAdapter::poweredOff(bool active, const std::string& msg) noexcept {
526 if( !isValid() ) {
527 ERR_PRINT("BTAdapter invalid: dev_id %d, %p", dev_id, this);
528 return;
529 }
530 DBG_PRINT("BTAdapter::poweredOff(active %d, %s).0: ... %p, %s", active, msg.c_str(), this, toString().c_str());
531 if( jau::environment::get().debug ) {
532 if( !active ) {
533 jau::print_backtrace(true /* skip_anon_frames */, 4 /* max_frames */, 2 /* skip_frames: print_b*() + get_b*() */);
534 }
535 }
536 if( !hci.isOpen() ) {
537 jau::INFO_PRINT("BTAdapter::poweredOff: HCI closed: active %d -> 0: %s", active, toString().c_str());
538 active = false;
539 } else if( active && !adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED) ) {
540 DBG_PRINT("BTAdapter::poweredOff: !POWERED: active %d -> 0: %s", active, toString().c_str());
541 active = false;
542 }
544
545 if( active ) {
546 stopDiscoveryImpl(true /* forceDiscoveringEvent */, false /* temporary */);
547 }
548
549 // Removes all device references from the lists: connectedDevices, discoveredDevices
550 disconnectAllDevices(HCIStatusCode::NOT_POWERED);
551 removeDiscoveredDevices();
552
553 // ensure all hci states are reset.
554 hci.resetAllStates(false);
555
556 currentMetaScanType = ScanType::NONE;
557 btRole = BTRole::Master;
558
559 unlockConnectAny();
560
561 DBG_PRINT("BTAdapter::poweredOff(active %d, %s).X: %s", active, msg.c_str(), toString().c_str());
562}
563
564void BTAdapter::printDeviceList(const std::string& prefix, const BTAdapter::device_list_t& list) noexcept {
565 const size_t sz = list.size();
566 jau::PLAIN_PRINT(true, "- BTAdapter::%s: %zu elements", prefix.c_str(), sz);
567 int idx = 0;
568 for (auto it = list.begin(); it != list.end(); ++idx, ++it) {
569 /**
570 * TODO
571 *
572 * g++ (Debian 12.2.0-3) 12.2.0, Debian 12 Bookworm 2022-10-17
573 * g++ bug: False positive of '-Werror=stringop-overflow=' using std::atomic_bool::load()
574 *
575 In member function ‘std::__atomic_base<_IntTp>::__int_type std::__atomic_base<_IntTp>::load(std::memory_order) const [with _ITp = bool]’,
576 inlined from ‘bool std::atomic<bool>::load(std::memory_order) const’ at /usr/include/c++/12/atomic:112:26,
577 inlined from ‘bool direct_bt::BTObject::isValidInstance() const’ at direct_bt/api/direct_bt/BTTypes1.hpp:66:86,
578 inlined from ‘static void direct_bt::BTAdapter::printDeviceList(const std::string&, const device_list_t&)’ at direct_bt/BTAdapter.cpp:526:42:
579/usr/include/c++/12/bits/atomic_base.h:488:31: error:
580 * ‘unsigned char __atomic_load_1(const volatile void*, int)’ writing 1 byte into a region of size 0 overflows the destination [-Werror=stringop-overflow=]
581 488 | return __atomic_load_n(&_M_i, int(__m));
582 | ~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~
583 */
586 if( nullptr != (*it) ) {
587 jau::PLAIN_PRINT(true, " - %d / %zu: null", (idx+1), sz);
588 } else if( (*it)->isValidInstance() /** TODO: See above */ ) {
589 jau::PLAIN_PRINT(true, " - %d / %zu: invalid", (idx+1), sz);
590 } else {
591 jau::PLAIN_PRINT(true, " - %d / %zu: %s, name '%s', visible %s", (idx+1), sz,
592 (*it)->getAddressAndType().toString().c_str(),
593 (*it)->getName().c_str(),
594 (*it)->getVisibleAddressAndType().toString().c_str());
595 }
597 }
598}
599void BTAdapter::printWeakDeviceList(const std::string& prefix, BTAdapter::weak_device_list_t& list) noexcept {
600 const size_t sz = list.size();
601 jau::PLAIN_PRINT(true, "- BTAdapter::%s: %zu elements", prefix.c_str(), sz);
602 int idx = 0;
603 for (auto it = list.begin(); it != list.end(); ++idx, ++it) {
604 std::weak_ptr<BTDevice> & w = *it;
605 BTDeviceRef e = w.lock();
606 if( nullptr == e ) {
607 jau::PLAIN_PRINT(true, " - %d / %zu: null", (idx+1), sz);
608 } else if( !e->isValidInstance() ) {
609 jau::PLAIN_PRINT(true, " - %d / %zu: invalid", (idx+1), sz);
610 } else {
611 jau::PLAIN_PRINT(true, " - %d / %zu: %s, name '%s', visible %s", (idx+1), sz,
612 e->getAddressAndType().toString().c_str(),
613 e->getName().c_str(),
614 e->getVisibleAddressAndType().toString().c_str());
615 }
616 }
617}
619 // Using expensive copies here: debug mode only
620 weak_device_list_t _sharedDevices, _discoveredDevices, _connectedDevices, _pausingDiscoveryDevice;
621 {
622 const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
623 for(const BTDeviceRef& d : sharedDevices) { _sharedDevices.push_back(d); }
624 }
625 {
626 const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
627 for(const BTDeviceRef& d : discoveredDevices) { _discoveredDevices.push_back(d); }
628 }
629 {
630 const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
631 for(const BTDeviceRef& d : connectedDevices) { _connectedDevices.push_back(d); }
632 }
633 {
634 const std::lock_guard<std::mutex> lock(mtx_pausingDiscoveryDevices); // RAII-style acquire and relinquish via destructor
635 _pausingDiscoveryDevice = pausing_discovery_devices;
636 }
637 printWeakDeviceList("SharedDevices ", _sharedDevices);
638 printWeakDeviceList("ConnectedDevices ", _connectedDevices);
639 printWeakDeviceList("DiscoveredDevices ", _discoveredDevices);
640 printWeakDeviceList("PausingDiscoveryDevices ", _pausingDiscoveryDevice);
642}
643
645 auto begin = statusListenerList.begin(); // lock mutex and copy_store
646 jau::PLAIN_PRINT(true, "- BTAdapter::StatusListener : %zu elements", (size_t)begin.size());
647 for(int ii=0; !begin.is_end(); ++ii, ++begin ) {
648 jau::PLAIN_PRINT(true, " - %d / %zu: %p, %s", (ii+1), (size_t)begin.size(), begin->listener.get(), begin->listener->toString().c_str());
649 }
650}
651
652HCIStatusCode BTAdapter::setName(const std::string &name, const std::string &short_name) noexcept {
653 if( isAdapterSettingBitSet(adapterInfo.getCurrentSettingMask(), AdapterSetting::POWERED) ) {
655 }
656 std::shared_ptr<NameAndShortName> res = mgmt->setLocalName(dev_id, name, short_name);
657 return nullptr != res ? HCIStatusCode::SUCCESS : HCIStatusCode::FAILED;
658}
659
660bool BTAdapter::setPowered(const bool power_on) noexcept {
661 AdapterSetting settings = adapterInfo.getCurrentSettingMask();
662 if( power_on == isAdapterSettingBitSet(settings, AdapterSetting::POWERED) ) {
663 // unchanged
664 return true;
665 }
666 if( !mgmt->setMode(dev_id, MgmtCommand::Opcode::SET_POWERED, power_on ? 1 : 0, settings) ) {
667 return false;
668 }
669 const AdapterSetting new_settings = adapterInfo.setCurrentSettingMask(settings);
670 updateAdapterSettings(false /* off_thread */, new_settings, false /* sendEvent */, 0);
671 return power_on == isAdapterSettingBitSet(new_settings, AdapterSetting::POWERED);
672}
673
674HCIStatusCode BTAdapter::setPrivacy(const bool enable) noexcept {
676 if( enable ) {
677 std::unique_ptr<std::random_device> rng_hw = std::make_unique<std::random_device>();
678 for(int i=0; i<4; ++i) {
679 std::uint32_t v = (*rng_hw)();
680 irk.data[4*i+0] = v & 0x000000ffu;
681 irk.data[4*i+1] = ( v & 0x0000ff00u ) >> 8;
682 irk.data[4*i+2] = ( v & 0x00ff0000u ) >> 16;
683 irk.data[4*i+3] = ( v & 0xff000000u ) >> 24;
684 }
685 } else {
686 irk.clear();
687 }
688 AdapterSetting settings = adapterInfo.getCurrentSettingMask();
689 HCIStatusCode res = mgmt->setPrivacy(dev_id, enable ? 0x01 : 0x00, irk, settings);
690 if( HCIStatusCode::SUCCESS == res ) {
691 if( enable ) {
692 // FIXME: Not working for LE set scan param etc ..
693 // TODO visibleAddressAndType = ???
694 // visibleMACType = HCILEOwnAddressType::RESOLVABLE_OR_RANDOM;
696 // visibleMACType = HCILEOwnAddressType::RANDOM;
697 } else {
698 visibleAddressAndType = adapterInfo.addressAndType;
699 visibleMACType = HCILEOwnAddressType::PUBLIC;
700 privacyIRK.address = adapterInfo.addressAndType.address;
701 privacyIRK.address_type = adapterInfo.addressAndType.type;
702 privacyIRK.irk.clear();
703 }
704 }
705 const AdapterSetting new_settings = adapterInfo.setCurrentSettingMask(settings);
706 updateAdapterSettings(false /* off_thread */, new_settings, true /* sendEvent */, 0);
707 return res;
708}
709
711 AdapterSetting settings = adapterInfo.getCurrentSettingMask();
714 }
715 if( enable == isAdapterSettingBitSet(settings, AdapterSetting::SECURE_CONN) ) {
716 // unchanged
718 }
719 if( !mgmt->setMode(dev_id, MgmtCommand::Opcode::SET_SECURE_CONN, enable ? 1 : 0, settings) ) {
721 }
722 const AdapterSetting new_settings = adapterInfo.setCurrentSettingMask(settings);
723 updateAdapterSettings(false /* off_thread */, new_settings, false /* sendEvent */, 0);
725}
726
727HCIStatusCode BTAdapter::setDefaultConnParam(const uint16_t conn_interval_min, const uint16_t conn_interval_max,
728 const uint16_t conn_latency, const uint16_t supervision_timeout) noexcept {
729 if( isAdapterSettingBitSet(adapterInfo.getCurrentSettingMask(), AdapterSetting::POWERED) ) {
731 }
732 return mgmt->setDefaultConnParam(dev_id, conn_interval_min, conn_interval_max, conn_latency, supervision_timeout);
733}
734
735void BTAdapter::setServerConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept {
736 BTDevice::validateSecParam(sec_level, io_cap, sec_level_server, io_cap_server);
737}
738
739void BTAdapter::setSMPKeyPath(std::string path) noexcept {
741 key_path = std::move(path);
742
743 std::vector<SMPKeyBin> keys = SMPKeyBin::readAllForLocalAdapter(getAddressAndType(), key_path, jau::environment::get().debug /* verbose_ */);
744 for(SMPKeyBin f : keys) {
745 uploadKeys(f, false /* write */);
746 }
747}
748
749HCIStatusCode BTAdapter::uploadKeys(SMPKeyBin& bin, const bool write) noexcept {
750 if( bin.getLocalAddrAndType() != adapterInfo.addressAndType ) {
751 if( bin.getVerbose() ) {
752 jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Adapter address not matching: %s, %s",
753 bin.toString().c_str(), toString().c_str());
754 }
756 }
757 EInfoReport ad_report;
758 {
759 ad_report.setSource( EInfoReport::Source::NA, false );
761 ad_report.setAddressType( bin.getRemoteAddrAndType().type );
762 ad_report.setAddress( bin.getRemoteAddrAndType().address );
763 }
764 // Enforce BTRole::Master on new device,
765 // since this functionality is only for local being BTRole::Slave peripheral!
766 BTDeviceRef device = BTDevice::make_shared(*this, ad_report);
767 device->btRole = BTRole::Master;
768 addSharedDevice(device);
769
770 HCIStatusCode res;
771 res = mgmt->unpairDevice(dev_id, bin.getRemoteAddrAndType(), false /* disconnect */);
773 ERR_PRINT("(dev_id %d): Unpair device failed %s of %s: %s",
774 dev_id, to_string(res).c_str(), bin.getRemoteAddrAndType().toString().c_str(),
775 toString().c_str());
776 }
777
778 res = device->uploadKeys(bin, BTSecurityLevel::NONE);
779 if( HCIStatusCode::SUCCESS != res ) {
780 WARN_PRINT("(dev_id %d): Upload SMPKeyBin failed %s, %s (removing file)",
781 dev_id, to_string(res).c_str(), bin.toString().c_str());
782 if( key_path.size() > 0 ) {
783 bin.remove(key_path);
784 }
785 return res;
786 } else {
787 DBG_PRINT("BTAdapter::setSMPKeyBin(dev_id %d): Upload OK: %s, %s",
788 dev_id, bin.toString().c_str(), toString().c_str());
789 }
790 addSMPKeyBin( std::make_shared<SMPKeyBin>(bin), write); // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS
792}
793
794HCIStatusCode BTAdapter::initialize(const BTMode btMode, const bool powerOn) noexcept {
795 const bool was_powered = adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED);
796 adapter_initialized = true;
797 adapter_poweredoff_at_init = was_powered;
798
799 // Also fails if unable to power-on and not powered-on!
800 HCIStatusCode status = mgmt->initializeAdapter(adapterInfo, dev_id, btMode, powerOn);
801 if( HCIStatusCode::SUCCESS != status ) {
802 WARN_PRINT("Adapter[%d]: Failed initializing (1): res0 %s, powered[before %d, now %d], %s - %s",
803 dev_id, to_string(status).c_str(),
804 was_powered, adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED),
805 adapterInfo.toString().c_str(), toString().c_str());
806 return status;
807 }
808 const bool is_powered = adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED);
809 if( !enableListening(true) ) {
811 }
812 updateAdapterSettings(false /* off_thread */, adapterInfo.getCurrentSettingMask(), false /* sendEvent */, 0);
813
814 WORDY_PRINT("BTAdapter::initialize: Adapter[%d]: OK: powered[%d -> %d], %s",
815 dev_id, was_powered, is_powered, toString().c_str());
817}
818
819bool BTAdapter::lockConnect(const BTDevice & device, const bool wait, const SMPIOCapability io_cap) noexcept {
820 std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
821 const jau::fraction_i64 timeout = 10_s; // FIXME: Configurable?
822
823 if( nullptr != single_conn_device_ptr ) {
824 if( device == *single_conn_device_ptr ) {
825 COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: Already locked, same device: %s", device.toString().c_str());
826 return true; // already set, same device: OK, locked
827 }
828 if( wait ) {
830 while( nullptr != single_conn_device_ptr ) {
831 std::cv_status s = wait_until(cv_single_conn_device, lock, timeout_time);
832 if( std::cv_status::timeout == s && nullptr != single_conn_device_ptr ) {
833 if( debug_lock ) {
834 jau::PLAIN_PRINT(true, "BTAdapter::lockConnect: Failed: Locked (waited)");
835 jau::PLAIN_PRINT(true, " - locked-by-other-device %s", single_conn_device_ptr->toString().c_str());
836 jau::PLAIN_PRINT(true, " - lock-failed-for %s", device.toString().c_str());
837 }
838 return false;
839 }
840 }
841 // lock was released
842 } else {
843 if( debug_lock ) {
844 jau::PLAIN_PRINT(true, "BTAdapter::lockConnect: Failed: Locked (no-wait)");
845 jau::PLAIN_PRINT(true, " - locked-by-other-device %s", single_conn_device_ptr->toString().c_str());
846 jau::PLAIN_PRINT(true, " - lock-failed-for %s", device.toString().c_str());
847 }
848 return false; // already set, not waiting, blocked
849 }
850 }
851 single_conn_device_ptr = &device;
852
853 if( SMPIOCapability::UNSET != io_cap ) {
854 if constexpr ( USE_LINUX_BT_SECURITY ) {
856 const bool res_iocap = mgmt->setIOCapability(dev_id, io_cap, pre_io_cap);
857 if( res_iocap ) {
858 iocap_defaultval = pre_io_cap;
859 COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: New lock, setIOCapability[%s -> %s], %s",
860 to_string(pre_io_cap).c_str(), to_string(io_cap).c_str(),
861 device.toString().c_str());
862 return true;
863 } else {
864 // failed, unlock and exit
865 COND_PRINT(debug_lock, "BTAdapter::lockConnect: Failed: setIOCapability[%s], %s",
866 to_string(io_cap).c_str(), device.toString().c_str());
867 single_conn_device_ptr = nullptr;
868 lock.unlock(); // unlock mutex before notify_all to avoid pessimistic re-block of notified wait() thread.
869 cv_single_conn_device.notify_all(); // notify waiting getter
870 return false;
871 }
872 } else {
873 COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: New lock, ignored io-cap: %s, %s",
874 to_string(io_cap).c_str(), device.toString().c_str());
875 return true;
876 }
877 } else {
878 COND_PRINT(debug_lock, "BTAdapter::lockConnect: Success: New lock, no io-cap: %s", device.toString().c_str());
879 return true;
880 }
881}
882
883bool BTAdapter::unlockConnect(const BTDevice & device) noexcept {
884 std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
885
886 if( nullptr != single_conn_device_ptr && device == *single_conn_device_ptr ) {
887 const SMPIOCapability v = iocap_defaultval;
888 iocap_defaultval = SMPIOCapability::UNSET;
890 // Unreachable: !USE_LINUX_BT_SECURITY
892 const bool res = mgmt->setIOCapability(dev_id, v, o);
893 COND_PRINT(debug_lock, "BTAdapter::unlockConnect: Success: setIOCapability[res %d: %s -> %s], %s",
894 res, to_string(o).c_str(), to_string(v).c_str(),
895 single_conn_device_ptr->toString().c_str());
896 } else {
897 COND_PRINT(debug_lock, "BTAdapter::unlockConnect: Success: %s",
898 single_conn_device_ptr->toString().c_str());
899 }
900 single_conn_device_ptr = nullptr;
901 lock.unlock(); // unlock mutex before notify_all to avoid pessimistic re-block of notified wait() thread.
902 cv_single_conn_device.notify_all(); // notify waiting getter
903 return true;
904 } else {
905 if( debug_lock ) {
906 const std::string other_device_str = nullptr != single_conn_device_ptr ? single_conn_device_ptr->toString() : "null";
907 jau::PLAIN_PRINT(true, "BTAdapter::unlockConnect: Not locked:");
908 jau::PLAIN_PRINT(true, " - locked-by-other-device %s", other_device_str.c_str());
909 jau::PLAIN_PRINT(true, " - unlock-failed-for %s", device.toString().c_str());
910 }
911 return false;
912 }
913}
914
915bool BTAdapter::unlockConnectAny() noexcept {
916 std::unique_lock<std::mutex> lock(mtx_single_conn_device); // RAII-style acquire and relinquish via destructor
917
918 if( nullptr != single_conn_device_ptr ) {
919 const SMPIOCapability v = iocap_defaultval;
920 iocap_defaultval = SMPIOCapability::UNSET;
922 // Unreachable: !USE_LINUX_BT_SECURITY
924 const bool res = mgmt->setIOCapability(dev_id, v, o);
925 COND_PRINT(debug_lock, "BTAdapter::unlockConnectAny: Success: setIOCapability[res %d: %s -> %s]; %s",
926 res, to_string(o).c_str(), to_string(v).c_str(),
927 single_conn_device_ptr->toString().c_str());
928 } else {
929 COND_PRINT(debug_lock, "BTAdapter::unlockConnectAny: Success: %s",
930 single_conn_device_ptr->toString().c_str());
931 }
932 single_conn_device_ptr = nullptr;
933 lock.unlock(); // unlock mutex before notify_all to avoid pessimistic re-block of notified wait() thread.
934 cv_single_conn_device.notify_all(); // notify waiting getter
935 return true;
936 } else {
937 iocap_defaultval = SMPIOCapability::UNSET;
938 COND_PRINT(debug_lock, "BTAdapter::unlockConnectAny: Not locked");
939 return false;
940 }
941}
942
944 if( !isValid() ) {
945 ERR_PRINT("Adapter invalid: %s, %s", jau::to_hexstring(this).c_str(), toString().c_str());
947 }
948 if( !hci.isOpen() ) {
949 ERR_PRINT("HCI closed: %s, %s", jau::to_hexstring(this).c_str(), toString().c_str());
951 }
952 DBG_PRINT("BTAdapter::reset.0: %s", toString().c_str());
953 HCIStatusCode res = hci.resetAdapter( [&]() noexcept -> HCIStatusCode {
954 jau::nsize_t connCount = getConnectedDeviceCount();
955 if( 0 < connCount ) {
957 const jau::fraction_i64 poll_period = hci.env.HCI_COMMAND_POLL_PERIOD;
958 DBG_PRINT("BTAdapter::reset: %d connections pending - %s", connCount, toString().c_str());
959 jau::fraction_i64 td = 0_s;
960 while( timeout > td && 0 < connCount ) {
961 sleep_for( poll_period );
962 td += poll_period;
963 connCount = getConnectedDeviceCount();
964 }
965 if( 0 < connCount ) {
966 WARN_PRINT("%d connections pending after %" PRIi64 " ms - %s", connCount, td.to_ms(), toString().c_str());
967 } else {
968 DBG_PRINT("BTAdapter::reset: pending connections resolved after %" PRIi64 " ms - %s", td.to_ms(), toString().c_str());
969 }
970 }
971 return HCIStatusCode::SUCCESS; // keep going
972 });
973 DBG_PRINT("BTAdapter::reset.X: %s - %s", to_string(res).c_str(), toString().c_str());
974 return res;
975}
976
977
979 if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED
980 poweredOff(false /* active */, "setDefaultLE_PHY.np");
982 }
983 return hci.le_set_default_phy(Tx, Rx);
984}
985
986bool BTAdapter::isDeviceWhitelisted(const BDAddressAndType & addressAndType) noexcept {
987 return mgmt->isDeviceWhitelisted(dev_id, addressAndType);
988}
989
991 const uint16_t conn_interval_min, const uint16_t conn_interval_max,
992 const uint16_t conn_latency, const uint16_t timeout) {
993 if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED
994 poweredOff(false /* active */, "addDeviceToWhitelist.np");
995 return false;
996 }
997 if( mgmt->isDeviceWhitelisted(dev_id, addressAndType) ) {
998 ERR_PRINT("device already listed: dev_id %d, address%s", dev_id, addressAndType.toString().c_str());
999 return true;
1000 }
1001
1002 HCIStatusCode res = mgmt->uploadConnParam(dev_id, addressAndType, conn_interval_min, conn_interval_max, conn_latency, timeout);
1003 if( HCIStatusCode::SUCCESS != res ) {
1004 ERR_PRINT("uploadConnParam(dev_id %d, address%s, interval[%u..%u], latency %u, timeout %u): Failed %s",
1005 dev_id, addressAndType.toString().c_str(), conn_interval_min, conn_interval_max, conn_latency, timeout, to_string(res).c_str());
1006 }
1007 return mgmt->addDeviceToWhitelist(dev_id, addressAndType, ctype);
1008}
1009
1011 return mgmt->removeDeviceFromWhitelist(dev_id, addressAndType);
1012}
1013
1014BTAdapter::statusListenerList_t::equal_comparator BTAdapter::adapterStatusListenerRefEqComparator =
1015 [](const StatusListenerPair &a, const StatusListenerPair &b) -> bool { return *a.listener == *b.listener; };
1016
1018 if( nullptr == l ) {
1019 ERR_PRINT("AdapterStatusListener ref is null");
1020 return false;
1021 }
1022 const bool added = statusListenerList.push_back_unique(StatusListenerPair{l, std::weak_ptr<BTDevice>{} },
1023 adapterStatusListenerRefEqComparator);
1024 if( added ) {
1025 sendAdapterSettingsInitial(*l, jau::getCurrentMilliseconds());
1026 }
1027 if( _print_device_lists || jau::environment::get().verbose ) {
1028 jau::PLAIN_PRINT(true, "BTAdapter::addStatusListener.1: added %d, %s", added, toString().c_str());
1029 printDeviceLists();
1030 }
1031 return added;
1032}
1033
1035 if( nullptr == l ) {
1036 ERR_PRINT("AdapterStatusListener ref is null");
1037 return false;
1038 }
1039 if( nullptr == d ) {
1040 ERR_PRINT("Device ref is null");
1041 return false;
1042 }
1043 const bool added = statusListenerList.push_back_unique(StatusListenerPair{l, d},
1044 adapterStatusListenerRefEqComparator);
1045 if( added ) {
1046 sendAdapterSettingsInitial(*l, jau::getCurrentMilliseconds());
1047 }
1048 if( _print_device_lists || jau::environment::get().verbose ) {
1049 jau::PLAIN_PRINT(true, "BTAdapter::addStatusListener.2: added %d, %s", added, toString().c_str());
1050 printDeviceLists();
1051 }
1052 return added;
1053}
1054
1056 return addStatusListener(getSharedDevice(d), l);
1057}
1058
1060 if( nullptr == l ) {
1061 ERR_PRINT("AdapterStatusListener ref is null");
1062 return false;
1063 }
1064 const size_type count = statusListenerList.erase_matching(StatusListenerPair{l, std::weak_ptr<BTDevice>{}},
1065 false /* all_matching */,
1066 adapterStatusListenerRefEqComparator);
1067 if( _print_device_lists || jau::environment::get().verbose ) {
1068 jau::PLAIN_PRINT(true, "BTAdapter::removeStatusListener.1: res %d, %s", count>0, toString().c_str());
1069 printDeviceLists();
1070 }
1071 return count > 0;
1072}
1073
1075 if( nullptr == l ) {
1076 ERR_PRINT("AdapterStatusListener ref is null");
1077 return false;
1078 }
1079 bool res = false;
1080 {
1081 auto it = statusListenerList.begin(); // lock mutex and copy_store
1082 for (; !it.is_end(); ++it ) {
1083 if ( *it->listener == *l ) {
1084 it.erase();
1085 it.write_back();
1086 res = true;
1087 break;
1088 }
1089 }
1090 }
1091 if( _print_device_lists || jau::environment::get().verbose ) {
1092 jau::PLAIN_PRINT(true, "BTAdapter::removeStatusListener.2: res %d, %s", res, toString().c_str());
1093 printDeviceLists();
1094 }
1095 return res;
1096}
1097
1099 size_type count = 0;
1100
1101 if( 0 < statusListenerList.size() ) {
1102 auto begin = statusListenerList.begin();
1103 auto it = begin.end();
1104 do {
1105 --it;
1106 BTDeviceRef sda = it->wbr_device.lock();
1107 if ( nullptr != sda && *sda == d ) {
1108 it.erase();
1109 ++count;
1110 }
1111 } while( it != begin );
1112
1113 if( 0 < count ) {
1114 begin.write_back();
1115 }
1116 }
1117 return count;
1118}
1119
1121 const size_type count = statusListenerList.size();
1122 statusListenerList.clear();
1123 return count;
1124}
1125
1126void BTAdapter::checkDiscoveryState() noexcept {
1127 const ScanType currentNativeScanType = hci.getCurrentScanType();
1128 // Check LE scan state
1129 if( DiscoveryPolicy::AUTO_OFF == discovery_policy ) {
1130 if( is_set(currentMetaScanType, ScanType::LE) != is_set(currentNativeScanType, ScanType::LE) ) {
1131 std::string msg("Invalid DiscoveryState: policy "+to_string(discovery_policy)+
1132 ", currentScanType*[native "+
1133 to_string(currentNativeScanType)+" != meta "+
1134 to_string(currentMetaScanType)+"], "+toString());
1135 ERR_PRINT(msg.c_str());
1136 // ABORT?
1137 }
1138 } else {
1139 if( !is_set(currentMetaScanType, ScanType::LE) && is_set(currentNativeScanType, ScanType::LE) ) {
1140 std::string msg("Invalid DiscoveryState: policy "+to_string(discovery_policy)+
1141 ", currentScanType*[native "+
1142 to_string(currentNativeScanType)+", meta "+
1143 to_string(currentMetaScanType)+"], "+toString());
1144 ERR_PRINT(msg.c_str());
1145 // ABORT?
1146 }
1147 }
1148}
1149
1151 const DiscoveryPolicy policy, const bool le_scan_active,
1152 const uint16_t le_scan_interval, const uint16_t le_scan_window,
1153 const uint8_t filter_policy,
1154 const bool filter_dup) noexcept
1155{
1156 // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to setup BREDR, LE or DUAL scanning!
1157 // ERR_PRINT("Test");
1158 // throw jau::RuntimeException("Test", E_FILE_LINE);
1159
1160 clearDevicesPausingDiscovery();
1161
1162 if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED
1163 poweredOff(false /* active */, "startDiscovery.np");
1165 }
1166
1167 const std::lock_guard<std::mutex> lock(mtx_discovery); // RAII-style acquire and relinquish via destructor
1168
1169 if( isAdvertising() ) {
1170 WARN_PRINT("Adapter in advertising mode: %s", toString(true).c_str());
1172 }
1173
1174 l2cap_service.stop();
1175
1176 removeDiscoveredDevices();
1177
1178 scan_filter_dup = filter_dup; // cache for background scan
1179
1180 const ScanType currentNativeScanType = hci.getCurrentScanType();
1181
1182 if( is_set(currentNativeScanType, ScanType::LE) ) {
1183 btRole = BTRole::Master;
1184 if( discovery_policy == policy ) {
1185 DBG_PRINT("BTAdapter::startDiscovery: Already discovering, unchanged policy %s -> %s, currentScanType[native %s, meta %s] ...\n- %s",
1186 to_string(discovery_policy).c_str(), to_string(policy).c_str(),
1187 to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(), toString(true).c_str());
1188 } else {
1189 DBG_PRINT("BTAdapter::startDiscovery: Already discovering, changed policy %s -> %s, currentScanType[native %s, meta %s] ...\n- %s",
1190 to_string(discovery_policy).c_str(), to_string(policy).c_str(),
1191 to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(), toString(true).c_str());
1192 discovery_policy = policy;
1193 }
1194 gattServerData = gattServerData_;
1195 if( _print_device_lists || jau::environment::get().verbose ) {
1196 jau::PLAIN_PRINT(true, "BTAdapter::startDiscovery: End.0: Result %s, policy %s -> %s, currentScanType[native %s, meta %s] ...\n- %s",
1198 to_string(discovery_policy).c_str(), to_string(policy).c_str(),
1199 to_string(hci.getCurrentScanType()).c_str(), to_string(currentMetaScanType).c_str(), toString().c_str());
1200 printDeviceLists();
1201 }
1202 checkDiscoveryState();
1204 }
1205
1206 if( _print_device_lists || jau::environment::get().verbose ) {
1207 jau::PLAIN_PRINT(true, "BTAdapter::startDiscovery: Start: policy %s -> %s, currentScanType[native %s, meta %s] ...\n- %s",
1208 to_string(discovery_policy).c_str(), to_string(policy).c_str(),
1209 to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(), toString().c_str());
1210 }
1211
1212 discovery_policy = policy;
1213
1214 if( nullptr != gattServerData_ ) {
1215 gattServerData_->setServicesHandles();
1216 }
1217
1218 // if le_enable_scan(..) is successful, it will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType.
1219 const HCIStatusCode status = hci.le_start_scan(filter_dup, le_scan_active, visibleMACType,
1221
1222 if( HCIStatusCode::SUCCESS != status ) {
1223 gattServerData = nullptr;
1224 } else {
1225 gattServerData = gattServerData_;
1226 }
1227
1228 if( _print_device_lists || jau::environment::get().verbose ) {
1229 jau::PLAIN_PRINT(true, "BTAdapter::startDiscovery: End.1: Result %s, policy %s -> %s, currentScanType[native %s, meta %s] ...\n- %s",
1230 to_string(status).c_str(),
1231 to_string(discovery_policy).c_str(), to_string(policy).c_str(),
1232 to_string(hci.getCurrentScanType()).c_str(), to_string(currentMetaScanType).c_str(), toString().c_str());
1233 printDeviceLists();
1234 }
1235
1236 checkDiscoveryState();
1237
1238 return status;
1239}
1240
1241void BTAdapter::discoveryServerWork(jau::service_runner& sr) noexcept {
1242 static jau::nsize_t trial_count = 0;
1243 bool retry = false;
1244
1245 // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to setup BREDR, LE or DUAL scanning!
1246 if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED
1247 poweredOff(false /* active */, "discoveryServerWork.np");
1248 } else {
1249 {
1250 const std::lock_guard<std::mutex> lock(mtx_discovery); // RAII-style acquire and relinquish via destructor
1251 const ScanType currentNativeScanType = hci.getCurrentScanType();
1252
1253 if( !is_set(currentNativeScanType, ScanType::LE) &&
1254 DiscoveryPolicy::AUTO_OFF != discovery_policy &&
1255 0 == getDevicesPausingDiscoveryCount() ) // still required to start discovery ???
1256 {
1257 // if le_enable_scan(..) is successful, it will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType.
1258 DBG_PRINT("BTAdapter::startDiscoveryBackground[%u/%u]: Policy %s, currentScanType[native %s, meta %s] ... %s",
1259 trial_count+1, MAX_BACKGROUND_DISCOVERY_RETRY,
1260 to_string(discovery_policy).c_str(),
1261 to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(), toString().c_str());
1262 const HCIStatusCode status = hci.le_enable_scan(true /* enable */, scan_filter_dup);
1263 if( HCIStatusCode::SUCCESS != status ) {
1264 ERR_PRINT2("le_enable_scan failed[%u/%u]: %s - %s",
1265 trial_count+1, MAX_BACKGROUND_DISCOVERY_RETRY,
1266 to_string(status).c_str(), toString().c_str());
1267 if( trial_count < MAX_BACKGROUND_DISCOVERY_RETRY ) {
1268 trial_count++;
1269 retry = true;
1270 }
1271 }
1272 checkDiscoveryState();
1273 }
1274 }
1275 if( retry && !sr.shall_stop() ) {
1276 std::this_thread::sleep_for(std::chrono::milliseconds(100)); // wait a little (FIXME)
1277 }
1278 }
1279 if( !retry ) {
1280 trial_count=0;
1281 sr.set_shall_stop();
1282 }
1283}
1284
1286 clearDevicesPausingDiscovery();
1287
1288 return stopDiscoveryImpl(false /* forceDiscoveringEvent */, false /* temporary */);
1289}
1290
1291HCIStatusCode BTAdapter::stopDiscoveryImpl(const bool forceDiscoveringEvent, const bool temporary) noexcept {
1292 // We allow !isEnabled, to utilize method for adjusting discovery state and notifying listeners
1293 // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to stop BREDR, LE or DUAL scanning!
1294
1295 if( !isValid() ) {
1296 ERR_PRINT("Adapter invalid: %s, %s", jau::to_hexstring(this).c_str(), toString().c_str());
1298 }
1299 const std::lock_guard<std::mutex> lock(mtx_discovery); // RAII-style acquire and relinquish via destructor
1300 /**
1301 * Need to send mgmtEvDeviceDiscoveringMgmt(..)
1302 * as manager/hci won't produce such event having temporarily disabled discovery.
1303 * + --+-------+--------+-----------+----------------------------------------------------+
1304 * | # | meta | native | keepAlive | Note
1305 * +---+-------+--------+-----------+----------------------------------------------------+
1306 * | 1 | true | true | false | -
1307 * | 2 | false | false | false | -
1308 * +---+-------+--------+-----------+----------------------------------------------------+
1309 * | 3 | true | true | true | -
1310 * | 4 | true | false | true | temporarily disabled -> startDiscoveryBackground()
1311 * | 5 | false | false | true | [4] -> [5] requires manual DISCOVERING event
1312 * +---+-------+--------+-----------+----------------------------------------------------+
1313 * [4] current -> [5] post stopDiscovery == sendEvent
1314 */
1315 const ScanType currentNativeScanType = hci.getCurrentScanType();
1316 const bool le_scan_temp_disabled = is_set(currentMetaScanType, ScanType::LE) && // true
1317 !is_set(currentNativeScanType, ScanType::LE) && // false
1318 DiscoveryPolicy::AUTO_OFF != discovery_policy; // true
1319
1320 DBG_PRINT("BTAdapter::stopDiscovery: Start: policy %s, currentScanType[native %s, meta %s], le_scan_temp_disabled %d, forceDiscEvent %d ...",
1321 to_string(discovery_policy).c_str(),
1322 to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str(),
1323 le_scan_temp_disabled, forceDiscoveringEvent);
1324
1325 if( !temporary ) {
1326 discovery_policy = DiscoveryPolicy::AUTO_OFF;
1327 }
1328
1329 if( !is_set(currentMetaScanType, ScanType::LE) ) {
1330 DBG_PRINT("BTAdapter::stopDiscovery: Already disabled, policy %s, currentScanType[native %s, meta %s] ...",
1331 to_string(discovery_policy).c_str(),
1332 to_string(currentNativeScanType).c_str(), to_string(currentMetaScanType).c_str());
1333 checkDiscoveryState();
1335 }
1336
1337 HCIStatusCode status;
1338 if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED
1339 poweredOff(false /* active */, "stopDiscoveryImpl.np");
1341 goto exit;
1342 }
1343
1344 if( le_scan_temp_disabled ) {
1345 // meta state transition [4] -> [5], w/o native disabling
1346 // Will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType
1347 // currentMetaScanType = changeScanType(currentMetaScanType, false, ScanType::LE);
1348 status = HCIStatusCode::SUCCESS; // send event: discoveryTempDisabled
1349 } else {
1350 // if le_enable_scan(..) is successful, it will issue 'mgmtEvDeviceDiscoveringHCI(..)' immediately, which updates currentMetaScanType.
1351 status = hci.le_enable_scan(false /* enable */);
1352 if( HCIStatusCode::SUCCESS != status ) {
1353 ERR_PRINT("le_enable_scan failed: %s", to_string(status).c_str());
1354 }
1355 }
1356
1357exit:
1358 if( HCIStatusCode::SUCCESS != status ) {
1359 // Sync nativeDiscoveryState with currentMetaScanType,
1360 // the latter git set to NONE via mgmtEvDeviceDiscoveringHCI(..) below.
1361 // Resolves checkDiscoveryState error message @ HCI command failure.
1362 hci.setCurrentScanType( ScanType::NONE );
1363 }
1364 if( le_scan_temp_disabled || forceDiscoveringEvent || HCIStatusCode::SUCCESS != status ) {
1365 // In case of discoveryTempDisabled, power-off, le_enable_scan failure
1366 // or already closed HCIHandler, send the event directly.
1367 const MgmtEvtDiscovering e(dev_id, ScanType::LE, false);
1368 mgmtEvDeviceDiscoveringHCI( e );
1369 }
1370 if( _print_device_lists || jau::environment::get().verbose ) {
1371 jau::PLAIN_PRINT(true, "BTAdapter::stopDiscovery: End: Result %s, policy %s, currentScanType[native %s, meta %s], le_scan_temp_disabled %d ...\n- %s",
1372 to_string(status).c_str(), to_string(discovery_policy).c_str(),
1373 to_string(hci.getCurrentScanType()).c_str(), to_string(currentMetaScanType).c_str(), le_scan_temp_disabled,
1374 toString().c_str());
1375 printDeviceLists();
1376 }
1377
1378 checkDiscoveryState();
1379
1380 return status;
1381}
1382
1383// *************************************************
1384
1385BTDeviceRef BTAdapter::findDiscoveredDevice (const EUI48 & address, const BDAddressType addressType) noexcept {
1386 const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
1387 return findDevice(hci, discoveredDevices, address, addressType);
1388}
1389
1390bool BTAdapter::addDiscoveredDevice(BTDeviceRef const &device) noexcept {
1391 const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
1392 if( nullptr != findDevice(discoveredDevices, *device) ) {
1393 // already discovered
1394 return false;
1395 }
1396 discoveredDevices.push_back(device);
1397 return true;
1398}
1399
1400bool BTAdapter::removeDiscoveredDevice(const BDAddressAndType & addressAndType) noexcept {
1401 const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
1402 for (auto it = discoveredDevices.begin(); it != discoveredDevices.end(); ++it) {
1403 const BTDevice& device = **it;
1404 if ( nullptr != *it && addressAndType == device.addressAndType ) {
1405 if( nullptr == getSharedDevice( device ) ) {
1406 removeAllStatusListener( device );
1407 }
1408 discoveredDevices.erase(it);
1409 return true;
1410 }
1411 }
1412 return false;
1413}
1414
1416 size_type res;
1417 {
1418 const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor
1419
1420 res = discoveredDevices.size();
1421 if( 0 < res ) {
1422 auto it = discoveredDevices.end();
1423 do {
1424 --it;
1425 const BTDevice& device = **it;
1426 if( nullptr == getSharedDevice( device ) ) {
1427 removeAllStatusListener( device );
1428 }
1429 discoveredDevices.erase(it);
1430 } while( it != discoveredDevices.begin() );
1431 }
1432 }
1433 if( _print_device_lists || jau::environment::get().verbose ) {
1434 jau::PLAIN_PRINT(true, "BTAdapter::removeDiscoveredDevices: End: %zu, %s", (size_t)res, toString().c_str());
1436 }
1437 return res;
1438}
1439
1441 jau::sc_atomic_critical sync(sync_data); // lock-free simple cache-load 'snapshot'
1442 device_list_t res = discoveredDevices;
1443 return res;
1444}
1445
1446// *************************************************
1447
1448bool BTAdapter::addSharedDevice(BTDeviceRef const &device) noexcept {
1449 const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
1450 if( nullptr != findDevice(sharedDevices, *device) ) {
1451 // already shared
1452 return false;
1453 }
1454 sharedDevices.push_back(device);
1455 return true;
1456}
1457
1458BTDeviceRef BTAdapter::getSharedDevice(const BTDevice & device) noexcept {
1459 const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
1460 return findDevice(sharedDevices, device);
1461}
1462
1463void BTAdapter::removeSharedDevice(const BTDevice & device) noexcept {
1464 const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
1465 for (auto it = sharedDevices.begin(); it != sharedDevices.end(); ) {
1466 if ( nullptr != *it && device == **it ) {
1467 sharedDevices.erase(it);
1468 return; // unique set
1469 } else {
1470 ++it;
1471 }
1472 }
1473}
1474
1475BTDeviceRef BTAdapter::findSharedDevice (const EUI48 & address, const BDAddressType addressType) noexcept {
1476 const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor
1477 return findDevice(hci, sharedDevices, address, addressType);
1478}
1479
1480// *************************************************
1481
1482void BTAdapter::removeDevice(BTDevice & device) noexcept {
1483 WORDY_PRINT("DBTAdapter::removeDevice: Start %s", toString().c_str());
1484 removeAllStatusListener(device);
1485
1486 const HCIStatusCode status = device.disconnect(HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION);
1487 WORDY_PRINT("BTAdapter::removeDevice: disconnect %s, %s", to_string(status).c_str(), toString().c_str());
1488 unlockConnect(device);
1489 removeConnectedDevice(device); // usually done in BTAdapter::mgmtEvDeviceDisconnectedHCI
1490 removeDiscoveredDevice(device.addressAndType); // usually done in BTAdapter::mgmtEvDeviceDisconnectedHCI
1491 removeDevicePausingDiscovery(device);
1492 if( device.getAvailableSMPKeys(false /* responder */) == SMPKeyType::NONE ) {
1493 // Only remove from shared device list if not an initiator (LL master) having paired keys!
1494 removeSharedDevice(device);
1495 }
1496
1497 if( _print_device_lists || jau::environment::get().verbose ) {
1498 jau::PLAIN_PRINT(true, "BTAdapter::removeDevice: End %s, %s", device.getAddressAndType().toString().c_str(), toString().c_str());
1499 printDeviceLists();
1500 }
1501}
1502
1503// *************************************************
1504
1505BTAdapter::SMPKeyBinRef BTAdapter::findSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress) noexcept {
1506 const jau::nsize_t size = keys.size();
1507 for (jau::nsize_t i = 0; i < size; ++i) {
1508 SMPKeyBinRef& k = keys[i];
1509 if ( nullptr != k && remoteAddress == k->getRemoteAddrAndType() ) {
1510 return k;
1511 }
1512 }
1513 return nullptr;
1514}
1515bool BTAdapter::removeSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress, const bool remove_file, const std::string& key_path_) noexcept {
1516 for (auto it = keys.begin(); it != keys.end(); ++it) {
1517 const SMPKeyBinRef& k = *it;
1518 if ( nullptr != k && remoteAddress == k->getRemoteAddrAndType() ) {
1519 DBG_PRINT("BTAdapter::removeSMPKeyBin(file %d): %s", remove_file, k->toString().c_str());
1520 if( remove_file && key_path_.size() > 0 ) {
1521 if( !k->remove(key_path_) ) {
1522 WARN_PRINT("Failed removal of SMPKeyBin file: %s", k->getFilename(key_path_).c_str());
1523 }
1524 }
1525 keys.erase(it);
1526 return true;
1527 }
1528 }
1529 return false;
1530}
1531BTAdapter::SMPKeyBinRef BTAdapter::findSMPKeyBin(BDAddressAndType const & remoteAddress) noexcept {
1532 const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor
1533 return findSMPKeyBin(key_list, remoteAddress);
1534}
1535bool BTAdapter::addSMPKeyBin(const SMPKeyBinRef& key, const bool write_file) noexcept {
1536 const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor
1537 removeSMPKeyBin(key_list, key->getRemoteAddrAndType(), write_file /* remove_file */, key_path);
1538 if( jau::environment::get().debug ) {
1539 key->setVerbose(true);
1540 DBG_PRINT("BTAdapter::addSMPKeyBin(file %d): %s", write_file, key->toString().c_str());
1541 }
1542 key_list.push_back( key );
1543 if( write_file && key_path.size() > 0 ) {
1544 if( !key->write(key_path, true /* overwrite */) ) {
1545 WARN_PRINT("Failed write of SMPKeyBin file: %s", key->getFilename(key_path).c_str());
1546 }
1547 }
1548 return true;
1549}
1550bool BTAdapter::removeSMPKeyBin(BDAddressAndType const & remoteAddress, const bool remove_file) noexcept {
1551 const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor
1552 return removeSMPKeyBin(key_list, remoteAddress, remove_file, key_path);
1553}
1554
1555// *************************************************
1556
1558 EInfoReport& eir, EIRDataType adv_mask, EIRDataType scanrsp_mask,
1559 const uint16_t adv_interval_min, const uint16_t adv_interval_max,
1560 const AD_PDU_Type adv_type,
1561 const uint8_t adv_chan_map,
1562 const uint8_t filter_policy) noexcept {
1563 if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED
1564 poweredOff(false /* active */, "startAdvertising.np");
1566 }
1567
1568 if( isDiscovering() ) {
1569 WARN_PRINT("Not allowed (scan enabled): %s", toString(true).c_str());
1571 }
1572 const jau::nsize_t connCount = getConnectedDeviceCount();
1573 if( 0 < connCount ) { // FIXME: May shall not be a restriction
1574 WARN_PRINT("Not allowed (%d connections open/pending): %s", connCount, toString(true).c_str());
1575 printDeviceLists();
1577 }
1578 if( jau::environment::get().debug ) {
1579 std::vector<MgmtDefaultParam> params = mgmt->readDefaultSysParam(dev_id);
1580 DBG_PRINT("BTAdapter::startAdvertising[%d]: SysParam: %zd", dev_id, params.size());
1581 for(size_t i=0; i<params.size(); ++i) {
1582 jau::PLAIN_PRINT(true, "[%2.2zd]: %s", i, params[i].toString().c_str());
1583 }
1584 }
1585 if constexpr ( USE_LINUX_BT_SECURITY ) {
1587 SMPIOCapability new_io_cap = SMPIOCapability::UNSET != io_cap_server ? io_cap_server : SMPIOCapability::NO_INPUT_NO_OUTPUT;
1588 const bool res_iocap = mgmt->setIOCapability(dev_id, new_io_cap, pre_io_cap);
1589 DBG_PRINT("BTAdapter::startAdvertising: dev_id %u, setIOCapability[%s -> %s]: result %d",
1590 dev_id, to_string(pre_io_cap).c_str(), to_string(new_io_cap).c_str(), res_iocap);
1591 }
1592 DBG_PRINT("BTAdapter::startAdvertising.1: dev_id %u, %s", dev_id, toString().c_str());
1593 l2cap_service.stop();
1594 l2cap_service.start();
1595
1596 if( !l2cap_att_srv.is_open() ) {
1597 ERR_PRINT("l2cap_service failed: %s", toString(true).c_str());
1598 l2cap_service.stop();
1600 }
1601
1602 // set minimum ...
1603 eir.addFlags(GAPFlags::LE_Gen_Disc);
1604 eir.setName(getName());
1605 if( EIRDataType::NONE == ( adv_mask & EIRDataType::FLAGS ) ||
1606 EIRDataType::NONE == ( scanrsp_mask & EIRDataType::FLAGS ) ) {
1607 adv_mask = adv_mask | EIRDataType::FLAGS;
1608 }
1609 if( EIRDataType::NONE == ( adv_mask & EIRDataType::NAME ) ||
1610 EIRDataType::NONE == ( scanrsp_mask & EIRDataType::NAME ) ) {
1611 scanrsp_mask = scanrsp_mask | EIRDataType::NAME;
1612 }
1613
1614 if( nullptr != gattServerData_ ) {
1615 gattServerData_->setServicesHandles();
1616 }
1617
1618 const EUI48 peer_bdaddr=EUI48::ANY_DEVICE;
1619 const HCILEOwnAddressType own_mac_type=visibleMACType;
1621
1622 HCIStatusCode status = hci.le_start_adv(eir, adv_mask, scanrsp_mask,
1623 peer_bdaddr, own_mac_type, peer_mac_type,
1625 if( HCIStatusCode::SUCCESS != status ) {
1626 ERR_PRINT("le_start_adv failed: %s - %s", to_string(status).c_str(), toString(true).c_str());
1627 gattServerData = nullptr;
1628 l2cap_service.stop();
1629 } else {
1630 gattServerData = gattServerData_;
1631 btRole = BTRole::Slave;
1632 DBG_PRINT("BTAdapter::startAdvertising.OK: dev_id %u, %s", dev_id, toString().c_str());
1633 }
1634 return status;
1635}
1636
1638 const uint16_t adv_interval_min, const uint16_t adv_interval_max,
1639 const AD_PDU_Type adv_type,
1640 const uint8_t adv_chan_map,
1641 const uint8_t filter_policy) noexcept {
1642 EInfoReport eir;
1645
1647 eir.setName(getName());
1648 eir.setConnInterval(10, 24); // default
1649 if( nullptr != gattServerData_ ) {
1650 for(const DBGattServiceRef& s : gattServerData_->getServices()) {
1651 eir.addService(s->getType());
1652 }
1653 }
1654
1655 return startAdvertising(gattServerData_,
1656 eir, adv_mask, scanrsp_mask,
1658 adv_type,
1661}
1662
1663/**
1664 * Closes the advertising session.
1665 * <p>
1666 * This adapter's HCIHandler instance is used to stop advertising,
1667 * see HCIHandler::le_enable_adv().
1668 * </p>
1669 * @return HCIStatusCode::SUCCESS if successful, otherwise the HCIStatusCode error state
1670 */
1672 if( !isPowered() ) { // isValid() && hci.isOpen() && POWERED
1673 poweredOff(false /* active */, "stopAdvertising.np");
1675 }
1676
1677 l2cap_service.stop();
1678
1679 HCIStatusCode status = hci.le_enable_adv(false /* enable */);
1680 if( HCIStatusCode::SUCCESS != status ) {
1681 ERR_PRINT("le_enable_adv failed: %s", to_string(status).c_str());
1682 }
1683 return status;
1684}
1685
1686// *************************************************
1687
1688std::string BTAdapter::toString(bool includeDiscoveredDevices) const noexcept {
1689 std::string random_address_info = adapterInfo.addressAndType != visibleAddressAndType ? " ("+visibleAddressAndType.toString()+")" : "";
1690 std::string out("Adapter["+std::to_string(dev_id)+", BT "+std::to_string(getBTMajorVersion())+", BTMode "+to_string(getBTMode())+", "+to_string(getRole())+", "+adapterInfo.addressAndType.toString()+random_address_info+
1691 ", '"+getName()+"', curSettings"+to_string(adapterInfo.getCurrentSettingMask())+
1692 ", valid "+std::to_string(isValid())+
1693 ", adv "+std::to_string(hci.isAdvertising())+
1694 ", scanType[native "+to_string(hci.getCurrentScanType())+", meta "+to_string(currentMetaScanType)+"]"
1695 ", open[mgmt, "+std::to_string(mgmt->isOpen())+", hci "+std::to_string(hci.isOpen())+
1696 "], "+l2cap_att_srv.toString()+", "+javaObjectToString()+"]");
1697 if( includeDiscoveredDevices ) {
1698 device_list_t devices = getDiscoveredDevices();
1699 if( devices.size() > 0 ) {
1700 out.append("\n");
1701 for(const auto& p : devices) {
1702 if( nullptr != p ) {
1703 out.append(" ").append(p->toString()).append("\n");
1704 }
1705 }
1706 }
1707 }
1708 return out;
1709}
1710
1711// *************************************************
1712
1713void BTAdapter::sendAdapterSettingsChanged(const AdapterSetting old_settings_, const AdapterSetting current_settings, AdapterSetting changes,
1714 const uint64_t timestampMS) noexcept
1715{
1716 int i=0;
1717 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
1718 try {
1719 p.listener->adapterSettingsChanged(*this, old_settings_, current_settings, changes, timestampMS);
1720 } catch (std::exception &e) {
1721 ERR_PRINT("BTAdapter:CB:NewSettings-CBs %d/%zd: %s of %s: Caught exception %s",
1722 i+1, statusListenerList.size(),
1723 p.listener->toString().c_str(), toString().c_str(), e.what());
1724 }
1725 i++;
1726 });
1727}
1728
1729void BTAdapter::sendAdapterSettingsInitial(AdapterStatusListener & asl, const uint64_t timestampMS) noexcept
1730{
1731 const AdapterSetting current_settings = adapterInfo.getCurrentSettingMask();
1732 COND_PRINT(debug_event, "BTAdapter::sendAdapterSettingsInitial: NONE -> %s, changes NONE: %s",
1733 to_string(current_settings).c_str(), toString().c_str() );
1734 try {
1735 asl.adapterSettingsChanged(*this, AdapterSetting::NONE, current_settings, AdapterSetting::NONE, timestampMS);
1736 } catch (std::exception &e) {
1737 ERR_PRINT("BTAdapter::sendAdapterSettingsChanged-CB: %s of %s: Caught exception %s",
1738 asl.toString().c_str(), toString().c_str(), e.what());
1739 }
1740}
1741
1742void BTAdapter::sendDeviceUpdated(std::string cause, BTDeviceRef device, uint64_t timestamp, EIRDataType updateMask) noexcept {
1743 int i=0;
1744 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
1745 try {
1746 if( p.match(device) ) {
1747 p.listener->deviceUpdated(device, updateMask, timestamp);
1748 }
1749 } catch (std::exception &e) {
1750 ERR_PRINT("BTAdapter::sendDeviceUpdated-CBs (%s) %d/%zd: %s of %s: Caught exception %s",
1751 cause.c_str(), i+1, statusListenerList.size(),
1752 p.listener->toString().c_str(), device->toString().c_str(), e.what());
1753 }
1754 i++;
1755 });
1756}
1757
1758// *************************************************
1759
1760void BTAdapter::mgmtEvHCIAnyHCI(const MgmtEvent& e) noexcept {
1761 DBG_PRINT("BTAdapter:hci::Any: %s", e.toString().c_str());
1762 (void)e;
1763}
1764void BTAdapter::mgmtEvMgmtAnyMgmt(const MgmtEvent& e) noexcept {
1765 DBG_PRINT("BTAdapter:mgmt:Any: %s", e.toString().c_str());
1766 (void)e;
1767}
1768
1769void BTAdapter::mgmtEvDeviceDiscoveringHCI(const MgmtEvent& e) noexcept {
1770 const MgmtEvtDiscovering &event = *static_cast<const MgmtEvtDiscovering *>(&e);
1771 mgmtEvDeviceDiscoveringAny(event.getScanType(), event.getEnabled(), event.getTimestamp(), true /* hciSourced */);
1772}
1773
1774void BTAdapter::mgmtEvDeviceDiscoveringMgmt(const MgmtEvent& e) noexcept {
1775 const MgmtEvtDiscovering &event = *static_cast<const MgmtEvtDiscovering *>(&e);
1776 mgmtEvDeviceDiscoveringAny(event.getScanType(), event.getEnabled(), event.getTimestamp(), false /* hciSourced */);
1777}
1778
1779void BTAdapter::updateDeviceDiscoveringState(const ScanType eventScanType, const bool eventEnabled) noexcept {
1780 mgmtEvDeviceDiscoveringAny(eventScanType, eventEnabled, jau::getCurrentMilliseconds(), false /* hciSourced */);
1781}
1782
1783void BTAdapter::mgmtEvDeviceDiscoveringAny(const ScanType eventScanType, const bool eventEnabled, const uint64_t eventTimestamp,
1784 const bool hciSourced) noexcept {
1785 const std::string srctkn = hciSourced ? "hci" : "mgmt";
1786 ScanType currentNativeScanType = hci.getCurrentScanType();
1787
1788 // FIXME: Respect BTAdapter::btMode, i.e. BTMode::BREDR, BTMode::LE or BTMode::DUAL to setup BREDR, LE or DUAL scanning!
1789 //
1790 // Also catches case where discovery changes w/o user interaction [start/stop]Discovery(..)
1791 // if sourced from mgmt channel (!hciSourced)
1792
1793 ScanType nextMetaScanType;
1794 if( eventEnabled ) {
1795 // enabled eventScanType
1796 nextMetaScanType = changeScanType(currentMetaScanType, eventScanType, true);
1797 } else {
1798 // disabled eventScanType
1799 if( is_set(eventScanType, ScanType::LE) && DiscoveryPolicy::AUTO_OFF != discovery_policy ) {
1800 // Unchanged meta for disabled-LE && keep_le_scan_alive
1801 nextMetaScanType = currentMetaScanType;
1802 } else {
1803 nextMetaScanType = changeScanType(currentMetaScanType, eventScanType, false);
1804 }
1805 }
1806
1807 if( !hciSourced ) {
1808 // update HCIHandler's currentNativeScanType from other source
1809 const ScanType nextNativeScanType = changeScanType(currentNativeScanType, eventScanType, eventEnabled);
1810 DBG_PRINT("BTAdapter:%s:DeviceDiscovering: dev_id %d, policy %s: scanType[native %s -> %s, meta %s -> %s])",
1811 srctkn.c_str(), dev_id, to_string(discovery_policy).c_str(),
1812 to_string(currentNativeScanType).c_str(), to_string(nextNativeScanType).c_str(),
1813 to_string(currentMetaScanType).c_str(), to_string(nextMetaScanType).c_str());
1814 currentNativeScanType = nextNativeScanType;
1815 hci.setCurrentScanType(currentNativeScanType);
1816 } else {
1817 DBG_PRINT("BTAdapter:%s:DeviceDiscovering: dev_id %d, policy %d: scanType[native %s, meta %s -> %s])",
1818 srctkn.c_str(), dev_id, to_string(discovery_policy).c_str(),
1819 to_string(currentNativeScanType).c_str(),
1820 to_string(currentMetaScanType).c_str(), to_string(nextMetaScanType).c_str());
1821 }
1822 currentMetaScanType = nextMetaScanType;
1823 if( isDiscovering() ) {
1824 btRole = BTRole::Master;
1825 }
1826
1827 checkDiscoveryState();
1828
1829 int i=0;
1830 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
1831 try {
1832 p.listener->discoveringChanged(*this, currentMetaScanType, eventScanType, eventEnabled, discovery_policy, eventTimestamp);
1833 } catch (std::exception &except) {
1834 ERR_PRINT("BTAdapter:%s:DeviceDiscovering-CBs %d/%zd: %s of %s: Caught exception %s",
1835 srctkn.c_str(), i+1, statusListenerList.size(),
1836 p.listener->toString().c_str(), toString().c_str(), except.what());
1837 }
1838 i++;
1839 });
1840
1841 if( !is_set(currentNativeScanType, ScanType::LE) &&
1842 DiscoveryPolicy::AUTO_OFF != discovery_policy &&
1843 0 == getDevicesPausingDiscoveryCount() )
1844 {
1845 discovery_service.start();
1846 }
1847}
1848
1849void BTAdapter::mgmtEvNewSettingsMgmt(const MgmtEvent& e) noexcept {
1850 COND_PRINT(debug_event, "BTAdapter:mgmt:NewSettings: %s", e.toString().c_str());
1851 const MgmtEvtNewSettings &event = *static_cast<const MgmtEvtNewSettings *>(&e);
1852 const AdapterSetting new_settings = adapterInfo.setCurrentSettingMask(event.getSettings()); // probably done by mgmt callback already
1853
1854 updateAdapterSettings(true /* off_thread */, new_settings, true /* sendEvent */, event.getTimestamp());
1855}
1856
1857void BTAdapter::updateAdapterSettings(const bool off_thread, const AdapterSetting new_settings, const bool sendEvent, const uint64_t timestamp) noexcept {
1858 const AdapterSetting old_settings_ = old_settings;
1859
1860 const AdapterSetting changes = getAdapterSettingMaskDiff(new_settings, old_settings_);
1861
1862 const bool justPoweredOn = isAdapterSettingBitSet(changes, AdapterSetting::POWERED) &&
1864
1865 const bool justPoweredOff = isAdapterSettingBitSet(changes, AdapterSetting::POWERED) &&
1867
1868 old_settings = new_settings;
1869
1870 COND_PRINT(debug_event, "BTAdapter::updateAdapterSettings: %s -> %s, changes %s: %s, sendEvent %d, offThread %d",
1871 to_string(old_settings_).c_str(),
1872 to_string(new_settings).c_str(),
1873 to_string(changes).c_str(), toString().c_str(), sendEvent, off_thread ); // NOLINT(clang-analyzer-optin.cplusplus.VirtualCall)
1874
1875 updateDataFromAdapterInfo();
1876
1877 if( justPoweredOn ) {
1878 // Adapter has been powered on, ensure all hci states are reset.
1879 if( hci.resetAllStates(true) ) {
1880 updateDataFromHCI();
1881 }
1882 }
1883 if( sendEvent && AdapterSetting::NONE != changes ) {
1884 sendAdapterSettingsChanged(old_settings_, new_settings, changes, timestamp);
1885 }
1886
1887 if( justPoweredOff ) {
1888 // Adapter has been powered off, close connections and cleanup off-thread.
1889 if( off_thread ) {
1890 std::thread bg(&BTAdapter::poweredOff, this, false, "adapter_settings.0"); // @suppress("Invalid arguments")
1891 bg.detach();
1892 } else {
1893 poweredOff(false, "powered_off.1");
1894 }
1895 }
1896}
1897
1898void BTAdapter::mgmtEvLocalNameChangedMgmt(const MgmtEvent& e) noexcept {
1899 COND_PRINT(debug_event, "BTAdapter:mgmt:LocalNameChanged: %s", e.toString().c_str());
1900 const MgmtEvtLocalNameChanged &event = *static_cast<const MgmtEvtLocalNameChanged *>(&e);
1901 std::string old_name = getName();
1902 std::string old_shortName = getShortName();
1903 const bool nameChanged = old_name != event.getName();
1904 const bool shortNameChanged = old_shortName != event.getShortName();
1905 if( nameChanged ) {
1906 adapterInfo.setName(event.getName());
1907 }
1908 if( shortNameChanged ) {
1909 adapterInfo.setShortName(event.getShortName());
1910 }
1911 COND_PRINT(debug_event, "BTAdapter:mgmt:LocalNameChanged: Local name: %d: '%s' -> '%s'; short_name: %d: '%s' -> '%s'",
1912 nameChanged, old_name.c_str(), getName().c_str(),
1913 shortNameChanged, old_shortName.c_str(), getShortName().c_str());
1914 (void)nameChanged;
1915 (void)shortNameChanged;
1916}
1917
1918void BTAdapter::l2capServerInit(jau::service_runner& sr0) noexcept {
1919 (void)sr0;
1920
1921 l2cap_att_srv.set_interrupted_query( jau::bind_member(&l2cap_service, &jau::service_runner::shall_stop2) );
1922
1923 if( !l2cap_att_srv.open() ) {
1924 ERR_PRINT("Adapter[%d]: L2CAP ATT open failed: %s", dev_id, l2cap_att_srv.toString().c_str());
1925 }
1926}
1927
1928void BTAdapter::l2capServerEnd(jau::service_runner& sr) noexcept {
1929 (void)sr;
1930 if( !l2cap_att_srv.close() ) {
1931 ERR_PRINT("Adapter[%d]: L2CAP ATT close failed: %s", dev_id, l2cap_att_srv.toString().c_str());
1932 }
1933}
1934
1935void BTAdapter::l2capServerWork(jau::service_runner& sr) noexcept {
1936 (void)sr;
1937 std::unique_ptr<L2CAPClient> l2cap_att_ = l2cap_att_srv.accept();
1938 if( BTRole::Slave == getRole() && nullptr != l2cap_att_ && l2cap_att_->getRemoteAddressAndType().isLEAddress() ) {
1939 DBG_PRINT("L2CAP-ACCEPT: BTAdapter::l2capServer connected.1: (public) %s", l2cap_att_->toString().c_str());
1940
1941 std::unique_lock<std::mutex> lock(mtx_l2cap_att); // RAII-style acquire and relinquish via destructor
1942 l2cap_att = std::move( l2cap_att_ );
1943 lock.unlock(); // unlock mutex before notify_all to avoid pessimistic re-block of notified wait() thread.
1944 cv_l2cap_att.notify_all(); // notify waiting getter
1945
1946 } else if( nullptr != l2cap_att_ ) {
1947 DBG_PRINT("L2CAP-ACCEPT: BTAdapter::l2capServer connected.2: (ignored) %s", l2cap_att_->toString().c_str());
1948 } else {
1949 DBG_PRINT("L2CAP-ACCEPT: BTAdapter::l2capServer connected.0: nullptr");
1950 }
1951}
1952
1953std::unique_ptr<L2CAPClient> BTAdapter::get_l2cap_connection(const std::shared_ptr<BTDevice>& device) {
1954 if( BTRole::Slave != getRole() ) {
1955 DBG_PRINT("L2CAP-ACCEPT: BTAdapter:get_l2cap_connection(dev_id %d): Not in server mode", dev_id);
1956 return nullptr;
1957 }
1959
1960 std::unique_lock<std::mutex> lock(mtx_l2cap_att); // RAII-style acquire and relinquish via destructor
1962 while( device->getConnected() && ( nullptr == l2cap_att || l2cap_att->getRemoteAddressAndType() != device->getAddressAndType() ) ) {
1963 std::cv_status s = wait_until(cv_l2cap_att, lock, timeout_time);
1964 if( std::cv_status::timeout == s && ( nullptr == l2cap_att || l2cap_att->getRemoteAddressAndType() != device->getAddressAndType() ) ) {
1965 DBG_PRINT("L2CAP-ACCEPT: BTAdapter:get_l2cap_connection(dev_id %d): l2cap_att TIMEOUT", dev_id);
1966 return nullptr;
1967 }
1968 }
1969 if( nullptr != l2cap_att && l2cap_att->getRemoteAddressAndType() == device->getAddressAndType() ) {
1970 std::unique_ptr<L2CAPClient> l2cap_att_ = std::move( l2cap_att );
1971 DBG_PRINT("L2CAP-ACCEPT: BTAdapter:get_l2cap_connection(dev_id %d): Accept: l2cap_att %s", dev_id, l2cap_att_->toString().c_str());
1972 return l2cap_att_; // copy elision
1973 } else if( nullptr != l2cap_att ) {
1974 std::unique_ptr<L2CAPClient> l2cap_att_ = std::move( l2cap_att );
1975 DBG_PRINT("L2CAP-ACCEPT: BTAdapter:get_l2cap_connection(dev_id %d): Ignore: l2cap_att %s", dev_id, l2cap_att_->toString().c_str());
1976 return nullptr;
1977 } else {
1978 DBG_PRINT("L2CAP-ACCEPT: BTAdapter:get_l2cap_connection(dev_id %d): Null: Might got disconnected", dev_id);
1979 return nullptr;
1980 }
1981}
1982
1983jau::fraction_i64 BTAdapter::smp_timeoutfunc(jau::simple_timer& timer) {
1984 if( timer.shall_stop() ) {
1985 return 0_s;
1986 }
1987 device_list_t failed_devices;
1988 {
1989 const bool useServerAuth = BTRole::Slave == getRole() &&
1990 BTSecurityLevel::ENC_AUTH <= sec_level_server &&
1991 hasSMPIOCapabilityAnyIO(io_cap_server);
1992
1993 const std::lock_guard<std::mutex> lock(mtx_connectedDevices); // RAII-style acquire and relinquish via destructor
1994 jau::for_each_fidelity(connectedDevices, [&](BTDeviceRef& device) {
1995 if( device->isValidInstance() && device->getConnected() &&
1996 !useServerAuth &&
1997 BTSecurityLevel::NONE < device->getConnSecurityLevel() &&
1998 SMPPairingState::KEY_DISTRIBUTION == device->pairing_data.state )
1999 // isSMPPairingActive( device->pairing_data.state ) &&
2000 // !isSMPPairingUserInteraction( device->pairing_data.state ) )
2001 {
2002 // actively within SMP negotiations, excluding user interaction
2003 const uint32_t smp_events = device->smp_events;
2004 if( 0 == smp_events ) {
2005 DBG_PRINT("BTAdapter::smp_timeoutfunc(dev_id %d): SMP Timeout: Pairing-Failed %u: %s", dev_id, smp_events, device->toString().c_str());
2006 failed_devices.push_back(device);
2007 } else {
2008 DBG_PRINT("BTAdapter::smp_timeoutfunc(dev_id %d): SMP Timeout: Ignore-2 %u -> 0: %s", dev_id, smp_events, device->toString().c_str());
2009 device->smp_events = 0;
2010 }
2011 } else {
2012 const uint32_t smp_events = device->smp_events;
2013 if( 0 < smp_events ) {
2014 DBG_PRINT("BTAdapter::smp_timeoutfunc(dev_id %d): SMP Timeout: Ignore-1 %u: %s", dev_id, smp_events, device->toString().c_str());
2015 device->smp_events = 0;
2016 }
2017 }
2018 });
2019 }
2020 jau::for_each_fidelity(failed_devices, [&](BTDeviceRef& device) {
2021 const bool smp_auto = device->isConnSecurityAutoEnabled();
2022 IRQ_PRINT("BTAdapter(dev_id %d): SMP Timeout: Start: smp_auto %d, %s", dev_id, smp_auto, device->toString().c_str());
2024 const HCIACLData::l2cap_frame source(device->getConnectionHandle(),
2026 L2CAP_CID::SMP, L2CAP_PSM::UNDEFINED, 0 /* len */);
2027 // BTAdapter::sendDevicePairingState() will delete device-key if server and issue disconnect if !smp_auto
2028 device->hciSMPMsgCallback(device, msg, source);
2029 DBG_PRINT("BTAdapter::smp_timeoutfunc(dev_id %d): SMP Timeout: Done: smp_auto %d, %s", dev_id, smp_auto, device->toString().c_str());
2030 });
2031 return timer.shall_stop() ? 0_s : SMP_NEXT_EVENT_TIMEOUT_MS; // keep going until BTAdapter closes
2032}
2033
2034void BTAdapter::mgmtEvDeviceConnectedHCI(const MgmtEvent& e) noexcept {
2035 const MgmtEvtDeviceConnected &event = *static_cast<const MgmtEvtDeviceConnected *>(&e);
2036 EInfoReport ad_report;
2037 {
2038 ad_report.setSource(EInfoReport::Source::EIR, false);
2039 ad_report.setTimestamp(event.getTimestamp());
2040 ad_report.setAddressType(event.getAddressType());
2041 ad_report.setAddress( event.getAddress() );
2042 ad_report.read_data(event.getData(), event.getDataSize());
2043 }
2044 DBG_PRINT("BTAdapter::mgmtEvDeviceConnectedHCI(dev_id %d): Event %s, AD EIR %s",
2045 dev_id, e.toString().c_str(), ad_report.toString(true).c_str());
2046
2047 int new_connect = 0;
2048 bool device_discovered = true;
2049 bool slave_unpair = false;
2050 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2051 if( nullptr == device ) {
2052 device = findDiscoveredDevice(event.getAddress(), event.getAddressType());
2053 if( nullptr != device ) {
2054 addSharedDevice(device); // connected devices must be in shared + discovered list
2055 new_connect = 1;
2056 }
2057 }
2058 if( nullptr == device ) {
2059 device = findSharedDevice(event.getAddress(), event.getAddressType());
2060 if( nullptr != device ) {
2061 addDiscoveredDevice(device); // connected devices must be in shared + discovered list
2062 new_connect = 2;
2063 slave_unpair = BTRole::Slave == getRole();
2064 // Re device_discovered: Even though device was not in discoveredDevices list above,
2065 // it once was hence the device is in the shared device list.
2066 // Note that discoveredDevices is flushed w/ startDiscovery()!
2067 }
2068 }
2069 if( nullptr == device ) {
2070 // (new_connect = 3) a whitelist auto-connect w/o previous discovery, or
2071 // (new_connect = 4) we are a peripheral being connected by a remote client
2072 device_discovered = false;
2073 device = BTDevice::make_shared(*this, ad_report);
2074 addDiscoveredDevice(device);
2075 addSharedDevice(device);
2076 new_connect = BTRole::Master == getRole() ? 3 : 4;
2077 slave_unpair = BTRole::Slave == getRole();
2078 }
2079 bool has_smp_keys;
2080 if( BTRole::Slave == getRole() ) {
2081 has_smp_keys = nullptr != findSMPKeyBin( device->getAddressAndType() ); // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS
2082 } else {
2083 has_smp_keys = false;
2084 }
2085
2086 DBG_PRINT("BTAdapter:hci:DeviceConnected(dev_id %d): state[role %s, new %d, discovered %d, unpair %d, has_keys %d], %s: %s",
2087 dev_id, to_string(getRole()).c_str(), new_connect, device_discovered, slave_unpair, has_smp_keys,
2088 e.toString().c_str(), ad_report.toString().c_str());
2089
2090 if( slave_unpair ) {
2091 /**
2092 * Without unpair in SC mode (or key pre-pairing), the peripheral fails the DHKey Check.
2093 */
2094 if( !has_smp_keys ) {
2095 // No pre-pairing -> unpair
2096 HCIStatusCode res = mgmt->unpairDevice(dev_id, device->getAddressAndType(), false /* disconnect */);
2097 if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) {
2098 WARN_PRINT("(dev_id %d, new_connect %d): Unpair device failed %s of %s",
2099 dev_id, new_connect, to_string(res).c_str(), device->getAddressAndType().toString().c_str());
2100 }
2101 }
2102 }
2103
2104 const SMPIOCapability io_cap_has = mgmt->getIOCapability(dev_id);
2105
2106 EIRDataType updateMask = device->update(ad_report);
2107 if( 0 == new_connect ) {
2108 WARN_PRINT("(dev_id %d, already connected, updated %s): %s, handle %s -> %s,\n %s,\n -> %s",
2109 dev_id, to_string(updateMask).c_str(), event.toString().c_str(),
2110 jau::to_hexstring(device->getConnectionHandle()).c_str(), jau::to_hexstring(event.getHCIHandle()).c_str(),
2111 ad_report.toString().c_str(),
2112 device->toString().c_str());
2113 } else {
2114 addConnectedDevice(device); // track device, if not done yet
2115 COND_PRINT(debug_event, "BTAdapter::hci:DeviceConnected(dev_id %d, new_connect %d, updated %s): %s, handle %s -> %s,\n %s,\n -> %s",
2116 dev_id, new_connect, to_string(updateMask).c_str(), event.toString().c_str(),
2117 jau::to_hexstring(device->getConnectionHandle()).c_str(), jau::to_hexstring(event.getHCIHandle()).c_str(),
2118 ad_report.toString().c_str(),
2119 device->toString().c_str());
2120 }
2121
2122 if( BTRole::Slave == getRole() ) {
2123 // filters and sets device->pairing_data.{sec_level_user, ioCap_user}
2124 device->setConnSecurity(sec_level_server, io_cap_server);
2125 }
2126 device->notifyConnected(device, event.getHCIHandle(), io_cap_has);
2127
2128 if( device->isConnSecurityAutoEnabled() ) {
2129 new_connect = 0; // disable deviceConnected() events for BTRole::Master for SMP-Auto
2130 }
2131
2132 int i=0;
2133 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2134 try {
2135 if( p.match(device) ) {
2136 if( EIRDataType::NONE != updateMask ) {
2137 p.listener->deviceUpdated(device, updateMask, ad_report.getTimestamp());
2138 }
2139 if( 0 < new_connect ) {
2140 p.listener->deviceConnected(device, device_discovered, event.getTimestamp());
2141 }
2142 }
2143 } catch (std::exception &except) {
2144 ERR_PRINT("BTAdapter::hci:DeviceConnected-CBs %d/%zd: %s of %s: Caught exception %s",
2145 i+1, statusListenerList.size(),
2146 p.listener->toString().c_str(), device->toString().c_str(), except.what());
2147 }
2148 i++;
2149 });
2150 if( BTRole::Slave == getRole() ) {
2151 // For BTRole::Master, BlueZ Kernel does request LE_Features already
2152 // Hence we have to trigger sending LE_Features for BTRole::Slave ourselves
2153 //
2154 // This is mandatory to trigger BTDevice::notifyLEFeatures() via our mgmtEvHCILERemoteUserFeaturesHCI()
2155 // to proceed w/ post-connection and eventually issue deviceRead().
2156
2157 // Replied with: Command Status 0x0f (timeout) and Status 0x0c (disallowed),
2158 // despite core-spec states valid for both master and slave.
2159 // hci.le_read_remote_features(event.getHCIHandle(), device->getAddressAndType());
2160
2161 // Hence .. induce it right after connect
2162 device->notifyLEFeatures(device, LE_Features::LE_Encryption);
2163 }
2164}
2165void BTAdapter::mgmtEvDeviceConnectedMgmt(const MgmtEvent& e) noexcept {
2166 const MgmtEvtDeviceConnected &event = *static_cast<const MgmtEvtDeviceConnected *>(&e);
2167 EInfoReport ad_report;
2168 {
2169 ad_report.setSource(EInfoReport::Source::EIR, false);
2170 ad_report.setTimestamp(event.getTimestamp());
2171 ad_report.setAddressType(event.getAddressType());
2172 ad_report.setAddress( event.getAddress() );
2173 ad_report.read_data(event.getData(), event.getDataSize());
2174 }
2175 DBG_PRINT("BTAdapter::mgmtEvDeviceConnectedMgmt(dev_id %d): Event %s, AD EIR %s",
2176 dev_id, e.toString().c_str(), ad_report.toString(true).c_str());
2177}
2178
2179void BTAdapter::mgmtEvConnectFailedHCI(const MgmtEvent& e) noexcept {
2180 const MgmtEvtDeviceConnectFailed &event = *static_cast<const MgmtEvtDeviceConnectFailed *>(&e);
2181
2182 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2183 if( nullptr != device ) {
2184 const uint16_t handle = device->getConnectionHandle();
2185 DBG_PRINT("BTAdapter::hci:ConnectFailed(dev_id %d): %s, handle %s -> zero,\n -> %s",
2186 dev_id, event.toString().c_str(), jau::to_hexstring(handle).c_str(),
2187 device->toString().c_str());
2188
2189 unlockConnect(*device);
2190 device->notifyDisconnected();
2191 removeConnectedDevice(*device);
2192
2193 if( !device->isConnSecurityAutoEnabled() ) {
2194 int i=0;
2195 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2196 try {
2197 if( p.match(device) ) {
2198 p.listener->deviceDisconnected(device, event.getHCIStatus(), handle, event.getTimestamp());
2199 }
2200 } catch (std::exception &except) {
2201 ERR_PRINT("BTAdapter::hci:DeviceDisconnected-CBs %d/%zd: %s of %s: Caught exception %s",
2202 i+1, statusListenerList.size(),
2203 p.listener->toString().c_str(), device->toString().c_str(), except.what());
2204 }
2205 i++;
2206 });
2207 device->clearData();
2208 removeDiscoveredDevice(device->addressAndType); // ensure device will cause a deviceFound event after disconnect
2209 }
2210 } else {
2211 WORDY_PRINT("BTAdapter::hci:DeviceDisconnected(dev_id %d): Device not tracked: %s",
2212 dev_id, event.toString().c_str());
2213 }
2214}
2215
2216void BTAdapter::mgmtEvHCILERemoteUserFeaturesHCI(const MgmtEvent& e) noexcept {
2217 const MgmtEvtHCILERemoteFeatures &event = *static_cast<const MgmtEvtHCILERemoteFeatures *>(&e);
2218
2219 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2220 if( nullptr != device ) {
2221 COND_PRINT(debug_event, "BTAdapter::hci:LERemoteUserFeatures(dev_id %d): %s, %s",
2222 dev_id, event.toString().c_str(), device->toString().c_str());
2223
2224 if( BTRole::Master == getRole() ) {
2225 const DiscoveryPolicy policy = discovery_policy;
2226 if( DiscoveryPolicy::AUTO_OFF == policy ) {
2227 if constexpr ( SCAN_DISABLED_POST_CONNECT ) {
2228 updateDeviceDiscoveringState(ScanType::LE, false /* eventEnabled */);
2229 } else {
2230 std::thread bg(&BTAdapter::stopDiscoveryImpl, this, false /* forceDiscoveringEvent */, true /* temporary */); // @suppress("Invalid arguments")
2231 bg.detach();
2232 }
2233 } else if( DiscoveryPolicy::ALWAYS_ON == policy ) {
2234 if constexpr ( SCAN_DISABLED_POST_CONNECT ) {
2235 updateDeviceDiscoveringState(ScanType::LE, false /* eventEnabled */);
2236 } else {
2237 discovery_service.start();
2238 }
2239 } else {
2240 addDevicePausingDiscovery(device);
2241 }
2242 }
2243 if( HCIStatusCode::SUCCESS == event.getHCIStatus() ) {
2244 device->notifyLEFeatures(device, event.getFeatures());
2245 // Performs optional SMP pairing, then one of
2246 // - sendDeviceReady()
2247 // - disconnect() .. eventually
2248 } // else: disconnect will occur
2249 } else {
2250 WORDY_PRINT("BTAdapter::hci:LERemoteUserFeatures(dev_id %d): Device not tracked: %s",
2251 dev_id, event.toString().c_str());
2252 }
2253}
2254
2255void BTAdapter::mgmtEvHCILEPhyUpdateCompleteHCI(const MgmtEvent& e) noexcept {
2256 const MgmtEvtHCILEPhyUpdateComplete &event = *static_cast<const MgmtEvtHCILEPhyUpdateComplete *>(&e);
2257
2258 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2259 if( nullptr != device ) {
2260 COND_PRINT(debug_event, "BTAdapter::hci:LEPhyUpdateComplete(dev_id %d): %s, %s",
2261 dev_id, event.toString().c_str(), device->toString().c_str());
2262
2263 device->notifyLEPhyUpdateComplete(event.getHCIStatus(), event.getTx(), event.getRx());
2264 } else {
2265 WORDY_PRINT("BTAdapter::hci:LEPhyUpdateComplete(dev_id %d): Device not tracked: %s",
2266 dev_id, event.toString().c_str());
2267 }
2268}
2269
2270void BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept {
2271 const MgmtEvtDeviceDisconnected &event = *static_cast<const MgmtEvtDeviceDisconnected *>(&e);
2272
2273 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2274 if( nullptr != device ) {
2275 if( device->getConnectionHandle() != event.getHCIHandle() ) {
2276 WORDY_PRINT("BTAdapter::hci:DeviceDisconnected(dev_id %d): ConnHandle mismatch %s\n -> %s",
2277 dev_id, event.toString().c_str(), device->toString().c_str());
2278 return;
2279 }
2280 DBG_PRINT("BTAdapter::hci:DeviceDisconnected(dev_id %d): %s, handle %s -> zero,\n -> %s",
2281 dev_id, event.toString().c_str(), jau::to_hexstring(event.getHCIHandle()).c_str(),
2282 device->toString().c_str());
2283
2284 unlockConnect(*device);
2285 device->notifyDisconnected(); // -> unpair()
2286 removeConnectedDevice(*device);
2287 if( BTRole::Slave == btRole ) {
2288 // Keep valid in LL master discovery mode (client).
2289 gattServerData = nullptr;
2290 }
2291
2292 if( !device->isConnSecurityAutoEnabled() ) {
2293 int i=0;
2294 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2295 try {
2296 if( p.match(device) ) {
2297 p.listener->deviceDisconnected(device, event.getHCIReason(), event.getHCIHandle(), event.getTimestamp());
2298 }
2299 } catch (std::exception &except) {
2300 ERR_PRINT("BTAdapter::hci:DeviceDisconnected-CBs %d/%zd: %s of %s: Caught exception %s",
2301 i+1, statusListenerList.size(),
2302 p.listener->toString().c_str(), device->toString().c_str(), except.what());
2303 }
2304 i++;
2305 });
2306 device->clearData();
2307 removeDiscoveredDevice(device->addressAndType); // ensure device will cause a deviceFound event after disconnect
2308 }
2309 if( BTRole::Slave == getRole() ) {
2310 // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS
2311 if( HCIStatusCode::AUTHENTICATION_FAILURE == event.getHCIReason() ||
2313 {
2314 // Pairing failed on the remote client side
2315 removeSMPKeyBin(device->getAddressAndType(), true /* remove_file */);
2316 } else {
2317 SMPKeyBinRef key = findSMPKeyBin(device->getAddressAndType());
2318 if( nullptr != key ) {
2319 HCIStatusCode res;
2320 #if 0
2321 res = getManager().unpairDevice(dev_id, device->getAddressAndType(), true /* disconnect */);
2322 if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) {
2323 WARN_PRINT("(dev_id %d): Unpair device failed %s of %s",
2324 dev_id, to_string(res).c_str(), device->getAddressAndType().toString().c_str());
2325 }
2326 res = device->uploadKeys(*key, BTSecurityLevel::NONE);
2327 #else
2328 res = device->uploadKeys(*key, BTSecurityLevel::NONE);
2329 #endif
2330 if( HCIStatusCode::SUCCESS != res ) {
2331 WARN_PRINT("(dev_id %d): Upload SMPKeyBin failed %s, %s (removing file)",
2332 dev_id, to_string(res).c_str(), key->toString().c_str());
2333 removeSMPKeyBin(device->getAddressAndType(), true /* remove_file */);
2334 }
2335 }
2336 }
2337 }
2338 removeDevicePausingDiscovery(*device);
2339 } else {
2340 DBG_PRINT("BTAdapter::hci:DeviceDisconnected(dev_id %d): Device not connected: %s",
2341 dev_id, event.toString().c_str());
2342 if( _print_device_lists || jau::environment::get().verbose ) {
2343 printDeviceLists();
2344 }
2345 device = findDevicePausingDiscovery(event.getAddress(), event.getAddressType());
2346 if( nullptr != device ) {
2347 removeDevicePausingDiscovery(*device);
2348 }
2349 }
2350}
2351
2352// Local BTRole::Slave
2353void BTAdapter::mgmtEvLELTKReqEventHCI(const MgmtEvent& e) noexcept {
2354 const MgmtEvtHCILELTKReq &event = *static_cast<const MgmtEvtHCILELTKReq *>(&e);
2355
2356 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2357 if( nullptr != device ) {
2358 // BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65.5 LE Long Term Key Request event
2359 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::COMPLETED);
2360 } else {
2361 WORDY_PRINT("BTAdapter::hci:LE_LTK_Request(dev_id %d): Device not tracked: %s",
2362 dev_id, event.toString().c_str());
2363 }
2364}
2365void BTAdapter::mgmtEvLELTKReplyAckCmdHCI(const MgmtEvent& e) noexcept {
2366 const MgmtEvtHCILELTKReplyAckCmd &event = *static_cast<const MgmtEvtHCILELTKReplyAckCmd *>(&e);
2367
2368 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2369 if( nullptr != device ) {
2370 // BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.25 LE Long Term Key Request Reply command
2371 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::COMPLETED);
2372 } else {
2373 WORDY_PRINT("BTAdapter::hci:LE_LTK_REPLY_ACK(dev_id %d): Device not tracked: %s",
2374 dev_id, event.toString().c_str());
2375 }
2376}
2377void BTAdapter::mgmtEvLELTKReplyRejCmdHCI(const MgmtEvent& e) noexcept {
2378 const MgmtEvtHCILELTKReplyRejCmd &event = *static_cast<const MgmtEvtHCILELTKReplyRejCmd *>(&e);
2379
2380 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2381 DBG_PRINT("BTAdapter::hci:LE_LTK_REPLY_REJ(dev_id %d): Ignored: %s (tracked %d)",
2382 dev_id, event.toString().c_str(), (nullptr!=device));
2383}
2384
2385// Local BTRole::Master
2386void BTAdapter::mgmtEvLEEnableEncryptionCmdHCI(const MgmtEvent& e) noexcept {
2387 const MgmtEvtHCILEEnableEncryptionCmd &event = *static_cast<const MgmtEvtHCILEEnableEncryptionCmd *>(&e);
2388
2389 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2390 if( nullptr != device ) {
2391 // BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.24 LE Enable Encryption command
2392 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::COMPLETED);
2393 } else {
2394 WORDY_PRINT("BTAdapter::hci:LE_ENABLE_ENC(dev_id %d): Device not tracked: %s",
2395 dev_id, event.toString().c_str());
2396 }
2397}
2398// On BTRole::Master (reply to MgmtEvtHCILEEnableEncryptionCmd) and BTRole::Slave
2399void BTAdapter::mgmtEvHCIEncryptionChangedHCI(const MgmtEvent& e) noexcept {
2400 const MgmtEvtHCIEncryptionChanged &event = *static_cast<const MgmtEvtHCIEncryptionChanged *>(&e);
2401
2402 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2403 if( nullptr != device ) {
2404 // BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.8 HCIEventType::ENCRYPT_CHANGE
2405 const HCIStatusCode evtStatus = event.getHCIStatus();
2406 const bool ok = HCIStatusCode::SUCCESS == evtStatus && 0 != event.getEncEnabled();
2408 device->updatePairingState(device, e, evtStatus, pstate);
2409 } else {
2410 WORDY_PRINT("BTAdapter::hci:ENC_CHANGED(dev_id %d): Device not tracked: %s",
2411 dev_id, event.toString().c_str());
2412 }
2413}
2414// On BTRole::Master (reply to MgmtEvtHCILEEnableEncryptionCmd) and BTRole::Slave
2415void BTAdapter::mgmtEvHCIEncryptionKeyRefreshCompleteHCI(const MgmtEvent& e) noexcept {
2417
2418 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2419 if( nullptr != device ) {
2420 // BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.39 HCIEventType::ENCRYPT_KEY_REFRESH_COMPLETE
2421 const HCIStatusCode evtStatus = event.getHCIStatus();
2422 const bool ok = HCIStatusCode::SUCCESS == evtStatus;
2424 device->updatePairingState(device, e, evtStatus, pstate);
2425 } else {
2426 WORDY_PRINT("BTAdapter::hci:ENC_KEY_REFRESH_COMPLETE(dev_id %d): Device not tracked: %s",
2427 dev_id, event.toString().c_str());
2428 }
2429}
2430
2431void BTAdapter::mgmtEvPairDeviceCompleteMgmt(const MgmtEvent& e) noexcept {
2432 const MgmtEvtPairDeviceComplete &event = *static_cast<const MgmtEvtPairDeviceComplete *>(&e);
2433
2434 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2435 if( nullptr != device ) {
2436 const HCIStatusCode evtStatus = to_HCIStatusCode( event.getStatus() );
2437 const bool ok = HCIStatusCode::ALREADY_PAIRED == evtStatus;
2439 device->updatePairingState(device, e, evtStatus, pstate);
2440 } else {
2441 WORDY_PRINT("BTAdapter::mgmt:PairDeviceComplete(dev_id %d): Device not tracked: %s",
2442 dev_id, event.toString().c_str());
2443 }
2444}
2445
2446void BTAdapter::mgmtEvNewLongTermKeyMgmt(const MgmtEvent& e) noexcept {
2447 const MgmtEvtNewLongTermKey& event = *static_cast<const MgmtEvtNewLongTermKey *>(&e);
2448 const MgmtLongTermKey& ltk_info = event.getLongTermKey();
2449 BTDeviceRef device = findConnectedDevice(ltk_info.address, ltk_info.address_type);
2450 if( nullptr != device ) {
2451 const bool ok = ltk_info.enc_size > 0 && ltk_info.key_type != MgmtLTKType::NONE;
2452 if( ok ) {
2453 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::COMPLETED);
2454 } else {
2455 WORDY_PRINT("BTAdapter::mgmt:NewLongTermKey(dev_id %d): Invalid LTK: %s",
2456 dev_id, event.toString().c_str());
2457 }
2458 } else {
2459 WORDY_PRINT("BTAdapter::mgmt:NewLongTermKey(dev_id %d): Device not tracked: %s",
2460 dev_id, event.toString().c_str());
2461 }
2462}
2463
2464void BTAdapter::mgmtEvNewLinkKeyMgmt(const MgmtEvent& e) noexcept {
2465 const MgmtEvtNewLinkKey& event = *static_cast<const MgmtEvtNewLinkKey *>(&e);
2466 const MgmtLinkKeyInfo& lk_info = event.getLinkKey();
2467 // lk_info.address_type might be wrongly reported by mgmt, i.e. BDADDR_BREDR, use any.
2468 BTDeviceRef device = findConnectedDevice(lk_info.address, BDAddressType::BDADDR_UNDEFINED);
2469 if( nullptr != device ) {
2470 const bool ok = lk_info.key_type != MgmtLinkKeyType::NONE;
2471 if( ok ) {
2472 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::COMPLETED);
2473 } else {
2474 WORDY_PRINT("BTAdapter::mgmt:NewLinkKey(dev_id %d): Invalid LK: %s",
2475 dev_id, event.toString().c_str());
2476 }
2477 } else {
2478 WORDY_PRINT("BTAdapter::mgmt:NewLinkKey(dev_id %d): Device not tracked: %s",
2479 dev_id, event.toString().c_str());
2480 }
2481}
2482
2483void BTAdapter::mgmtEvNewIdentityResolvingKeyMgmt(const MgmtEvent& e) noexcept {
2484 const MgmtEvtNewIdentityResolvingKey& event = *static_cast<const MgmtEvtNewIdentityResolvingKey *>(&e);
2485 const EUI48& randomAddress = event.getRandomAddress();
2486 const MgmtIdentityResolvingKey& irk = event.getIdentityResolvingKey();
2487 if( adapterInfo.addressAndType.address == irk.address && adapterInfo.addressAndType.type == irk.address_type ) {
2488 // setPrivacy ...
2489 visibleAddressAndType.address = randomAddress;
2490 visibleAddressAndType.type = BDAddressType::BDADDR_LE_RANDOM;
2492 privacyIRK = irk;
2493 WORDY_PRINT("BTAdapter::mgmt:NewIdentityResolvingKey(dev_id %d): Host Adapter: %s",
2494 dev_id, event.toString().c_str());
2495 } else {
2496 BTDeviceRef device = findConnectedDevice(randomAddress, BDAddressType::BDADDR_UNDEFINED);
2497 if( nullptr != device ) {
2498 // Handled via SMP
2499 WORDY_PRINT("BTAdapter::mgmt:NewIdentityResolvingKey(dev_id %d): Device found (Resolvable): %s, %s",
2500 dev_id, event.toString().c_str(), device->toString().c_str());
2501 } else {
2502 WORDY_PRINT("BTAdapter::mgmt:NewIdentityResolvingKey(dev_id %d): Device not tracked: %s",
2503 dev_id, event.toString().c_str());
2504 }
2505 }
2506}
2507
2508void BTAdapter::mgmtEvDeviceFoundHCI(const MgmtEvent& e) noexcept {
2509 // COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(dev_id %d): %s", dev_id, e.toString().c_str());
2510 const MgmtEvtDeviceFound &deviceFoundEvent = *static_cast<const MgmtEvtDeviceFound *>(&e);
2511
2512 const EInfoReport* eir = deviceFoundEvent.getEIR();
2513 if( nullptr == eir ) {
2514 // Sourced from Linux Mgmt, which we don't support
2515 ABORT("BTAdapter:hci:DeviceFound: Not sourced from LE_ADVERTISING_REPORT: %s", deviceFoundEvent.toString().c_str());
2516 return; // unreachable
2517 } // else: Sourced from HCIHandler via LE_ADVERTISING_REPORT (default!)
2518
2519 /**
2520 * + ------+-----------+------------+----------+----------+-------------------------------------------+
2521 * | # | connected | discovered | shared | update |
2522 * +-------+-----------+------------+----------+----------+-------------------------------------------+
2523 * | 1.0 | true | any | any | ignored | Already connected device -> Drop(1)
2524 * | 1.1 | false | false | false | ignored | New undiscovered/unshared -> deviceFound(..)
2525 * | 1.2 | false | false | true | ignored | Undiscovered but shared -> deviceFound(..) [deviceUpdated(..)]
2526 * | 2.1.1 | false | true | false | name | Discovered but unshared, name changed -> deviceFound(..)
2527 * | 2.1.2 | false | true | false | !name | Discovered but unshared, no name change -> Drop(2)
2528 * | 2.2.1 | false | true | true | any | Discovered and shared, updated -> deviceUpdated(..)
2529 * | 2.2.2 | false | true | true | none | Discovered and shared, not-updated -> Drop(3)
2530 * +-------+-----------+------------+----------+----------+-------------------------------------------+
2531 */
2532 BTDeviceRef dev_connected = findConnectedDevice(eir->getAddress(), eir->getAddressType());
2533 BTDeviceRef dev_discovered = findDiscoveredDevice(eir->getAddress(), eir->getAddressType());
2534 BTDeviceRef dev_shared = findSharedDevice(eir->getAddress(), eir->getAddressType());
2535 if( nullptr != dev_connected ) {
2536 // already connected device shall be suppressed
2537 DBG_PRINT("BTAdapter:hci:DeviceFound(1.0, dev_id %d): Discovered but already connected %s [discovered %d, shared %d] -> Drop(1) %s",
2538 dev_id, dev_connected->getAddressAndType().toString().c_str(),
2539 nullptr != dev_discovered, nullptr != dev_shared, eir->toString().c_str());
2540 if( _print_device_lists || jau::environment::get().verbose ) {
2541 printDeviceLists();
2542 }
2543 } else if( nullptr == dev_discovered ) { // nullptr == dev_connected && nullptr == dev_discovered
2544 if( nullptr == dev_shared ) {
2545 //
2546 // All new discovered device
2547 //
2548 dev_shared = BTDevice::make_shared(*this, *eir);
2549 addDiscoveredDevice(dev_shared);
2550 addSharedDevice(dev_shared);
2551 DBG_PRINT("BTAdapter:hci:DeviceFound(1.1, dev_id %d): New undiscovered/unshared %s -> deviceFound(..) %s",
2552 dev_id, dev_shared->getAddressAndType().toString().c_str(), eir->toString().c_str());
2553 if( _print_device_lists || jau::environment::get().verbose ) {
2554 printDeviceLists();
2555 }
2556
2557 {
2558 const HCIStatusCode res = mgmt->unpairDevice(dev_id, dev_shared->getAddressAndType(), false /* disconnect */);
2559 if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) {
2560 WARN_PRINT("(dev_id %d): Unpair device failed %s of %s",
2561 dev_id, to_string(res).c_str(), dev_shared->getAddressAndType().toString().c_str());
2562 }
2563 }
2564 int i=0;
2565 bool device_used = false;
2566 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2567 try {
2568 if( p.match(dev_shared) ) {
2569 device_used = p.listener->deviceFound(dev_shared, eir->getTimestamp()) || device_used;
2570 }
2571 } catch (std::exception &except) {
2572 ERR_PRINT("BTAdapter:hci:DeviceFound-CBs %d/%zd: %s of %s: Caught exception %s",
2573 i+1, statusListenerList.size(),
2574 p.listener->toString().c_str(), dev_shared->toString().c_str(), except.what());
2575 }
2576 i++;
2577 });
2578 if( !device_used ) {
2579 // keep to avoid duplicate finds: removeDiscoveredDevice(dev_discovered->addressAndType);
2580 // and still allowing usage, as connecting will re-add to shared list
2581 removeSharedDevice(*dev_shared); // pending dtor if discovered is flushed
2582 }
2583 } else { // nullptr != dev_shared
2584 //
2585 // Active shared device, but flushed from discovered devices
2586 // - update device
2587 // - issue deviceFound(..), allowing receivers to recognize the re-discovered device
2588 // - issue deviceUpdate(..), if at least one deviceFound(..) returned true and data has changed, allowing receivers to act upon
2589 // - removeSharedDevice(..), if non deviceFound(..) returned true
2590 //
2591 EIRDataType updateMask = dev_shared->update(*eir);
2592 addDiscoveredDevice(dev_shared); // re-add to discovered devices!
2593 dev_shared->ts_last_discovery = eir->getTimestamp();
2594 DBG_PRINT("BTAdapter:hci:DeviceFound(1.2, dev_id %d): Undiscovered but shared %s -> deviceFound(..) [deviceUpdated(..)] %s",
2595 dev_id, dev_shared->getAddressAndType().toString().c_str(), eir->toString().c_str());
2596 if( _print_device_lists || jau::environment::get().verbose ) {
2597 printDeviceLists();
2598 }
2599
2600 if( !dev_shared->isPrePaired() ) {
2601 HCIStatusCode res = dev_shared->unpair();
2602 if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) {
2603 WARN_PRINT("(dev_id %d): Unpair device failed: %s, %s",
2604 dev_id, to_string(res).c_str(), dev_shared->getAddressAndType().toString().c_str());
2605 }
2606 }
2607 int i=0;
2608 bool device_used = false;
2609 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2610 try {
2611 if( p.match(dev_shared) ) {
2612 device_used = p.listener->deviceFound(dev_shared, eir->getTimestamp()) || device_used;
2613 }
2614 } catch (std::exception &except) {
2615 ERR_PRINT("BTAdapter:hci:DeviceFound: %d/%zd: %s of %s: Caught exception %s",
2616 i+1, statusListenerList.size(),
2617 p.listener->toString().c_str(), dev_shared->toString().c_str(), except.what());
2618 }
2619 i++;
2620 });
2621 if( !device_used ) {
2622 // keep to avoid duplicate finds: removeDiscoveredDevice(dev->addressAndType);
2623 // and still allowing usage, as connecting will re-add to shared list
2624 removeSharedDevice(*dev_shared); // pending dtor until discovered is flushed
2625 } else if( EIRDataType::NONE != updateMask ) {
2626 sendDeviceUpdated("SharedDeviceFound", dev_shared, eir->getTimestamp(), updateMask);
2627 }
2628 }
2629 } else { // nullptr == dev_connected && nullptr != dev_discovered
2630 //
2631 // Already discovered device
2632 //
2633 const EIRDataType updateMask = dev_discovered->update(*eir);
2634 dev_discovered->ts_last_discovery = eir->getTimestamp();
2635 if( nullptr == dev_shared ) {
2636 //
2637 // Discovered but not a shared device,
2638 // i.e. all user deviceFound(..) returned false - no interest/rejected.
2639 //
2640 if( EIRDataType::NONE != ( updateMask & EIRDataType::NAME ) ) {
2641 // Name got updated, send out deviceFound(..) again
2642 DBG_PRINT("BTAdapter:hci:DeviceFound(2.1.1, dev_id %d): Discovered but unshared %s, name changed %s -> deviceFound(..) %s",
2643 dev_id, dev_discovered->getAddressAndType().toString().c_str(),
2644 direct_bt::to_string(updateMask).c_str(), eir->toString().c_str());
2645 addSharedDevice(dev_discovered); // re-add to shared devices!
2646 if( _print_device_lists || jau::environment::get().verbose ) {
2647 printDeviceLists();
2648 }
2649 int i=0;
2650 bool device_used = false;
2651 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2652 try {
2653 if( p.match(dev_discovered) ) {
2654 device_used = p.listener->deviceFound(dev_discovered, eir->getTimestamp()) || device_used;
2655 }
2656 } catch (std::exception &except) {
2657 ERR_PRINT("BTAdapter:hci:DeviceFound: %d/%zd: %s of %s: Caught exception %s",
2658 i+1, statusListenerList.size(),
2659 p.listener->toString().c_str(), dev_discovered->toString().c_str(), except.what());
2660 }
2661 i++;
2662 });
2663 if( !device_used ) {
2664 // keep to avoid duplicate finds: removeDiscoveredDevice(dev_discovered->addressAndType);
2665 // and still allowing usage, as connecting will re-add to shared list
2666 removeSharedDevice(*dev_discovered); // pending dtor if discovered is flushed
2667 }
2668 } else {
2669 // Drop: NAME didn't change
2670 COND_PRINT(debug_event, "BTAdapter:hci:DeviceFound(2.1.2, dev_id %d): Discovered but unshared %s, no name change -> Drop(2) %s",
2671 dev_id, dev_discovered->getAddressAndType().toString().c_str(), eir->toString().c_str());
2672 }
2673 } else { // nullptr != dev_shared
2674 //
2675 // Discovered and shared device,
2676 // i.e. at least one deviceFound(..) returned true - interest/picked.
2677 //
2678 if( EIRDataType::NONE != updateMask ) {
2679 if( debug_event ) {
2680 jau::PLAIN_PRINT(true, "BTAdapter:hci:DeviceFound(2.2.1, dev_id %d): Discovered and shared %s, updated %s -> deviceUpdated(..) %s",
2681 dev_id, dev_shared->getAddressAndType().toString().c_str(),
2682 direct_bt::to_string(updateMask).c_str(), eir->toString().c_str());
2683 if( _print_device_lists || jau::environment::get().verbose ) {
2684 printDeviceLists();
2685 }
2686 }
2687 sendDeviceUpdated("DiscoveredDeviceFound", dev_shared, eir->getTimestamp(), updateMask);
2688 } else {
2689 // Drop: No update
2690 if( debug_event ) {
2691 jau::PLAIN_PRINT(true, "BTAdapter:hci:DeviceFound(2.2.2, dev_id %d): Discovered and shared %s, not-updated -> Drop(3) %s",
2692 dev_id, dev_shared->getAddressAndType().toString().c_str(), eir->toString().c_str());
2693 if( _print_device_lists || jau::environment::get().verbose ) {
2694 printDeviceLists();
2695 }
2696 }
2697 }
2698 }
2699 }
2700}
2701
2702void BTAdapter::mgmtEvDeviceUnpairedMgmt(const MgmtEvent& e) noexcept {
2703 const MgmtEvtDeviceUnpaired &event = *static_cast<const MgmtEvtDeviceUnpaired *>(&e);
2704 DBG_PRINT("BTAdapter:mgmt:DeviceUnpaired: %s", event.toString().c_str());
2705}
2706void BTAdapter::mgmtEvPinCodeRequestMgmt(const MgmtEvent& e) noexcept {
2707 const MgmtEvtPinCodeRequest &event = *static_cast<const MgmtEvtPinCodeRequest *>(&e);
2708
2709 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2710 if( nullptr == device ) {
2711 WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
2712 dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
2713 event.toString().c_str());
2714 return;
2715 }
2716 DBG_PRINT("BTAdapter:mgmt:PinCodeRequest: %s", event.toString().c_str());
2717 // FIXME: device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::PASSKEY_EXPECTED);
2718}
2719void BTAdapter::mgmtEvAuthFailedMgmt(const MgmtEvent& e) noexcept {
2720 const MgmtEvtAuthFailed &event = *static_cast<const MgmtEvtAuthFailed *>(&e);
2721
2722 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2723 if( nullptr == device ) {
2724 WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
2725 dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
2726 event.toString().c_str());
2727 return;
2728 }
2729 const HCIStatusCode evtStatus = to_HCIStatusCode( event.getStatus() );
2730 device->updatePairingState(device, e, evtStatus, SMPPairingState::FAILED);
2731}
2732void BTAdapter::mgmtEvUserConfirmRequestMgmt(const MgmtEvent& e) noexcept {
2733 const MgmtEvtUserConfirmRequest &event = *static_cast<const MgmtEvtUserConfirmRequest *>(&e);
2734
2735 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2736 if( nullptr == device ) {
2737 WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
2738 dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
2739 event.toString().c_str());
2740 return;
2741 }
2742 // FIXME: Pass confirm_hint and value?
2743 DBG_PRINT("BTAdapter:mgmt:UserConfirmRequest: %s", event.toString().c_str());
2744 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::NUMERIC_COMPARE_EXPECTED);
2745}
2746void BTAdapter::mgmtEvUserPasskeyRequestMgmt(const MgmtEvent& e) noexcept {
2747 const MgmtEvtUserPasskeyRequest &event = *static_cast<const MgmtEvtUserPasskeyRequest *>(&e);
2748
2749 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2750 if( nullptr == device ) {
2751 WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
2752 dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
2753 event.toString().c_str());
2754 return;
2755 }
2756 DBG_PRINT("BTAdapter:mgmt:UserPasskeyRequest: %s", event.toString().c_str());
2757 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::PASSKEY_EXPECTED);
2758}
2759
2760void BTAdapter::mgmtEvPasskeyNotifyMgmt(const MgmtEvent& e) noexcept {
2761 const MgmtEvtPasskeyNotify &event = *static_cast<const MgmtEvtPasskeyNotify *>(&e);
2762
2763 BTDeviceRef device = findConnectedDevice(event.getAddress(), event.getAddressType());
2764 if( nullptr == device ) {
2765 WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address[%s, %s], %s",
2766 dev_id, event.getAddress().toString().c_str(), to_string(event.getAddressType()).c_str(),
2767 event.toString().c_str());
2768 return;
2769 }
2770 DBG_PRINT("BTAdapter:mgmt:PasskeyNotify: %s", event.toString().c_str());
2771 device->updatePairingState(device, e, HCIStatusCode::SUCCESS, SMPPairingState::PASSKEY_NOTIFY);
2772}
2773
2774void BTAdapter::hciSMPMsgCallback(const BDAddressAndType & addressAndType,
2775 const SMPPDUMsg& msg, const HCIACLData::l2cap_frame& source) noexcept {
2776 BTDeviceRef device = findConnectedDevice(addressAndType.address, addressAndType.type);
2777 if( nullptr == device ) {
2778 WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: Device not tracked: address%s: %s, %s",
2779 dev_id, addressAndType.toString().c_str(),
2780 msg.toString().c_str(), source.toString().c_str());
2781 return;
2782 }
2783 if( device->getConnectionHandle() != source.handle ) {
2784 WORDY_PRINT("BTAdapter:hci:SMP: dev_id %d: ConnHandle mismatch address%s: %s, %s\n -> %s",
2785 dev_id, addressAndType.toString().c_str(),
2786 msg.toString().c_str(), source.toString().c_str(), device->toString().c_str());
2787 return;
2788 }
2789
2790 device->hciSMPMsgCallback(device, msg, source);
2791}
2792
2793void BTAdapter::sendDevicePairingState(const BTDeviceRef& device, const SMPPairingState state, const PairingMode mode, uint64_t timestamp) noexcept
2794{
2795 if( BTRole::Slave == getRole() ) {
2796 // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS
2797 if( SMPPairingState::COMPLETED == state ) {
2798 // Pairing completed
2799 if( PairingMode::PRE_PAIRED != mode ) {
2800 // newly paired -> store keys
2801 SMPKeyBin key = SMPKeyBin::create(*device);
2802 if( key.isValid() ) {
2803 DBG_PRINT("sendDevicePairingState (dev_id %d): created SMPKeyBin: %s", dev_id, key.toString().c_str());
2804 addSMPKeyBin( std::make_shared<SMPKeyBin>(key), true /* write_file */ );
2805 } else {
2806 WARN_PRINT("(dev_id %d): created SMPKeyBin invalid: %s", dev_id, key.toString().c_str());
2807 }
2808 } else {
2809 // pre-paired, refresh PairingData of BTDevice (perhaps a new instance)
2810 const SMPKeyBinRef key = findSMPKeyBin( device->getAddressAndType() );
2811 if( nullptr != key ) {
2812 bool res = device->setSMPKeyBin(*key);
2813 if( !res ) {
2814 WARN_PRINT("(dev_id %d): device::setSMPKeyBin() failed %d, %s", dev_id, res, key->toString().c_str());
2815 }
2816 }
2817 }
2818 } else if( SMPPairingState::FAILED == state ) {
2819 // Pairing failed on this server side
2820 removeSMPKeyBin(device->getAddressAndType(), true /* remove_file */);
2821 }
2822 }
2823 int i=0;
2824 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2825 try {
2826 if( p.match(device) ) {
2827 p.listener->devicePairingState(device, state, mode, timestamp);
2828 }
2829 } catch (std::exception &except) {
2830 ERR_PRINT("BTAdapter::sendDevicePairingState: %d/%zd: %s of %s: Caught exception %s",
2831 i+1, statusListenerList.size(),
2832 p.listener->toString().c_str(), device->toString().c_str(), except.what());
2833 }
2834 i++;
2835 });
2836 if( SMPPairingState::FAILED == state && !device->isConnSecurityAutoEnabled() ) {
2837 // Don't rely on receiving a disconnect
2838 std::thread dc(&BTDevice::disconnect, device.get(), HCIStatusCode::AUTHENTICATION_FAILURE);
2839 dc.detach();
2840 }
2841}
2842
2843void BTAdapter::notifyPairingStageDone(const BTDeviceRef& device, uint64_t timestamp) noexcept {
2844 if( DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_PAIRED == discovery_policy ) {
2845 removeDevicePausingDiscovery(*device);
2846 }
2847 (void)timestamp;
2848}
2849
2850void BTAdapter::sendDeviceReady(BTDeviceRef device, uint64_t timestamp) noexcept {
2851 if( DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY == discovery_policy ) {
2852 removeDevicePausingDiscovery(*device);
2853 }
2854 int i=0;
2855 jau::for_each_fidelity(statusListenerList, [&](StatusListenerPair &p) {
2856 try {
2857 // Only issue if valid && received connected confirmation (HCI) && not have called disconnect yet.
2858 if( device->isValidInstance() && device->getConnected() && device->allowDisconnect ) {
2859 if( p.match(device) ) {
2860 p.listener->deviceReady(device, timestamp);
2861 }
2862 }
2863 } catch (std::exception &except) {
2864 ERR_PRINT("BTAdapter::sendDeviceReady: %d/%zd: %s of %s: Caught exception %s",
2865 i+1, statusListenerList.size(),
2866 p.listener->toString().c_str(), device->toString().c_str(), except.what());
2867 }
2868 i++;
2869 });
2870}
static constexpr const bool _print_device_lists
Definition: BTAdapter.cpp:56
std::string toString() const noexcept
Definition: BTTypes1.hpp:291
bool isCurrentSettingBitSet(const AdapterSetting bit) const noexcept
Definition: BTTypes1.hpp:282
AdapterSetting getCurrentSettingMask() const noexcept
Definition: BTTypes1.hpp:281
BTAdapter status listener for remote BTDevice discovery events: Added, updated and removed; as well a...
Definition: BTAdapter.hpp:127
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:175
std::string toString() const noexcept
Definition: BTTypes0.cpp:186
BTMode getBTMode() const noexcept
Returns the current BTMode of this adapter.
Definition: BTAdapter.hpp:681
HCIStatusCode setName(const std::string &name, const std::string &short_name) noexcept
Sets the name and short-name.
Definition: BTAdapter.cpp:652
jau::nsize_t size_type
Definition: BTAdapter.hpp:355
HCIStatusCode stopAdvertising() noexcept
Ends advertising.
Definition: BTAdapter.cpp:1671
HCIStatusCode setPrivacy(const bool enable) noexcept
Toggle adapter privacy address mode, i.e.
Definition: BTAdapter.cpp:674
void setSMPKeyPath(const std::string path) noexcept
Set the adapter's persistent storage directory for SMPKeyBin files.
Definition: BTAdapter.cpp:739
std::string toString() const noexcept override
Definition: BTAdapter.hpp:1331
HCIStatusCode setDefaultConnParam(const uint16_t conn_interval_min=8, const uint16_t conn_interval_max=40, const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 50)) noexcept
Set default connection parameter of incoming connections for this adapter when in server mode,...
Definition: BTAdapter.cpp:727
BTDeviceRef findSharedDevice(const EUI48 &address, const BDAddressType addressType) noexcept
Returns shared BTDevice if found, otherwise nullptr.
Definition: BTAdapter.cpp:1475
~BTAdapter() noexcept override
Releases this instance.
Definition: BTAdapter.cpp:455
HCIStatusCode reset() noexcept
Reset the adapter.
Definition: BTAdapter.cpp:943
HCIStatusCode startDiscovery(const DBGattServerRef &gattServerData_=nullptr, const DiscoveryPolicy policy=DiscoveryPolicy::PAUSE_CONNECTED_UNTIL_READY, const bool le_scan_active=true, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint8_t filter_policy=0x00, const bool filter_dup=true) noexcept
Starts discovery.
Definition: BTAdapter.cpp:1150
bool isValid() const noexcept
Returns whether the adapter is valid, i.e.
Definition: BTAdapter.hpp:666
size_type removeDiscoveredDevices() noexcept
Discards all discovered devices.
Definition: BTAdapter.cpp:1415
void printStatusListenerList() noexcept
Definition: BTAdapter.cpp:644
HCIStatusCode uploadKeys(SMPKeyBin &bin, const bool write) noexcept
Associate the given SMPKeyBin with the contained remote address, i.e.
Definition: BTAdapter.cpp:749
BTDeviceRef findDiscoveredDevice(const EUI48 &address, const BDAddressType addressType) noexcept
Returns shared BTDevice if found, otherwise nullptr.
Definition: BTAdapter.cpp:1385
HCIStatusCode initialize(const BTMode btMode, const bool powerOn) noexcept
Initialize the adapter with default values, including power-on.
Definition: BTAdapter.cpp:794
void setServerConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept
Sets the given BTSecurityLevel and SMPIOCapability for connecting device when in server (peripheral) ...
Definition: BTAdapter.cpp:735
HCIStatusCode stopDiscovery() noexcept
Ends discovery.
Definition: BTAdapter.cpp:1285
const uint16_t dev_id
Adapter's internal temporary device id.
Definition: BTAdapter.hpp:363
bool removeDeviceFromWhitelist(const BDAddressAndType &addressAndType)
Remove the given device from the adapter's autoconnect whitelist.
Definition: BTAdapter.cpp:1010
bool isPowered() const noexcept
Returns whether the adapter is valid, plugged in and powered.
Definition: BTAdapter.hpp:626
bool isDeviceWhitelisted(const BDAddressAndType &addressAndType) noexcept
Returns true, if the adapter's device is already whitelisted.
Definition: BTAdapter.cpp:986
BTRole getRole() const noexcept
Return the current BTRole of this adapter.
Definition: BTAdapter.hpp:676
size_type removeAllStatusListener() noexcept
Remove all status listener from the list.
Definition: BTAdapter.cpp:1120
HCIStatusCode startAdvertising(const DBGattServerRef &gattServerData_, EInfoReport &eir, EIRDataType adv_mask=EIRDataType::FLAGS|EIRDataType::SERVICE_UUID, EIRDataType scanrsp_mask=EIRDataType::NAME|EIRDataType::CONN_IVAL, const uint16_t adv_interval_min=160, const uint16_t adv_interval_max=480, const AD_PDU_Type adv_type=AD_PDU_Type::ADV_IND, const uint8_t adv_chan_map=0x07, const uint8_t filter_policy=0x00) noexcept
Starts advertising.
Definition: BTAdapter.cpp:1557
bool removeStatusListener(const AdapterStatusListenerRef &l) noexcept
Remove the given listener from the list.
Definition: BTAdapter.cpp:1059
void printDeviceLists() noexcept
Print the internally maintained BTDevice lists to stderr:
Definition: BTAdapter.cpp:618
HCIStatusCode setDefaultLE_PHY(const LE_PHYs Tx, const LE_PHYs Rx) noexcept
Sets default preference of LE_PHYs.
Definition: BTAdapter.cpp:978
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:990
BTAdapter(const BTAdapter::ctor_cookie &cc, BTManagerRef mgmt_, AdapterInfo adapterInfo_) noexcept
Private ctor for private BTAdapter::make_shared() intended for friends.
Definition: BTAdapter.cpp:419
bool addStatusListener(const AdapterStatusListenerRef &l) noexcept
Add the given listener to the list if not already present.
Definition: BTAdapter.cpp:1017
HCIStatusCode setSecureConnections(const bool enable) noexcept
Enable or disable Secure Connections (SC) of the adapter.
Definition: BTAdapter.cpp:710
jau::darray< BTDeviceRef > getDiscoveredDevices() const noexcept
Returns discovered devices from the last discovery.
Definition: BTAdapter.cpp:1440
bool setPowered(const bool power_on) noexcept
Set the power state of the adapter.
Definition: BTAdapter.cpp:660
bool removeDiscoveredDevice(const BDAddressAndType &addressAndType) noexcept
Discards matching discovered devices.
Definition: BTAdapter.cpp:1400
void close() noexcept
Closes this instance, usually being called by destructor or when this adapter is being removed as rec...
Definition: BTAdapter.cpp:472
BTDevice represents one remote Bluetooth device.
Definition: BTDevice.hpp:81
BDAddressAndType addressAndType
Device's unique mac address and type tuple, might be its initially reported (resolvable) random addre...
Definition: BTDevice.hpp:311
static void validateSecParam(const BTSecurityLevel sec_level, const SMPIOCapability io_cap, BTSecurityLevel &res_sec_level, SMPIOCapability &res_io_cap) noexcept
Returns the validated security parameter BTSecurityLevel and SMPIOCapability in the designated refere...
Definition: BTDevice.cpp:1864
HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept
Disconnect the LE or BREDR peer's GATT and HCI connection.
Definition: BTDevice.cpp:2488
std::string toString() const noexcept override
Definition: BTDevice.hpp:470
Collection of 'Extended Advertising Data' (EAD), 'Advertising Data' (AD) or 'Extended Inquiry Respons...
Definition: BTTypes0.hpp:898
std::string toString(const bool includeServices=true) const noexcept
Definition: BTTypes0.cpp:854
jau::EUI48 const & getAddress() const noexcept
Definition: BTTypes0.hpp:1118
void setSource(Source s, bool ext) noexcept
Definition: BTTypes0.hpp:982
void setFlags(GAPFlags f) noexcept
Definition: BTTypes0.hpp:991
void setAddressType(BDAddressType at) noexcept
Definition: BTTypes0.cpp:785
uint64_t getTimestamp() const noexcept
Definition: BTTypes0.hpp:1109
bool addService(const std::shared_ptr< const jau::uuid_t > &uuid) noexcept
Definition: BTTypes0.cpp:834
@ EIR
Extended Inquiry Response (EIR)
void setConnInterval(const uint16_t min, const uint16_t max) noexcept
Set slave connection interval range.
Definition: BTTypes0.hpp:1014
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:1003
void setTimestamp(uint64_t ts) noexcept
Definition: BTTypes0.hpp:983
BDAddressType getAddressType() const noexcept
Definition: BTTypes0.hpp:1117
void setAddress(jau::EUI48 const &a) noexcept
Definition: BTTypes0.hpp:987
const jau::fraction_i64 HCI_COMMAND_POLL_PERIOD
Poll period for certain HCI commands actively waiting for clearance, defaults to 125ms.
Definition: HCIHandler.hpp:119
const jau::fraction_i64 HCI_COMMAND_COMPLETE_REPLY_TIMEOUT
Timeout for HCI command complete replies, defaults to 10s.
Definition: HCIHandler.hpp:106
A thread safe singleton handler of the HCI control channel to one controller (BT adapter)
Definition: HCIHandler.hpp:179
bool use_ext_adv() const noexcept
Use extended advertising if LE_Features::LE_Ext_Adv is set (Bluetooth 5.0).
Definition: HCIHandler.hpp:429
void setBTMode(const BTMode mode) noexcept
Definition: HCIHandler.hpp:402
bool use_ext_conn() const noexcept
Use extended connection if HCI_LE_Extended_Create_Connection is supported (Bluetooth 5....
Definition: HCIHandler.hpp:416
bool isOpen() const noexcept
Returns true if this mgmt instance is open, connected and hence valid, otherwise false.
Definition: HCIHandler.hpp:405
HCIStatusCode le_clear_resolv_list() noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.40 LE Clear Resolving List command.
void close() noexcept
Definition: HCIHandler.cpp:949
HCIStatusCode le_enable_adv(const bool enable) noexcept
Enables or disabled advertising.
HCIStatusCode resetAdapter(const PostShutdownFunc &user_post_shutdown)
Complete adapter reset.
void clearAllCallbacks() noexcept
Removes all MgmtEventCallbacks from all MgmtEvent::Opcode lists and all SMPSecurityReqCallbacks.
LE_Features le_get_local_features() noexcept
Return previously fetched LE_Features for the controller via initSupCommands() via resetAllStates()
Definition: HCIHandler.hpp:532
bool resetAllStates(const bool powered_on) noexcept
Reset all internal states, i.e.
ScanType getCurrentScanType() const noexcept
Definition: HCIHandler.hpp:433
HCIStatusCode getLocalVersion(HCILocalVersion &version) noexcept
HCIStatusCode le_set_addr_resolv_enable(const bool enable) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.44 LE Set Address Resolution Enable command.
const HCIEnv & env
Definition: HCIHandler.hpp:185
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:410
bool close() noexcept override
Closing the L2CAP socket, see specializations.
Definition: L2CAPComm.hpp:333
@ SET_SECURE_CONN
LE Secure Connections: 0x01 enables SC mixed, 0x02 enables SC only mode; Core Spec >= 4....
uint16_t opcode, uint16_t dev-id, uint16_t param_size
Definition: MgmtTypes.hpp:1402
std::string toString() const noexcept override
Definition: MgmtTypes.hpp:1543
mgmt_addr_info { EUI48, uint8_t type }, uint8_t status
Definition: MgmtTypes.hpp:2097
mgmt_addr_info { EUI48, uint8_t type }, uint8_t status
Definition: MgmtTypes.hpp:1942
mgmt_addr_info { EUI48, uint8_t type }, uint32_t flags, uint16_t eir_len; uint8_t *eir
Definition: MgmtTypes.hpp:1813
mgmt_addr_info { EUI48, uint8_t type }, uint8_t reason
Definition: MgmtTypes.hpp:1860
mgmt_addr_info { EUI48, uint8_t type }, int8_t rssi, uint32_t flags, uint16_t eir_len; uint8_t *eir
Definition: MgmtTypes.hpp:2129
const EInfoReport * getEIR() const noexcept
Returns reference to the immutable EInfoReport, assuming creation occurred via HCIHandler.
Definition: MgmtTypes.hpp:2162
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:2234
mgmt_addr_info { EUI48, uint8_t type }, HCIStatusCode status (1 Octet) uint8_t enc_enabled (1 Octet)
Definition: MgmtTypes.hpp:2815
mgmt_addr_info { EUI48, uint8_t type }, HCIStatusCode status (1 Octet)
Definition: MgmtTypes.hpp:2858
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.24 LE Enable Encryption command.
Definition: MgmtTypes.hpp:2721
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.25 LE Long Term Key Request Reply command.
Definition: MgmtTypes.hpp:2633
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.26 LE Long Term Key Request Negative Reply command.
Definition: MgmtTypes.hpp:2676
BT Core Spec v5.2: Vol 4, Part E HCI: 7.7.65.5 LE Long Term Key Request event.
Definition: MgmtTypes.hpp:2550
mgmt_addr_info { EUI48, uint8_t type }, uint8_t Tx (8 Octets) uint8_t Rx (8 Octets)
Definition: MgmtTypes.hpp:2497
mgmt_addr_info { EUI48, uint8_t type }, uint64_t features (8 Octets)
Definition: MgmtTypes.hpp:2455
uint8_t name[MGMT_MAX_NAME_LENGTH]; uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
Definition: MgmtTypes.hpp:1718
uint8_t store_hint, EUI48 random_address; MgmtIdentityResolvingKey key
Definition: MgmtTypes.hpp:2249
uint8_t store_hint, MgmtLinkKey key
Definition: MgmtTypes.hpp:1751
uint8_t store_hint, MgmtLongTermKey key
Definition: MgmtTypes.hpp:1781
mgmt_addr_info { EUI48, uint8_t type }, MgmtStatus (1 octet)
Definition: MgmtTypes.hpp:2401
mgmt_addr_info { EUI48, uint8_t type }, uint32_t passkey uint8_t entered
Definition: MgmtTypes.hpp:2066
mgmt_addr_info { EUI48, uint8_t type }, uint8_t secure
Definition: MgmtTypes.hpp:1985
mgmt_addr_info { EUI48, uint8_t type }, uint8_t confirm_hint uint32_t value
Definition: MgmtTypes.hpp:2020
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:2050
Storage for SMP keys including required connection parameter per local adapter and remote device.
Definition: SMPKeyBin.hpp:79
static std::vector< SMPKeyBin > readAllForLocalAdapter(const BDAddressAndType &localAddress, const std::string &dname, const bool verbose_)
Definition: SMPKeyBin.cpp:137
std::string toString() const noexcept
Definition: SMPKeyBin.cpp:148
constexpr bool isValid() const noexcept
Returns true if.
Definition: SMPKeyBin.hpp:364
static SMPKeyBin create(const BTDevice &device)
Create a new SMPKeyBin instance based upon given BTDevice's BTSecurityLevel, SMPPairingState,...
Definition: SMPKeyBin.cpp:64
Handles the Security Manager Protocol (SMP) using Protocol Data Unit (PDU) encoded messages over L2CA...
Definition: SMPTypes.hpp:842
Vol 3, Part H: 3.5.5 Pairing Failed message.
Definition: SMPTypes.hpp:1403
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: cow_darray.hpp:752
bool(* equal_comparator)(const value_type &a, const value_type &b)
Generic value_type equal comparator to be user defined for e.g.
Definition: cow_darray.hpp:989
constexpr_atomic size_type size() const noexcept
Like std::vector::size().
Definition: cow_darray.hpp:717
constexpr iterator begin()
Returns an jau::cow_rw_iterator to the first element of this CoW storage.
Definition: cow_darray.hpp:660
constexpr iterator end() noexcept
Definition: darray.hpp:707
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:763
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: darray.hpp:1124
constexpr iterator begin() noexcept
Definition: darray.hpp:701
constexpr iterator erase(const_iterator pos)
Like std::vector::erase(), removes the elements at pos.
Definition: darray.hpp:940
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: darray.hpp:904
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:94
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...
constexpr int_type to_ms() const noexcept
Convenient shortcut to to_num_of(1_ms)
This class provides a RAII-style Sequentially Consistent (SC) data race free (DRF) critical block.
Service runner, a reusable dedicated thread performing custom user services.
bool stop() noexcept
Stops this service, if running.
bool shall_stop2(int dummy) noexcept
Helper function to easy FunctionDef usage w/o creating a lambda alike capture with same semantics as ...
A simple timer for timeout and interval applications, using one dedicated service_runner thread per i...
bool stop() noexcept
Stop timer, see service_runner::stop()
bool shall_stop() const noexcept
Returns true if timer shall stop.
static const uint8_t filter_policy
static const uint16_t le_scan_interval
static const uint16_t le_scan_window
static const bool filter_dup
static bool le_scan_active
static BTMode btMode
static const uint16_t adv_interval_max
static jau::sc_atomic_bool sync_data
static const uint8_t adv_chan_map
static bool startAdvertising(BTAdapter *a, std::string msg)
static const uint16_t adv_interval_min
static const AD_PDU_Type adv_type
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
Definition: debug.hpp:151
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:68
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
Definition: debug.hpp:101
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:112
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
Definition: debug.hpp:123
#define IRQ_PRINT(...)
Use for unconditional interruption messages, prefix '[elapsed_time] Interrupted @ FILE:LINE FUNC: '.
Definition: debug.hpp:115
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
#define PRAGMA_DISABLE_WARNING_PUSH
Definition: cpp_pragma.hpp:76
#define PRAGMA_DISABLE_WARNING_STRINGOP_OVERFLOW
Definition: cpp_pragma.hpp:86
#define PRAGMA_DISABLE_WARNING_POP
Definition: cpp_pragma.hpp:77
HCIStatusCode to_HCIStatusCode(const MgmtStatus mstatus) noexcept
Definition: MgmtTypes.cpp:648
constexpr const bool CONSIDER_HCI_CMD_FOR_SMP_STATE
Definition: DBTConst.hpp:62
constexpr const jau::fraction_i64 THREAD_SHUTDOWN_TIMEOUT_MS
Maximum time in fractions of seconds to wait for a thread shutdown.
Definition: DBTConst.hpp:73
SMPPairingState
SMP Pairing Process state definition.
Definition: SMPTypes.hpp:107
constexpr const bool SCAN_DISABLED_POST_CONNECT
Definition: DBTConst.hpp:64
constexpr const jau::fraction_i64 L2CAP_CLIENT_CONNECT_TIMEOUT_MS
Maximum time in fractions of seconds to wait for L2CAP client connection when adapter is in server mo...
Definition: DBTConst.hpp:83
constexpr const jau::fraction_i64 SMP_NEXT_EVENT_TIMEOUT_MS
Maximum time in fractions of seconds to wait for the next SMP event.
Definition: DBTConst.hpp:78
constexpr bool hasSMPIOCapabilityAnyIO(const SMPIOCapability ioc) noexcept
Definition: SMPTypes.hpp:233
SMPIOCapability
Vol 3, Part H, 2.3.2 IO capabilities.
Definition: SMPTypes.hpp:209
constexpr const jau::nsize_t MAX_BACKGROUND_DISCOVERY_RETRY
Maximum number of enabling discovery in background in case of failure.
Definition: DBTConst.hpp:88
constexpr const bool USE_LINUX_BT_SECURITY
Definition: DBTConst.hpp:60
@ PASSKEY_NOTIFY
Phase 2: Authentication (MITM) PASSKEY (produced by this responder adapter, acting as peripheral GATT...
@ COMPLETED
Phase 3: Key & value distribution completed by responding (slave) device sending SMPIdentInfoMsg (#1)...
@ NUMERIC_COMPARE_EXPECTED
Phase 2: Authentication (MITM) Numeric Comparison Reply expected now, see PairingMode::NUMERIC_COMPAR...
@ NONE
No pairing in process.
@ PASSKEY_EXPECTED
Phase 2: Authentication (MITM) PASSKEY expected now, see PairingMode::PASSKEY_ENTRY_ini.
@ FAILED
Pairing failed.
@ KEY_DISTRIBUTION
Phase 3: Key & value distribution started after SMPPairConfirmMsg or SMPPairPubKeyMsg (LE Secure Conn...
@ NONE
Denoting no or invalid link key type.
@ NONE
Denoting no or invalid long term key type.
@ UNSET
Denoting unset value, i.e.
@ NO_INPUT_NO_OUTPUT
No input not output, value 3.
std::shared_ptr< BTManager > BTManagerRef
Definition: BTAdapter.hpp:61
BTMode
Bluetooth adapter operating mode.
Definition: BTTypes0.hpp:112
std::shared_ptr< AdapterStatusListener > AdapterStatusListenerRef
Definition: BTAdapter.hpp:297
HCIWhitelistConnectType
HCI Whitelist connection type.
Definition: BTTypes0.hpp:471
DiscoveryPolicy
Discovery policy defines the BTAdapter discovery mode after connecting a remote BTDevice:
Definition: BTAdapter.hpp:82
constexpr ScanType changeScanType(const ScanType current, const ScanType changeType, const bool changeEnable) noexcept
Definition: BTTypes0.hpp:377
std::shared_ptr< BTDevice > BTDeviceRef
Definition: BTDevice.hpp:1347
std::string to_string(const DiscoveryPolicy v) noexcept
Definition: BTAdapter.cpp:58
LE_PHYs
LE Transport PHY bit values.
Definition: BTTypes0.hpp:231
ScanType
Meta ScanType as derived from BTMode, with defined value mask consisting of BDAddressType bits.
Definition: BTTypes0.hpp:350
constexpr AdapterSetting getAdapterSettingMaskDiff(const AdapterSetting setting_a, const AdapterSetting setting_b) noexcept
Definition: BTTypes1.hpp:183
AdapterSetting
Adapter Setting Bits.
Definition: BTTypes1.hpp:144
constexpr bool isAdapterSettingBitSet(const AdapterSetting mask, const AdapterSetting bit) noexcept
Definition: BTTypes1.hpp:185
BDAddressType
BT Core Spec v5.2: Vol 3, Part C Generic Access Profile (GAP): 15.1.1.1 Public Bluetooth address.
Definition: BTAddress.hpp:60
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:267
AD_PDU_Type
LE Advertising (AD) Protocol Data Unit (PDU) Types.
Definition: BTTypes0.hpp:397
PairingMode
Bluetooth secure pairing mode.
Definition: BTTypes0.hpp:317
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:138
constexpr bool is_set(const LE_Features mask, const LE_Features bit) noexcept
Definition: BTTypes0.hpp:219
EIRDataType
Bit mask of 'Extended Inquiry Response' (EIR) data fields, indicating a set of related data.
Definition: BTTypes0.hpp:838
@ NONE
Zero mode, neither DUAL, BREDR nor LE.
@ LE_Encryption
LE_Encryption.
@ PAUSE_CONNECTED_UNTIL_PAIRED
Pause discovery until all connected BTDevice are optionally SMP paired (~120ms) without GATT service ...
@ PAUSE_CONNECTED_UNTIL_READY
Pause discovery until all connected BTDevice reach readiness inclusive optional SMP pairing (~120ms) ...
@ ALWAYS_ON
Always keep discovery enabled, i.e.
@ AUTO_OFF
Turn off discovery when connected and leave discovery disabled, if turned off by host system.
@ BDADDR_LE_RANDOM
Bluetooth LE random address, see BLERandomAddressType.
@ BDADDR_UNDEFINED
Undefined.
@ Master
Master or central role, discovering remote devices and initiating connection.
@ Slave
Slave or peripheral role, advertising and waiting for connections to accept.
@ RESOLVABLE_OR_RANDOM
Controller Resolved Private Address or Random Address.
@ PUBLIC
Public Device Address.
@ RESOLVABLE_OR_PUBLIC
Controller Resolved Private Address or Public Address.
@ ENC_AUTH
Encryption and authentication (MITM).
@ NONE
No encryption and no authentication.
@ PRE_PAIRED
Reusing encryption keys from previous pairing.
std::shared_ptr< DBGattService > DBGattServiceRef
std::shared_ptr< DBGattServer > DBGattServerRef
void sync() noexcept
Synchronizes filesystems, i.e.
Definition: file_util.cpp:1932
fraction_timespec getMonotonicTime() noexcept
Returns current monotonic time since Unix Epoch 00:00:00 UTC on 1970-01-01.
Definition: basic_types.cpp:52
jau::function< R(A...)> bind_member(C1 *base, R(C0::*mfunc)(A...)) noexcept
Bind given class instance and non-void member function to an anonymous function using func_member_tar...
constexpr uint32_t number(const iostate rhs) noexcept
Definition: byte_stream.hpp:72
@ timeout
Input or output operation failed due to timeout.
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
void PLAIN_PRINT(const bool printPrefix, const char *format,...) noexcept
Use for unconditional plain messages, prefix '[elapsed_time] ' if printPrefix == true.
Definition: debug.cpp:258
void INFO_PRINT(const char *format,...) noexcept
Use for unconditional informal messages, prefix '[elapsed_time] Info: '.
Definition: debug.cpp:248
std::cv_status wait_until(std::condition_variable &cv, std::unique_lock< std::mutex > &lock, const fraction_timespec &absolute_time, const bool monotonic=true) noexcept
wait_until causes the current thread to block until the condition variable is notified,...
void print_backtrace(const bool skip_anon_frames, const jau::snsize_t max_frames=-1, const jau::snsize_t skip_frames=2) noexcept
Prints the de-mangled backtrace string separated by newline excluding this function to stderr,...
Definition: debug.cpp:173
uint64_t getCurrentMilliseconds() noexcept
Returns current monotonic time in milliseconds.
Definition: basic_types.cpp:64
bool sleep_for(const fraction_timespec &relative_time, const bool monotonic=true, const bool ignore_irq=true) noexcept
sleep_for causes the current thread to block until a specific amount of time has passed.
Representing ACL Datas' L2CAP Frame.
Definition: HCITypes.hpp:977
@ START_NON_AUTOFLUSH_HOST
0b00: Start of a non-automatically-flushable PDU from Host to Controller.
std::string toString() noexcept
Definition: HCITypes.cpp:389
Used for MgmtLoadIdentityResolvingKeyCmd and MgmtEvtNewIdentityResolvingKey.
Definition: MgmtTypes.hpp:289
Used for MgmtLoadLinkKeyCmd and MgmtEvtNewLinkKey.
Definition: MgmtTypes.hpp:345
MgmtLinkKeyType key_type
Definition: MgmtTypes.hpp:345
Used for MgmtLoadLongTermKeyCmd and MgmtEvtNewLongTermKey.
Definition: MgmtTypes.hpp:264
BDAddressType address_type
0 reserved, 1 le public, 2 le static random address.
Definition: MgmtTypes.hpp:264
uint8_t enc_size
Encryption Size.
Definition: MgmtTypes.hpp:264
MgmtLTKType key_type
Describing type of key, i.e.
Definition: MgmtTypes.hpp:264
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: eui48.hpp:324
Timespec structure using int64_t for its components in analogy to struct timespec_t on 64-bit platfor...
A 128-bit packed uint8_t data array.
Definition: int_types.hpp:114
void clear() noexcept
Definition: int_types.hpp:114
uint8_t data[16]
Definition: int_types.hpp:114