Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
BTDevice.cpp
Go to the documentation of this file.
1/*
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2020 Gothel Software e.K.
4 * Copyright (c) 2020 ZAFENA AB
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining
7 * a copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sublicense, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be
15 * included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26#include <cstring>
27#include <string>
28#include <memory>
29#include <cstdint>
30#include <cstdio>
31
32#include <algorithm>
33
34// #define VERBOSE_ON 1
35#include <jau/environment.hpp>
36#include <jau/debug.hpp>
37
38#include "BTAdapter.hpp"
39#include "BTDevice.hpp"
40#include "BTManager.hpp"
41#include "BTGattService.hpp"
42
43using namespace direct_bt;
44using namespace jau::fractions_i64_literals;
45
46BTDevice::BTDevice(const ctor_cookie& cc, BTAdapter & a, EInfoReport const & r)
47: adapter(a), btRole(!a.getRole()),
48 l2cap_att( std::make_unique<L2CAPClient>(adapter.dev_id, adapter.getAddressAndType(), L2CAP_PSM::UNDEFINED, L2CAP_CID::ATT) ), // copy elision, not copy-ctor
49 ts_last_discovery(r.getTimestamp()),
50 ts_last_update(ts_last_discovery),
51 name(),
52 eir( std::make_shared<EInfoReport>() ),
53 eir_ind( std::make_shared<EInfoReport>() ),
54 eir_scan_rsp( std::make_shared<EInfoReport>() ),
55 hciConnHandle(0),
56 le_features(LE_Features::NONE),
57 le_phy_tx(LE_PHYs::NONE),
58 le_phy_rx(LE_PHYs::NONE),
59 isConnected(false),
60 allowDisconnect(false),
61 supervision_timeout(0),
62 smp_events(0),
63 pairing_data { },
64 ts_creation(ts_last_discovery),
65 visibleAddressAndType{r.getAddress(), r.getAddressType()},
66 addressAndType(visibleAddressAndType)
67{
68 (void)cc;
69
70 clearSMPStates(false /* connected */);
71
72 if( !r.isSet(EIRDataType::BDADDR) ) {
73 throw jau::IllegalArgumentException("Address not set: "+r.toString(), E_FILE_LINE);
74 }
76 throw jau::IllegalArgumentException("AddressType not set: "+r.toString(), E_FILE_LINE);
77 }
78 update(r);
79
82 if( BLERandomAddressType::UNDEFINED == leRandomAddressType ) {
83 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
84 throw jau::IllegalArgumentException("BDADDR_LE_RANDOM: Invalid BLERandomAddressType "+to_string(leRandomAddressType)+": "+toString(), E_FILE_LINE);
85 }
86 } else {
87 if( BLERandomAddressType::UNDEFINED != leRandomAddressType ) {
88 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
89 throw jau::IllegalArgumentException("Not BDADDR_LE_RANDOM: Invalid given native BLERandomAddressType "+to_string(leRandomAddressType)+": "+toString(), E_FILE_LINE);
90 }
91 }
92}
93
95 DBG_PRINT("BTDevice::dtor: ... %p %s", this, addressAndType.toString().c_str());
96 adapter.removeAllStatusListener(*this);
97 DBG_PRINT("BTDevice::dtor: XXX %p %s", this, addressAndType.toString().c_str());
98}
99
100std::shared_ptr<BTDevice> BTDevice::getSharedInstance() const noexcept {
101 return adapter.getSharedDevice(*this);
102}
103
105 switch( btRole ) {
106 case BTRole::Master: return GATTRole::Server;
107 case BTRole::Slave: return GATTRole::Client;
108 default: return GATTRole::None;
109 }
110}
111
112std::string const BTDevice::getName() const noexcept {
113 jau::sc_atomic_critical sync(sync_data);
114 std::string res = name;
115 return res;
116}
117
119 const std::lock_guard<std::mutex> lock(mtx_eir); // RAII-style acquire and relinquish via destructor
120 return eir;
121}
122
124 const std::lock_guard<std::mutex> lock(mtx_eir); // RAII-style acquire and relinquish via destructor
125 return eir_ind;
126}
127
129 const std::lock_guard<std::mutex> lock(mtx_eir); // RAII-style acquire and relinquish via destructor
130 return eir_scan_rsp;
131}
132
133std::string BTDevice::toString(bool includeDiscoveredServices) const noexcept {
134 const uint64_t t0 = jau::getCurrentMilliseconds();
136 std::string resaddr_s = visibleAddressAndType != addressAndType ? ", visible "+visibleAddressAndType.toString() : "";
137 std::shared_ptr<const EInfoReport> eir_ = eir;
138 std::string eir_s = BTRole::Slave == getRole() ? ", "+eir_->toString( includeDiscoveredServices ) : "";
139 std::string out("Device["+to_string(getRole())+", "+addressAndType.toString()+resaddr_s+", name['"+name+
140 "'], age[total "+std::to_string(t0-ts_creation)+", ldisc "+std::to_string(t0-ts_last_discovery)+", lup "+std::to_string(t0-ts_last_update)+
141 "]ms, connected["+std::to_string(allowDisconnect)+"/"+std::to_string(isConnected)+", handle "+jau::to_hexstring(hciConnHandle)+
142 ", phy[Tx "+direct_bt::to_string(le_phy_tx)+", Rx "+direct_bt::to_string(le_phy_rx)+
143 "], sec[enc "+std::to_string(pairing_data.encryption_enabled)+", lvl "+to_string(pairing_data.sec_level_conn)+", io "+to_string(pairing_data.io_cap_conn)+
144 ", auto "+to_string(pairing_data.io_cap_auto)+", pairing "+to_string(pairing_data.mode)+", state "+to_string(pairing_data.state)+
145 ", sc "+std::to_string(pairing_data.use_sc)+"]], rssi "+std::to_string(getRSSI())+
146 ", tx-power "+std::to_string(tx_power)+eir_s+
147 ", "+javaObjectToString()+"]");
148 return out;
149}
150
151void BTDevice::clearData() noexcept {
152 // already done == performed on previous notidyDisconnected() !
153 if( getConnected() ) {
154 ERR_PRINT("Device still connected: %s", toString().c_str());
155 return;
156 }
157 btRole = !adapter.getRole(); // update role
158 // l2cap_att->close(); // already done
159 // ts_last_discovery = 0; // leave
160 // ts_last_update = 0; // leave
161 name.clear();
162 rssi = 127; // The core spec defines 127 as the "not available" value
163 tx_power = 127; // The core spec defines 127 as the "not available" value
164 {
165 const std::lock_guard<std::mutex> lock(mtx_eir); // RAII-style acquire and relinquish via destructor
166 eir = std::make_shared<EInfoReport>();
167 eir_ind = std::make_shared<EInfoReport>();
168 eir_scan_rsp = std::make_shared<EInfoReport>();
169 }
170 // hciConnHandle = 0; // already done
171 le_features = LE_Features::NONE;
172 le_phy_tx = LE_PHYs::NONE;
173 le_phy_rx = LE_PHYs::NONE;
174 // isConnected = false; // already done
175 // allowDisconnect = false; // already done
176 // supervision_timeout = 0; // already done
177 // clearSMPStates( false /* connected */); // already done
178}
179
180bool BTDevice::updateIdentityAddress(BDAddressAndType const & identityAddress, bool sendEvent) noexcept {
181 if( BDAddressType::BDADDR_LE_PUBLIC != addressAndType.type && addressAndType != identityAddress ) {
182 addressAndType = identityAddress;
183 adapter.hci.setResolvHCIConnectionAddr(visibleAddressAndType, addressAndType);
184 if( sendEvent ) {
185 std::shared_ptr<BTDevice> sharedInstance = getSharedInstance();
186 if( nullptr == sharedInstance ) {
187 ERR_PRINT("Device unknown to adapter and not tracked: %s", toString().c_str());
188 } else {
189 adapter.sendDeviceUpdated("Address Resolution", sharedInstance, jau::getCurrentMilliseconds(), EIRDataType::BDADDR | EIRDataType::BDADDR_TYPE);
190 }
191 }
192 return true;
193 } else {
194 return false;
195 }
196}
197
198bool BTDevice::updateVisibleAddress(BDAddressAndType const & randomPrivateAddress) noexcept {
199 if( visibleAddressAndType != randomPrivateAddress ) {
200 visibleAddressAndType = randomPrivateAddress;
201 return true;
202 } else {
203 return false;
204 }
205}
206
207EIRDataType BTDevice::update(EInfoReport const & data) noexcept {
208 const std::lock_guard<std::mutex> lock(mtx_eir); // RAII-style acquire and relinquish via destructor
209
210 btRole = !adapter.getRole(); // update role
211
212 // Update eir CoW style
213 std::shared_ptr<EInfoReport> eir_new( std::make_shared<EInfoReport>( *eir ) );
214 EIRDataType res0 = eir_new->set(data);
215 if( EIRDataType::NONE != res0 ) {
216 eir = eir_new;
217 }
218 if( EInfoReport::Source::AD_IND == data.getSource() ) {
219 eir_ind = std::make_shared<EInfoReport>( data );
220 } else if( EInfoReport::Source::AD_SCAN_RSP == data.getSource() ) {
221 eir_scan_rsp = std::make_shared<EInfoReport>( data );
222 }
223
224 ts_last_update = data.getTimestamp();
225 if( data.isSet(EIRDataType::BDADDR) ) {
226 if( data.getAddress() != this->addressAndType.address ) {
227 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
228 WARN_PRINT("BDADDR update not supported: %s for %s", data.toString().c_str(), this->toString().c_str());
229 }
230 }
231 if( data.isSet(EIRDataType::BDADDR_TYPE) ) {
232 if( data.getAddressType() != this->addressAndType.type ) {
233 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
234 WARN_PRINT("BDADDR_TYPE update not supported: %s for %s", data.toString().c_str(), this->toString().c_str());
235 }
236 }
237 if( data.isSet(EIRDataType::NAME) ) {
238 if( data.getName().length() > 0 ) {
239 name = data.getName();
240 }
241 }
242 if( data.isSet(EIRDataType::NAME_SHORT) ) {
243 if( 0 == name.length() ) {
244 name = data.getShortName();
245 }
246 }
247 if( data.isSet(EIRDataType::RSSI) ) {
248 if( rssi != data.getRSSI() ) {
249 rssi = data.getRSSI();
250 }
251 }
252 if( data.isSet(EIRDataType::TX_POWER) ) {
253 if( tx_power != data.getTxPower() ) {
254 tx_power = data.getTxPower();
255 }
256 }
257 return res0;
258}
259
260EIRDataType BTDevice::update(GattGenericAccessSvc const &data, const uint64_t timestamp) noexcept {
261 const std::lock_guard<std::mutex> lock(mtx_eir); // RAII-style acquire and relinquish via destructor
262
263 // Update eir CoW style
264 std::shared_ptr<EInfoReport> eir_new( std::make_shared<EInfoReport>( *eir ) );
265 bool mod = false;
266
268 ts_last_update = timestamp;
269 if( 0 == name.length() && data.deviceName.length() > 0 ) {
270 name = data.deviceName;
271 eir_new->setName( name );
273 mod = true;
274 }
275 if( eir_new->getAppearance() != data.appearance && AppearanceCat::UNKNOWN != data.appearance) {
276 eir_new->setAppearance( data.appearance );
278 mod = true;
279 }
280 if( mod ) {
281 eir = eir_new;
282 }
283 return res;
284}
285
287 return adapter.addStatusListener(*this, l);
288}
289
291 return adapter.removeStatusListener(l);
292}
293
294std::shared_ptr<ConnectionInfo> BTDevice::getConnectionInfo() noexcept {
295 const BTManagerRef& mgmt = adapter.getManager();
296 std::shared_ptr<ConnectionInfo> connInfo = mgmt->getConnectionInfo(adapter.dev_id, addressAndType);
297 if( nullptr != connInfo ) {
298 EIRDataType updateMask = EIRDataType::NONE;
299 if( rssi != connInfo->getRSSI() ) {
300 rssi = connInfo->getRSSI();
301 set(updateMask, EIRDataType::RSSI);
302 }
303 if( tx_power != connInfo->getTxPower() ) {
304 tx_power = connInfo->getTxPower();
305 set(updateMask, EIRDataType::TX_POWER);
306 }
307 if( EIRDataType::NONE != updateMask ) {
308 std::shared_ptr<BTDevice> sharedInstance = getSharedInstance();
309 if( nullptr == sharedInstance ) {
310 ERR_PRINT("Device unknown to adapter and not tracked: %s", toString().c_str());
311 } else {
312 adapter.sendDeviceUpdated("getConnectionInfo", sharedInstance, jau::getCurrentMilliseconds(), updateMask);
313 }
314 }
315 }
316 return connInfo;
317}
318
319// #define TEST_NOENC 1
320
322 const uint16_t conn_interval_min, const uint16_t conn_interval_max,
323 const uint16_t conn_latency, const uint16_t conn_supervision_timeout) noexcept
324{
325 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_connect); // RAII-style acquire and relinquish via destructor
326 if( !adapter.isPowered() ) { // isValid() && hci.isOpen() && POWERED
327 WARN_PRINT("Adapter not powered: %s, %s", adapter.toString().c_str(), toString().c_str());
329 }
330 HCILEOwnAddressType hci_own_mac_type = adapter.visibleMACType;
331 HCILEPeerAddressType hci_peer_mac_type;
332
333 switch( addressAndType.type ) {
335 hci_peer_mac_type = HCILEPeerAddressType::PUBLIC;
336 break;
338 // TODO: Shall we support 'resolving list' and/or LE Set Privacy Mode (HCI) ?
339 const BLERandomAddressType leRandomAddressType = addressAndType.getBLERandomAddressType();
340 switch( leRandomAddressType ) {
342 // TODO: OK to not be able to resolve?
343 hci_peer_mac_type = HCILEPeerAddressType::RANDOM;
344 break;
346 // TODO: Shall we resolve this address using IRK to set HCILEPeerAddressType::PUBLIC_IDENTITY ?
347 hci_peer_mac_type = HCILEPeerAddressType::RANDOM;
348 break;
350 // Static random address is not changing between power-cycles.
351 hci_peer_mac_type = HCILEPeerAddressType::RANDOM;
352 break;
353 default: {
354 ERR_PRINT("Can't connectLE to LE Random address type '%s': %s",
355 to_string(leRandomAddressType).c_str(), toString().c_str());
357 }
358 }
359 } break;
360 default: {
361 ERR_PRINT("Can't connectLE to address type '%s': %s", to_string(addressAndType.type).c_str(), toString().c_str());
363 }
364 }
365
366 if( isConnected ) {
367 ERR_PRINT("Already connected: %s", toString().c_str());
369 }
370
371 HCIHandler &hci = adapter.getHCI();
372 if( !hci.isOpen() ) {
373 ERR_PRINT("HCI closed: %s", toString().c_str());
375 }
376
377 HCIStatusCode statusConnect; // HCI le_create_conn result
378 const SMPIOCapability smp_auto_io_cap = pairing_data.io_cap_auto; // cache against clearSMPState
379 const bool smp_auto = SMPIOCapability::UNSET != smp_auto_io_cap; // logical cached state
380 bool smp_auto_done = !smp_auto;
381 int smp_auto_count = 0;
382 BTSecurityLevel sec_level = pairing_data.sec_level_user; // iterate down
383 SMPIOCapability io_cap = pairing_data.io_cap_user; // iterate down
385
386 do {
387 smp_auto_count++;
388
389 if( !smp_auto_done ) {
390 BTSecurityLevel sec_level_pre = sec_level;
391 SMPIOCapability io_cap_pre = io_cap;
392 if( BTSecurityLevel::UNSET == sec_level && SMPIOCapability::NO_INPUT_NO_OUTPUT != smp_auto_io_cap ) {
394 io_cap = smp_auto_io_cap;
395 } else if( BTSecurityLevel::ENC_AUTH_FIPS == sec_level && SMPIOCapability::NO_INPUT_NO_OUTPUT != smp_auto_io_cap ) {
396 sec_level = BTSecurityLevel::ENC_AUTH;
397 io_cap = smp_auto_io_cap;
398 } else if( BTSecurityLevel::ENC_AUTH == sec_level || BTSecurityLevel::UNSET == sec_level ) {
399#if TEST_NOENC
400 sec_level = BTSecurityLevel::NONE;
402 smp_auto_done = true;
403#else
404 sec_level = BTSecurityLevel::ENC_ONLY;
406#endif
407 } else if( BTSecurityLevel::ENC_ONLY == sec_level ) {
408 sec_level = BTSecurityLevel::NONE;
410 smp_auto_done = true;
411 }
412 pairing_data.io_cap_auto = smp_auto_io_cap; // reload against clearSMPState
413 pairing_data.sec_level_user = sec_level;
414 pairing_data.io_cap_user = io_cap;
415 DBG_PRINT("BTDevice::connectLE: SEC AUTO.%d.1: lvl %s -> %s, io %s -> %s, %s", smp_auto_count,
416 to_string(sec_level_pre).c_str(), to_string(sec_level).c_str(),
417 to_string(io_cap_pre).c_str(), to_string(io_cap).c_str(),
418 toString().c_str());
419 }
420
421 {
423 if( !adapter.lockConnect(*this, true /* wait */, pairing_data.io_cap_user) ) {
424 ERR_PRINT("adapter::lockConnect() failed: %s", toString().c_str());
426 }
427 }
428 statusConnect = hci.le_create_conn(addressAndType.address,
429 hci_peer_mac_type, hci_own_mac_type,
430 le_scan_interval, le_scan_window, conn_interval_min, conn_interval_max,
431 conn_latency, conn_supervision_timeout);
432 supervision_timeout = 10 * conn_supervision_timeout; // [ms] = 10 * [ms/10]
433 allowDisconnect = true;
434 if( HCIStatusCode::COMMAND_DISALLOWED == statusConnect ) {
435 WARN_PRINT("Could not yet create connection: status 0x%2.2X (%s), errno %d, hci-atype[peer %s, own %s] %s on %s",
436 static_cast<uint8_t>(statusConnect), to_string(statusConnect).c_str(), errno, strerror(errno),
437 to_string(hci_peer_mac_type).c_str(),
438 to_string(hci_own_mac_type).c_str(),
439 toString().c_str());
440 adapter.unlockConnect(*this);
441 smp_auto_done = true; // premature end of potential SMP auto-negotiation
442 } else if ( HCIStatusCode::SUCCESS != statusConnect ) {
443 ERR_PRINT("Could not create connection: status 0x%2.2X (%s), errno %d %s, hci-atype[peer %s, own %s] on %s",
444 static_cast<uint8_t>(statusConnect), to_string(statusConnect).c_str(), errno, strerror(errno),
445 to_string(hci_peer_mac_type).c_str(),
446 to_string(hci_own_mac_type).c_str(),
447 toString().c_str());
448 adapter.unlockConnect(*this);
449 smp_auto_done = true; // premature end of potential SMP auto-negotiation
450 } else if( smp_auto ) { // implies HCIStatusCode::SUCCESS
451 // Waiting for PairingState
452 bool pairing_timeout = false;
453 {
454 std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
455 const std::chrono::steady_clock::time_point timeout_time = std::chrono::steady_clock::now() + hci.env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT.to_duration(std::chrono::milliseconds::zero());
456 while( !hasSMPPairingFinished( pairing_data.state ) ) {
457 std::cv_status s = cv_pairing_state_changed.wait_until(lock_pairing, timeout_time);
458 DBG_PRINT("BTDevice::connectLE: SEC AUTO.%d.2c Wait for SMPPairing: state %s, %s",
459 smp_auto_count, to_string(pairing_data.state).c_str(), toString().c_str());
460 if( std::cv_status::timeout == s && !hasSMPPairingFinished( pairing_data.state ) ) {
461 // timeout
462 ERR_PRINT("SEC AUTO.%d.X Timeout SMPPairing: Disconnecting %s", smp_auto_count, toString().c_str());
463 smp_auto_done = true;
464 pairing_data.io_cap_auto = SMPIOCapability::UNSET;
465 pairing_timeout = true;
466 break;
467 }
468 }
469 pstate = pairing_data.state;
470 DBG_PRINT("BTDevice::connectLE: SEC AUTO.%d.2d Wait for SMPPairing: state %s, %s",
471 smp_auto_count, to_string(pstate).c_str(), toString().c_str());
472 }
473 if( pairing_timeout ) {
474 pairing_data.io_cap_auto = SMPIOCapability::UNSET;
476 statusConnect = HCIStatusCode::INTERNAL_TIMEOUT;
477 adapter.unlockConnect(*this);
478 smp_auto_done = true;
479 } else if( SMPPairingState::COMPLETED == pstate ) {
480 DBG_PRINT("BTDevice::connectLE: SEC AUTO.%d.X Done: %s", smp_auto_count, toString().c_str());
481 smp_auto_done = true;
482 (void)smp_auto_done;
483 break;
484 } else if( SMPPairingState::FAILED == pstate ) {
485 if( !smp_auto_done ) { // not last one
486 // disconnect for next smp_auto mode test
487 jau::fraction_i64 td_disconnect = 0_s;
488 DBG_PRINT("BTDevice::connectLE: SEC AUTO.%d.3 Failed SMPPairing -> Disconnect: %s", smp_auto_count, toString().c_str());
490 if( HCIStatusCode::SUCCESS == dres ) {
491 while( isConnected && hci.env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT > td_disconnect ) {
493 td_disconnect += hci.env.HCI_COMMAND_POLL_PERIOD;
495 }
496 }
497 if( hci.env.HCI_COMMAND_COMPLETE_REPLY_TIMEOUT <= td_disconnect ) {
498 // timeout
499 ERR_PRINT("SEC AUTO.%d.4 Timeout Disconnect td_pairing %" PRIi64 " ms: %s",
500 smp_auto_count, td_disconnect.to_ms(), toString().c_str());
501 pairing_data.io_cap_auto = SMPIOCapability::UNSET;
502 statusConnect = HCIStatusCode::INTERNAL_TIMEOUT;
503 adapter.unlockConnect(*this);
504 smp_auto_done = true;
505 }
506 }
507 }
508 }
509 } while( !smp_auto_done );
510 if( smp_auto ) {
512 if( HCIStatusCode::SUCCESS == statusConnect && SMPPairingState::FAILED == pstate ) {
513 ERR_PRINT("SEC AUTO.%d.X Failed SMPPairing -> Disconnect: %s", smp_auto_count, toString().c_str());
514 pairing_data.io_cap_auto = SMPIOCapability::UNSET;
516 statusConnect = HCIStatusCode::AUTH_FAILED;
517 }
518 pairing_data.io_cap_auto = SMPIOCapability::UNSET; // always clear post-auto-action: Allow normal notification.
519 }
520 return statusConnect;
521}
522
523HCIStatusCode BTDevice::connectBREDR(const uint16_t pkt_type, const uint16_t clock_offset, const uint8_t role_switch) noexcept
524{
525 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_connect); // RAII-style acquire and relinquish via destructor
526 if( !adapter.isPowered() ) { // isValid() && hci.isOpen() && POWERED
527 WARN_PRINT("Adapter not powered: %s, %s", adapter.toString().c_str(), toString().c_str());
529 }
530
531 if( isConnected ) {
532 ERR_PRINT("Already connected: %s", toString().c_str());
534 }
535 if( !addressAndType.isBREDRAddress() ) {
536 ERR_PRINT("Not a BDADDR_BREDR address: %s", toString().c_str());
538 }
539
540 HCIHandler &hci = adapter.getHCI();
541 if( !hci.isOpen() ) {
542 ERR_PRINT("HCI closed: %s", toString().c_str());
544 }
545
546 {
548 if( !adapter.lockConnect(*this, true /* wait */, pairing_data.io_cap_user) ) {
549 ERR_PRINT("adapter::lockConnect() failed: %s", toString().c_str());
551 }
552 }
553 HCIStatusCode status = hci.create_conn(addressAndType.address, pkt_type, clock_offset, role_switch);
554 allowDisconnect = true;
555 if ( HCIStatusCode::SUCCESS != status ) {
556 ERR_PRINT("Could not create connection: status 0x%2.2X (%s), errno %d %s on %s",
557 static_cast<uint8_t>(status), to_string(status).c_str(), errno, strerror(errno), toString().c_str());
558 adapter.unlockConnect(*this);
559 }
560 return status;
561}
562
564{
565 switch( addressAndType.type ) {
567 [[fallthrough]];
569 return connectLE();
571 return connectBREDR();
572 default:
573 ERR_PRINT("Not a valid address type: %s", toString().c_str());
575 }
576}
577
578void BTDevice::notifyConnected(const std::shared_ptr<BTDevice>& sthis, const uint16_t handle, const SMPIOCapability io_cap_has) noexcept {
579 // coming from connected callback, update states including pairing_data.ioCap_conn
581 DBG_PRINT("BTDevice::notifyConnected: Start: handle %s -> %s, io %s / %s -> %s, %s",
582 jau::to_hexstring(hciConnHandle).c_str(), jau::to_hexstring(handle).c_str(),
583 to_string(pairing_data.io_cap_conn).c_str(), to_string(io_cap_has).c_str(), to_string(pairing_data.io_cap_user).c_str(),
584 toString().c_str());
585 allowDisconnect = true;
586 isConnected = true;
587 hciConnHandle = handle;
588 SMPIOCapability io_cap_pre = pairing_data.io_cap_conn;
589 if( SMPIOCapability::UNSET == pairing_data.io_cap_conn ) { // Exclusion for smp-auto mode
590 pairing_data.io_cap_conn = io_cap_has;
591 }
592 DBG_PRINT("BTDevice::notifyConnected: End: io_cap %s: %s / %s -> %s, %s",
593 to_string(pairing_data.io_cap_user).c_str(),
594 to_string(io_cap_pre).c_str(),
595 to_string(io_cap_has).c_str(),
596 to_string(pairing_data.io_cap_conn).c_str(),
597 toString().c_str());
598 (void)sthis; // not used yet
599}
600
601void BTDevice::notifyLEFeatures(const std::shared_ptr<BTDevice>& sthis, const LE_Features features) noexcept {
602 const bool is_local_server = BTRole::Master == btRole; // -> local GattRole::Server
603 bool enc_done, using_auth, is_pre_paired;
604 getSMPEncStatus(enc_done, using_auth, is_pre_paired);
605
606 DBG_PRINT("BTDevice::notifyLEFeatures: start[local_server %d, enc_done %d, auth %d, pre_paired %d]: %s -> %s, %s",
607 is_local_server, enc_done, using_auth, is_pre_paired,
608 direct_bt::to_string(le_features).c_str(),
609 direct_bt::to_string(features).c_str(),
610 toString().c_str());
611
612 le_features = features;
613 if( addressAndType.isLEAddress() && ( !l2cap_att->is_open() || is_local_server ) ) {
614 std::thread bg(&BTDevice::processL2CAPSetup, this, sthis); // @suppress("Invalid arguments")
615 bg.detach();
616 }
617}
618
619void BTDevice::notifyLEPhyUpdateComplete(const HCIStatusCode status, const LE_PHYs Tx, const LE_PHYs Rx) noexcept {
620 DBG_PRINT("BTDevice::notifyLEPhyUpdateComplete: %s: [Tx %s, Rx %s], %s",
621 direct_bt::to_string(status).c_str(),
622 direct_bt::to_string(Tx).c_str(), direct_bt::to_string(Rx).c_str(), toString().c_str());
623 if( HCIStatusCode::SUCCESS == status ) {
624 le_phy_tx = Tx;
625 le_phy_rx = Rx;
626 }
627}
628
629void BTDevice::processL2CAPSetup(std::shared_ptr<BTDevice> sthis) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
630 bool callProcessDeviceReady = false;
631 bool callDisconnect = false;
632 bool smp_auto = false;
633 const bool is_local_server = BTRole::Master == btRole; // -> local GattRole::Server
634
635 if( addressAndType.isLEAddress() && ( is_local_server || !l2cap_att->is_open() ) ) {
636 std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
637
638 DBG_PRINT("BTDevice::processL2CAPSetup: Start dev_id %u, %s", adapter.dev_id, toString().c_str());
639
640 BTSecurityLevel sec_level;
641 SMPIOCapability io_cap;
642 validateConnectedSecParam(sec_level, io_cap);
643
644 pairing_data.sec_level_conn = sec_level;
645 // pairing_data.io_cap_conn = io_cap; // unchanged
646
647 bool l2cap_open;
648 if( is_local_server ) {
649 const uint64_t t0 = ( jau::environment::get().debug ) ? jau::getCurrentMilliseconds() : 0;
650 std::unique_ptr<L2CAPClient> l2cap_att_new = adapter.get_l2cap_connection(sthis);
651 const uint64_t td = ( jau::environment::get().debug ) ? jau::getCurrentMilliseconds() - t0 : 0;
652 if( nullptr == l2cap_att_new ) {
653 DBG_PRINT("L2CAP-ACCEPT: BTDevice::processL2CAPSetup: dev_id %d, td %" PRIu64 "ms, NULL l2cap_att", adapter.dev_id, td);
654 l2cap_open = false;
655 } else {
656 l2cap_att = std::move(l2cap_att_new);
657 DBG_PRINT("L2CAP-ACCEPT: BTDevice::processL2CAPSetup: dev_id %d, td %" PRIu64 "ms, l2cap_att %s", adapter.dev_id, td, l2cap_att->toString().c_str());
658 if( BTSecurityLevel::UNSET < sec_level && sec_level < BTSecurityLevel::ENC_AUTH ) { // authentication must be left alone in server mode
659 l2cap_open = l2cap_att->setBTSecurityLevel(sec_level);
660 } else {
661 l2cap_open = true;
662 }
663 }
664 } else {
665 l2cap_open = l2cap_att->open(*this, sec_level); // initiates hciSMPMsgCallback() if sec_level > BT_SECURITY_LOW
666 }
667 const bool l2cap_enc = l2cap_open && BTSecurityLevel::NONE < sec_level;
668
669 const bool own_smp = SMP_SUPPORTED_BY_OS ? connectSMP(sthis, sec_level) && BTSecurityLevel::NONE < sec_level : false;
670
671 DBG_PRINT("BTDevice::processL2CAPSetup: dev_id %u, lvl %s, connect[own_smp %d, l2cap[open %d, enc %d]]",
672 adapter.dev_id, to_string(sec_level).c_str(), own_smp, l2cap_open, l2cap_enc);
673
674 adapter.unlockConnect(*this);
675
676 if( !l2cap_open ) {
677 pairing_data.sec_level_conn = BTSecurityLevel::NONE;
678 const SMPIOCapability smp_auto_io_cap = pairing_data.io_cap_auto; // cache against clearSMPState
679 smp_auto = SMPIOCapability::UNSET != smp_auto_io_cap; // logical cached state
680 if( smp_auto ) {
681 pairing_data.mode = PairingMode::NONE;
682 pairing_data.state = SMPPairingState::FAILED;
683 lock_pairing.unlock(); // unlock mutex before notify_all to avoid pessimistic re-block of notified wait() thread.
684 cv_pairing_state_changed.notify_all();
685 } else {
686 callDisconnect = true;
688 }
689 } else if( !l2cap_enc ) {
690 callProcessDeviceReady = true;
691 lock_pairing.unlock(); // unlock mutex before notifying and `processDeviceReady()`
692 const uint64_t ts = jau::getCurrentMilliseconds();
693 adapter.notifyPairingStageDone(sthis, ts);
694 processDeviceReady(sthis, ts);
695 }
696 } else {
697 DBG_PRINT("BTDevice::processL2CAPSetup: Skipped (not LE) dev_id %u, %s", adapter.dev_id, toString().c_str());
698 }
699 DBG_PRINT("BTDevice::processL2CAPSetup: End [dev_id %u, disconnect %d, deviceReady %d, smp_auto %d], %s",
700 adapter.dev_id, callDisconnect, callProcessDeviceReady, smp_auto, toString().c_str());
701}
702
703void BTDevice::getSMPEncStatus(bool& enc_done, bool& using_auth, bool& is_pre_paired) {
704 BTSecurityLevel sec_level;
705 PairingMode pmode;
706 SMPPairingState pstate;
707 {
708 jau::sc_atomic_critical sync(sync_data);
709 sec_level = pairing_data.sec_level_conn;
710 pmode = pairing_data.mode;
711 pstate = pairing_data.state;
712 }
713 is_pre_paired = PairingMode::PRE_PAIRED == pmode;
714 enc_done = is_pre_paired || SMPPairingState::COMPLETED == pstate;
715 using_auth = BTSecurityLevel::ENC_AUTH <= sec_level;
716}
717
718void BTDevice::processDeviceReady(std::shared_ptr<BTDevice> sthis, const uint64_t timestamp) { // NOLINT(performance-unnecessary-value-param): Pass-by-value out-of-thread
719 const bool is_local_server = BTRole::Master == btRole; // -> local GattRole::Server
720 bool enc_done, using_auth, is_pre_paired;
721 getSMPEncStatus(enc_done, using_auth, is_pre_paired);
722
723 DBG_PRINT("BTDevice::processDeviceReady: start[local_server %d, enc_done %d, auth %d, pre_paired %d], %s",
724 is_local_server, enc_done, using_auth, is_pre_paired, toString().c_str());
725
726 if( BTRole::Slave == btRole ) { // -> local GattRole::Client
727 /**
728 * Give remote slave (peripheral, Gatt-Server) 'some time'
729 * to complete connection and listening to our Gatt-Client requests.
730 *
731 * We give the Gatt-Server a slightly longer period
732 * after newly paired encryption keys.
733 */
734 if( enc_done && !is_pre_paired ) {
735 // newly paired keys
736 std::this_thread::sleep_for(std::chrono::milliseconds(150));
737 } else {
738 // pre-paired or no encryption
739 std::this_thread::sleep_for(std::chrono::milliseconds(100));
740 }
741 }
742
744 const bool gatt_res = connectGATT(sthis);
745
746 if( !gatt_res && enc_done ) {
747 // Need to repair as GATT communication failed
748 unpair_res = unpair();
749 }
750 DBG_PRINT("BTDevice::processDeviceReady: done[GATT %d, unpair %s], %s",
751 gatt_res, to_string(unpair_res).c_str(), toString().c_str());
752
753 if( gatt_res ) {
754 adapter.sendDeviceReady(sthis, timestamp);
755 }
756}
757
758
759// Pre-Pair minimum requirement: ENC_KEY and LINK_KEY (if available for SC)!
762
763bool BTDevice::checkPairingKeyDistributionComplete() const noexcept {
764 bool res = false;
765
766 if( pairing_data.encryption_enabled &&
767 SMPPairingState::KEY_DISTRIBUTION == pairing_data.state )
768 {
769 const SMPKeyType key_mask = pairing_data.use_sc ? _key_mask_sc : _key_mask_legacy;
770
771 if( ( pairing_data.keys_init_has & key_mask ) == ( pairing_data.keys_init_exp & key_mask ) &&
772 ( pairing_data.keys_resp_has & key_mask ) == ( pairing_data.keys_resp_exp & key_mask ) ) {
773 res = true;
774 }
775 }
776 return res;
777}
778
779std::string BTDevice::PairingData::toString(const uint16_t dev_id, const BDAddressAndType& addrAndType, const BTRole& role) const {
780 std::string res = "PairingData[dev_id "+std::to_string(dev_id)+", Remote ["+addrAndType.toString()+", role "+to_string(role)+"], \n";
781 res.append(" Status: Encrypted "+std::to_string(encryption_enabled)+
782 ", State "+to_string(state)+", Mode "+to_string(mode)+
783 ", Responder-Req "+std::to_string(res_requested_sec)+"\n");
784 res.append(" Setup:\n");
785 res.append(" - SC "+std::to_string(use_sc)+"\n");
786 res.append(" - Pre-Paired "+std::to_string(is_pre_paired)+"\n");
787 res.append(" - IOCap conn "+to_string(io_cap_conn)+", user "+to_string(io_cap_user)+", auto "+to_string(io_cap_auto)+"\n");
788 res.append(" - Level conn "+to_string(sec_level_conn)+", user "+to_string(sec_level_user)+"\n");
789 res.append(" Initiator (master) Set:\n");
790 res.append(" - OOB "+to_string(oobFlag_init)+"\n");
791 res.append(" - Auth "+to_string(authReqs_init)+"\n");
792 res.append(" - IOCap "+to_string(ioCap_init)+"\n");
793 res.append(" - EncSz "+std::to_string(maxEncsz_init)+"\n");
794 res.append(" - Keys "+to_string(keys_init_has)+" / "+to_string(keys_init_exp)+"\n");
795 res.append(" - "+ltk_init.toString()+"\n");
796 res.append(" - "+lk_init.toString()+"\n");
797 res.append(" - "+irk_init.toString()+"\n");
798 res.append(" - "+csrk_init.toString()+"\n");
799 res.append(" - IdAdr "+id_address_init.toString()+"\n");
800 res.append(" Responder (slave) Set:\n");
801 res.append(" - OOB "+to_string(oobFlag_resp)+"\n");
802 res.append(" - Auth "+to_string(authReqs_resp)+"\n");
803 res.append(" - IOCap "+to_string(ioCap_resp)+"\n");
804 res.append(" - EncSz "+std::to_string(maxEncsz_resp)+"\n");
805 res.append(" - Keys "+to_string(keys_resp_has)+" / "+to_string(keys_resp_exp)+"\n");
806 res.append(" - PassKey "+toPassKeyString(passKey_resp)+"\n");
807 res.append(" - "+ltk_resp.toString()+"\n");
808 res.append(" - "+lk_resp.toString()+"\n");
809 res.append(" - "+irk_resp.toString()+"\n");
810 res.append(" - "+csrk_resp.toString()+"\n");
811 res.append(" - IdAdr "+id_address_resp.toString()+" ]");
812 return res;
813}
814
815bool BTDevice::updatePairingState(const std::shared_ptr<BTDevice>& sthis, const MgmtEvent& evt, const HCIStatusCode evtStatus, SMPPairingState claimed_state) noexcept {
816 std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
817 const std::string timestamp = jau::to_decstring(jau::environment::getElapsedMillisecond(evt.getTimestamp()), ',', 9);
818
819 if( jau::environment::get().debug ) {
820 jau::PLAIN_PRINT(false, "[%s] BTDevice::updatePairingState.0: state %s -> claimed %s, mode %s",
821 timestamp.c_str(),
822 to_string(pairing_data.state).c_str(), to_string(claimed_state).c_str(), to_string(pairing_data.mode).c_str());
823 jau::PLAIN_PRINT(false, "[%s] %s", timestamp.c_str(), evt.toString().c_str());
824 }
825
826 const SMPIOCapability iocap = pairing_data.io_cap_conn;
827 const MgmtEvent::Opcode mgmtEvtOpcode = evt.getOpcode();
828 PairingMode mode = pairing_data.mode;
829 bool check_pairing_complete = false;
830 bool is_device_ready = false;
831
832 smp_events++;
833
834 // No encryption key will be overwritten by this update method, only initial setting allowed.
835 // SMP encryption information always overrules.
836
837 if( SMPPairingState::FAILED == pairing_data.state ) { // no recovery from FAILED via SMP
838 claimed_state = SMPPairingState::FAILED;
839 }
840 if( pairing_data.state != claimed_state ) {
841 // Potentially force update PairingMode by forced state change, assuming being the initiator.
842 switch( claimed_state ) {
844 // no change
845 claimed_state = pairing_data.state;
846 break;
847 case SMPPairingState::FAILED: { /* Next: disconnect(..) by user or auto-mode */
848 mode = PairingMode::NONE;
849 } break;
851 if( hasSMPIOCapabilityFullInput( iocap ) ) {
853 } else {
854 // BT core requesting passkey input w/o full input caps is nonsense (bug?)
855 // Reply with a default value '0' off-thread ASAP
856 DBG_PRINT("BTDevice::updatePairingState.1a: state %s [ignored %s, sending PASSKEY 0 reply], mode %s",
857 to_string(pairing_data.state).c_str(), to_string(claimed_state).c_str(),
858 to_string(pairing_data.mode).c_str());
859 claimed_state = pairing_data.state; // suppress
860 std::thread dc(&BTDevice::setPairingPasskey, sthis, 0);
861 dc.detach();
862 }
863 break;
865 if( hasSMPIOCapabilityBinaryInput( iocap ) ) {
867 } else {
868 // BT core requesting binary input w/o input caps is nonsense (bug?)
869 // Reply with a default value 'true' off-thread ASAP
870 DBG_PRINT("BTDevice::updatePairingState.1b: state %s [ignored %s, sending CONFIRM reply], mode %s",
871 to_string(pairing_data.state).c_str(), to_string(claimed_state).c_str(),
872 to_string(pairing_data.mode).c_str());
873 claimed_state = pairing_data.state; // suppress
874 std::thread dc(&BTDevice::setPairingNumericComparison, sthis, true);
875 dc.detach();
876 }
877 break;
879 if( MgmtEvent::Opcode::PASSKEY_NOTIFY == mgmtEvtOpcode ) {
880 // we must be in slave/responder mode, i.e. peripheral/GATT-server providing auth passkey
881 const MgmtEvtPasskeyNotify& event = *static_cast<const MgmtEvtPasskeyNotify *>(&evt);
883 pairing_data.passKey_resp = event.getPasskey();
884 }
885 break;
887 // 2
889 break;
891 if( SMPPairingState::FEATURE_EXCHANGE_STARTED > pairing_data.state ) {
892 //
893 // No SMP pairing in process (maybe REQUESTED_BY_RESPONDER at maximum),
894 //
895 if constexpr ( CONSIDER_HCI_CMD_FOR_SMP_STATE ) {
896 if( MgmtEvent::Opcode::HCI_LE_ENABLE_ENC == mgmtEvtOpcode &&
897 HCIStatusCode::SUCCESS == evtStatus )
898 {
899 // 3a
900 // No SMP pairing in process (maybe REQUESTED_BY_RESPONDER at maximum),
901 // i.e. prepairing or already paired, reusing keys and usable connection
902 //
903 // Local BTRole::Master initiator
904 // Encryption key is associated with the remote device having role BTRole::Slave (responder).
905 const MgmtEvtHCILEEnableEncryptionCmd& event = *static_cast<const MgmtEvtHCILEEnableEncryptionCmd *>(&evt);
906 const bool use_auth = BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn;
907 const SMPLongTermKey smp_ltk = event.toSMPLongTermKey(pairing_data.use_sc, use_auth);
908 if( smp_ltk.isValid() ) {
909 if( pairing_data.use_sc ) { // in !SC mode, SMP will deliver different keys!
910 // Secure Connections (SC) use AES sync key for both, initiator and responder.
911 // true == smp_ltk.isResponder()
912 if( ( SMPKeyType::ENC_KEY & pairing_data.keys_resp_has ) == SMPKeyType::NONE ) { // no overwrite
913 pairing_data.ltk_resp = smp_ltk;
914 pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY;
915 if( pairing_data.use_sc &&
916 ( SMPKeyType::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyType::NONE ) // no overwrite
917 {
918 pairing_data.ltk_init = smp_ltk;
919 pairing_data.ltk_init.properties &= ~SMPLongTermKey::Property::RESPONDER; // enforce for SC
920 pairing_data.keys_init_has |= SMPKeyType::ENC_KEY;
921 }
922 }
923 }
925 // Waiting for HCI_ENC_CHANGED or HCI_ENC_KEY_REFRESH_COMPLETE
926 }
927 claimed_state = pairing_data.state; // not yet
928 break; // case SMPPairingState::COMPLETED:
929 }
930 }
931 if( MgmtEvent::Opcode::HCI_ENC_CHANGED == mgmtEvtOpcode &&
932 HCIStatusCode::SUCCESS == evtStatus )
933 {
934 // 3b
935 // No SMP pairing in process (maybe REQUESTED_BY_RESPONDER at maximum),
936 // i.e. prepairing or already paired, reusing keys and usable connection
938 pairing_data.encryption_enabled = true;
939 is_device_ready = true;
940 } else if( MgmtEvent::Opcode::PAIR_DEVICE_COMPLETE == mgmtEvtOpcode &&
941 HCIStatusCode::ALREADY_PAIRED == evtStatus )
942 {
943 // 3c
944 // No SMP pairing in process (maybe REQUESTED_BY_RESPONDER at maximum),
945 // i.e. already paired, reusing keys and usable connection
947 pairing_data.encryption_enabled = true;
948 is_device_ready = true;
949 }
950 } else if( HCIStatusCode::SUCCESS == evtStatus &&
951 SMPPairingState::KEY_DISTRIBUTION == pairing_data.state )
952 {
953 //
954 // SMPPairingState::KEY_DISTRIBUTION
955 //
956 if constexpr ( CONSIDER_HCI_CMD_FOR_SMP_STATE ) {
957 if( MgmtEvent::Opcode::HCI_LE_ENABLE_ENC == mgmtEvtOpcode ) {
958 // 4b
959 // Local BTRole::Master initiator
960 // Encryption key is associated with the remote device having role BTRole::Slave (responder).
961 const MgmtEvtHCILEEnableEncryptionCmd& event = *static_cast<const MgmtEvtHCILEEnableEncryptionCmd *>(&evt);
962 const bool use_auth = BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn;
963 const SMPLongTermKey smp_ltk = event.toSMPLongTermKey(pairing_data.use_sc, use_auth);
964 if( smp_ltk.isValid() ) {
965 if( pairing_data.use_sc ) { // in !SC mode, SMP will deliver different keys!
966 // Secure Connections (SC) use AES sync key for both, initiator and responder.
967 // true == smp_ltk.isResponder()
968 if( ( SMPKeyType::ENC_KEY & pairing_data.keys_resp_has ) == SMPKeyType::NONE ) { // no overwrite
969 pairing_data.ltk_resp = smp_ltk;
970 pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY;
971 if( pairing_data.use_sc &&
972 ( SMPKeyType::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyType::NONE ) // no overwrite
973 {
974 pairing_data.ltk_init = smp_ltk;
975 pairing_data.ltk_init.properties &= ~SMPLongTermKey::Property::RESPONDER; // enforce for SC
976 pairing_data.keys_init_has |= SMPKeyType::ENC_KEY;
977 }
978 }
979 }
980 // Waiting for HCI_ENC_CHANGED or HCI_ENC_KEY_REFRESH_COMPLETE
981 }
982 claimed_state = pairing_data.state; // not yet
983 break; // case SMPPairingState::COMPLETED:
984 } else if( MgmtEvent::Opcode::HCI_LE_LTK_REQUEST == mgmtEvtOpcode ) {
985 // 4c
986 // Local BTRole::Slave responder
987 const MgmtEvtHCILELTKReq& event = *static_cast<const MgmtEvtHCILELTKReq *>(&evt);
988 const bool use_auth = BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn;
989 const SMPLongTermKey smp_ltk = event.toSMPLongTermKey(pairing_data.use_sc, use_auth);
990 { // if( smp_ltk.isValid() ) // not yet valid
991 if( pairing_data.use_sc ) { // in !SC mode, SMP will deliver different keys!
992 // true == smp_ltk.isResponder()
993 if( ( SMPKeyType::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyType::NONE ) { // no overwrite
994 pairing_data.ltk_resp = smp_ltk;
995 // pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY; // not yet -> LE_LTK_REPLY_ACK LTK { LTK }
996 }
997 }
998 // Waiting for LE_LTK_REPLY_ACK { LTK }
999 }
1000 claimed_state = pairing_data.state; // not yet
1001 break; // case SMPPairingState::COMPLETED:
1002 } else if( MgmtEvent::Opcode::HCI_LE_LTK_REPLY_ACK == mgmtEvtOpcode ) {
1003 // 4d
1004 // Local BTRole::Slave responder
1005 const MgmtEvtHCILELTKReplyAckCmd& event = *static_cast<const MgmtEvtHCILELTKReplyAckCmd *>(&evt);
1006 SMPLongTermKey smp_ltk = pairing_data.ltk_resp;
1007 smp_ltk.enc_size = 16; // valid now;
1008 smp_ltk.ltk = event.getLTK();
1009 if( smp_ltk.isValid() ) {
1010 if( pairing_data.use_sc ) { // in !SC mode, SMP will deliver different keys!
1011 // Secure Connections (SC) use AES sync key for both, initiator and responder.
1012 // true == smp_ltk.isResponder()
1013 if( ( SMPKeyType::ENC_KEY & pairing_data.keys_resp_has ) == SMPKeyType::NONE ) { // no overwrite
1014 pairing_data.ltk_resp = smp_ltk;
1015 pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY;
1016 if( pairing_data.use_sc &&
1017 ( SMPKeyType::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyType::NONE ) // no overwrite
1018 {
1019 pairing_data.ltk_init = smp_ltk;
1020 pairing_data.ltk_init.properties &= ~SMPLongTermKey::Property::RESPONDER; // enforce for SC
1021 pairing_data.keys_init_has |= SMPKeyType::ENC_KEY;
1022 }
1023 check_pairing_complete = true;
1024 }
1025 }
1026 }
1027 if( !check_pairing_complete ) {
1028 claimed_state = pairing_data.state; // invalid smp_ltk or no overwrite
1029 }
1030 break; // case SMPPairingState::COMPLETED:
1031 }
1032 }
1033 if( MgmtEvent::Opcode::HCI_ENC_CHANGED == mgmtEvtOpcode ||
1035 // 4a
1036 pairing_data.encryption_enabled = true;
1037 check_pairing_complete = true;
1038 } else if( MgmtEvent::Opcode::NEW_LONG_TERM_KEY == mgmtEvtOpcode ) { /* Legacy: 2; SC: 2 (synthetic by mgmt) */
1039 // 4e
1040 // SMP pairing has started, mngr issued new LTK command
1041 const MgmtEvtNewLongTermKey& event = *static_cast<const MgmtEvtNewLongTermKey *>(&evt);
1042 const MgmtLongTermKey& mgmt_ltk = event.getLongTermKey();
1043 const SMPLongTermKey smp_ltk = mgmt_ltk.toSMPLongTermKey(!btRole);
1044 if( smp_ltk.isValid() ) {
1045 // Secure Connections (SC) use AES sync key for both, initiator and responder.
1046 if( pairing_data.use_sc || smp_ltk.isResponder() ) {
1047 if( ( SMPKeyType::ENC_KEY & pairing_data.keys_resp_has ) == SMPKeyType::NONE ) { // no overwrite
1048 pairing_data.ltk_resp = smp_ltk;
1049 pairing_data.ltk_resp.properties |= SMPLongTermKey::Property::RESPONDER; // enforce for SC
1050 pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY;
1051 check_pairing_complete = true;
1052 }
1053 }
1054 if( pairing_data.use_sc || !smp_ltk.isResponder() ) {
1055 if( ( SMPKeyType::ENC_KEY & pairing_data.keys_init_has ) == SMPKeyType::NONE ) { // no overwrite
1056 pairing_data.ltk_init = smp_ltk;
1057 pairing_data.ltk_init.properties &= ~SMPLongTermKey::Property::RESPONDER; // enforce for SC
1058 pairing_data.keys_init_has |= SMPKeyType::ENC_KEY;
1059 check_pairing_complete = true;
1060 }
1061 }
1062 }
1063 if( !check_pairing_complete ) {
1064 claimed_state = pairing_data.state; // invalid smp_ltk or no overwrite
1065 }
1066 } else if( MgmtEvent::Opcode::NEW_LINK_KEY == mgmtEvtOpcode ) { /* Legacy: N/A; SC: 4 (last value) */
1067 // 4f
1068 // SMP pairing has started, mngr issued new LinkKey command
1069 // Link Key is for this host only!
1070 const MgmtEvtNewLinkKey& event = *static_cast<const MgmtEvtNewLinkKey *>(&evt);
1071 const MgmtLinkKeyInfo& mgmt_lk = event.getLinkKey();
1072 const BTRole hostRole = !btRole;
1073 const SMPLinkKey smp_lk = mgmt_lk.toSMPLinkKeyInfo( BTRole::Slave == hostRole /* isResponder */ );
1074 if( smp_lk.isValid() ) {
1075 if( smp_lk.isResponder() ) {
1076 if( ( SMPKeyType::LINK_KEY & pairing_data.keys_resp_has ) == SMPKeyType::NONE ) { // no overwrite
1077 pairing_data.lk_resp = smp_lk;
1078 pairing_data.keys_resp_has |= SMPKeyType::LINK_KEY;
1079 check_pairing_complete = true;
1080 }
1081 if( smp_lk.isCombiKey() &&
1082 ( SMPKeyType::LINK_KEY & pairing_data.keys_init_has ) == SMPKeyType::NONE ) // no overwrite
1083 {
1084 pairing_data.lk_init = smp_lk;
1085 pairing_data.lk_init.responder = false;
1086 pairing_data.keys_init_has |= SMPKeyType::LINK_KEY;
1087 }
1088 } else {
1089 if( ( SMPKeyType::LINK_KEY & pairing_data.keys_init_has ) == SMPKeyType::NONE ) { // no overwrite
1090 pairing_data.lk_init = smp_lk;
1091 pairing_data.keys_init_has |= SMPKeyType::LINK_KEY;
1092 check_pairing_complete = true;
1093 }
1094 if( smp_lk.isCombiKey() &&
1095 ( SMPKeyType::LINK_KEY & pairing_data.keys_resp_has ) == SMPKeyType::NONE ) // no overwrite
1096 {
1097 pairing_data.lk_resp = smp_lk;
1098 pairing_data.lk_resp.responder = true;
1099 pairing_data.keys_resp_has |= SMPKeyType::LINK_KEY;
1100 }
1101 }
1102 }
1103 if( !check_pairing_complete ) {
1104 claimed_state = pairing_data.state; // invalid smp_lk or no overwrite
1105 }
1106 } /* if ( X == mgmtEvtOpcode ) */
1107 else {
1108 // Ignore: Undesired event or SMP pairing is in process, which needs to be completed.
1109 claimed_state = pairing_data.state;
1110 }
1111 } /* if( HCIStatusCode::SUCCESS == evtStatus && SMPPairingState::KEY_DISTRIBUTION == pairing_data.state ) */
1112 else {
1113 // Ignore: Undesired event or SMP pairing is in process, which needs to be completed.
1114 claimed_state = pairing_data.state;
1115 }
1116 break; // case SMPPairingState::COMPLETED:
1117 default: // use given state as-is
1118 break;
1119 } // switch( claimed_state )
1120
1121 if( !is_device_ready && check_pairing_complete ) {
1122 is_device_ready = checkPairingKeyDistributionComplete();
1123 if( !is_device_ready ) {
1124 claimed_state = pairing_data.state; // not yet
1125 }
1126 }
1127 if( is_device_ready ) {
1128 pairing_data.is_pre_paired = true;
1129 pairing_data.sec_level_conn = l2cap_att->getBTSecurityLevel();
1130 }
1131 } // if( pairing_data.state != claimed_state )
1132
1133 // 5
1134 if( pairing_data.state != claimed_state ) {
1135 if( is_device_ready && BTRole::Master == btRole &&
1136 pairing_data.sec_level_user >= BTSecurityLevel::ENC_ONLY ) {
1137 // Validate encryption and authentication requirements in server mode!
1138 if( ( pairing_data.sec_level_user == BTSecurityLevel::ENC_ONLY &&
1139 pairing_data.sec_level_conn < BTSecurityLevel::ENC_ONLY ) ||
1140
1141 ( pairing_data.sec_level_user >= BTSecurityLevel::ENC_AUTH &&
1142 pairing_data.sec_level_conn < BTSecurityLevel::ENC_AUTH )
1143 )
1144 {
1145 claimed_state = SMPPairingState::FAILED;
1146 is_device_ready = false;
1147 DBG_PRINT("BTDevice:updatePairingState:Sec-Failure: Requested Sec-Level %s > Actual %s",
1148 to_string(pairing_data.sec_level_user).c_str(),
1149 to_string(pairing_data.sec_level_conn).c_str());
1150 }
1151 }
1152
1153 // 5b
1154 if( jau::environment::get().debug ) {
1155 jau::PLAIN_PRINT(false, "[%s] BTDevice::updatePairingState.5b: state %s -> %s, mode %s -> %s, ready %d, checkedPState %d",
1156 timestamp.c_str(),
1157 to_string(pairing_data.state).c_str(), to_string(claimed_state).c_str(),
1158 to_string(pairing_data.mode).c_str(), to_string(mode).c_str(), is_device_ready, check_pairing_complete);
1159 jau::PLAIN_PRINT(false, "[%s] %s", timestamp.c_str(), evt.toString().c_str());
1160 }
1161 pairing_data.mode = mode;
1162 pairing_data.state = claimed_state;
1163
1164 if( jau::environment::get().debug ) {
1165 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1166 jau::PLAIN_PRINT(false, "[%s] BTDevice::updatePairingState.5b: %s", timestamp.c_str(),
1167 pairing_data.toString(adapter.dev_id, addressAndType, btRole).c_str());
1168 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1169 }
1170
1171 adapter.sendDevicePairingState(sthis, claimed_state, mode, evt.getTimestamp());
1172
1173 if( is_device_ready ) {
1174 smp_events = 0;
1175 adapter.notifyPairingStageDone(sthis, evt.getTimestamp());
1176 std::thread dc(&BTDevice::processDeviceReady, this, sthis, evt.getTimestamp()); // @suppress("Invalid arguments")
1177 dc.detach();
1178 }
1179
1180 if( jau::environment::get().debug ) {
1181 jau::PLAIN_PRINT(false, "[%s] BTDevice::updatePairingState.5b: End: state %s",
1182 timestamp.c_str(), to_string(pairing_data.state).c_str());
1183 jau::PLAIN_PRINT(false, "[%s] %s", timestamp.c_str(), toString().c_str());
1184 }
1185
1186 lock_pairing.unlock(); // unlock mutex before notify_all to avoid pessimistic re-block of notified wait() thread.
1187 cv_pairing_state_changed.notify_all();
1188
1189 return true;
1190 } else {
1191 // 5a
1192 if( jau::environment::get().debug ) {
1193 jau::PLAIN_PRINT(false, "[%s] BTDevice::updatePairingState.5a: state %s == %s, mode %s -> %s, ready %d, checkedPState %d",
1194 timestamp.c_str(),
1195 to_string(pairing_data.state).c_str(), to_string(claimed_state).c_str(),
1196 to_string(pairing_data.mode).c_str(), to_string(mode).c_str(), is_device_ready, check_pairing_complete);
1197 jau::PLAIN_PRINT(false, "[%s] %s", timestamp.c_str(), evt.toString().c_str());
1198 }
1199 pairing_data.mode = mode;
1200 if( jau::environment::get().debug ) {
1201 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1202 jau::PLAIN_PRINT(false, "[%s] BTDevice::updatePairingState.5a: %s", timestamp.c_str(),
1203 pairing_data.toString(adapter.dev_id, addressAndType, btRole).c_str());
1204 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1205 jau::PLAIN_PRINT(false, "[%s] BTDevice::updatePairingState.5a: End: state %s",
1206 timestamp.c_str(), to_string(pairing_data.state).c_str());
1207 jau::PLAIN_PRINT(false, "[%s] %s", timestamp.c_str(), toString().c_str());
1208 }
1209 }
1210 return false;
1211}
1212
1213void BTDevice::hciSMPMsgCallback(const std::shared_ptr<BTDevice>& sthis, const SMPPDUMsg& msg, const HCIACLData::l2cap_frame& source) noexcept {
1214 std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
1215 const bool msg_sent = HCIACLData::l2cap_frame::PBFlag::START_NON_AUTOFLUSH_HOST == source.pb_flag; // from from Host to Controller
1216 const std::string msg_sent_s = msg_sent ? "sent" : "received";
1217 const BTRole localRole = !btRole; // local adapter role, opposite of device role
1218 const bool msg_from_initiator = ( msg_sent && BTRole::Master == localRole ) || ( !msg_sent && BTRole::Slave == localRole );
1219 const std::string msg_dir_s = msg_from_initiator ? "from_initiator_master" : "from_responder_slave";
1220 const SMPPairingState old_pstate = pairing_data.state;
1221 const PairingMode old_pmode = pairing_data.mode;
1222 const std::string timestamp = jau::to_decstring(jau::environment::getElapsedMillisecond(msg.getTimestamp()), ',', 9);
1223
1224 SMPPairingState pstate = old_pstate;
1225 PairingMode pmode = old_pmode;
1226 bool check_pairing_complete = false;
1227 bool is_device_ready = false;
1228
1229 smp_events++;
1230
1231 if( jau::environment::get().debug ) {
1232 jau::PLAIN_PRINT(false, "[%s] BTDevice:hci:SMP.0: %s: msg %s, local %s, remote %s @ address%s",
1233 timestamp.c_str(), msg_sent_s.c_str(), msg_dir_s.c_str(), to_string(localRole).c_str(),
1234 to_string(btRole).c_str(), addressAndType.toString().c_str());
1235 jau::PLAIN_PRINT(false, "[%s] - %s", timestamp.c_str(), msg.toString().c_str());
1236 jau::PLAIN_PRINT(false, "[%s] - %s", timestamp.c_str(), source.toString().c_str());
1237 jau::PLAIN_PRINT(false, "[%s] - %s", timestamp.c_str(), toString().c_str());
1238 }
1239
1240 const SMPPDUMsg::Opcode opc = msg.getOpcode();
1241
1242 // SMP encryption information always overrules and will overwrite.
1243
1244 switch( opc ) {
1245 // Phase 1: SMP Negotiation phase
1246
1247 case SMPPDUMsg::Opcode::SECURITY_REQUEST: // from responder (slave)
1248 // 1a
1249 if( SMPPairingState::FAILED >= pstate ) { // ignore otherwise
1252 pairing_data.res_requested_sec = true;
1253 }
1254 break;
1255
1256 case SMPPDUMsg::Opcode::PAIRING_REQUEST: // from initiator (master)
1257 // 1b
1258 if( msg_from_initiator ) {
1259 const SMPPairingMsg & msg1 = *static_cast<const SMPPairingMsg *>( &msg );
1260 pairing_data.authReqs_init = msg1.getAuthReqMask();
1261 pairing_data.ioCap_init = msg1.getIOCapability();
1262 pairing_data.oobFlag_init = msg1.getOOBDataFlag();
1263 pairing_data.maxEncsz_init = msg1.getMaxEncryptionKeySize();
1264 pairing_data.keys_init_exp = msg1.getInitKeyDist();
1267 }
1268 break;
1269
1270 case SMPPDUMsg::Opcode::PAIRING_RESPONSE: { // from responder (slave)
1271 // 1c
1272 if( !msg_from_initiator ) {
1273 const SMPPairingMsg & msg1 = *static_cast<const SMPPairingMsg *>( &msg );
1274 pairing_data.authReqs_resp = msg1.getAuthReqMask();
1275 pairing_data.ioCap_resp = msg1.getIOCapability();
1276 pairing_data.oobFlag_resp = msg1.getOOBDataFlag();
1277 pairing_data.maxEncsz_resp = msg1.getMaxEncryptionKeySize();
1278 pairing_data.keys_init_exp = msg1.getInitKeyDist(); // responding device overrides initiator's request!
1279 pairing_data.keys_resp_exp = msg1.getRespKeyDist();
1280
1281 const bool use_sc = is_set( pairing_data.authReqs_init, SMPAuthReqs::SECURE_CONNECTIONS ) &&
1282 is_set( pairing_data.authReqs_resp, SMPAuthReqs::SECURE_CONNECTIONS );
1283 pairing_data.use_sc = use_sc;
1284
1285 pmode = ::getPairingMode(use_sc,
1286 pairing_data.authReqs_init, pairing_data.ioCap_init, pairing_data.oobFlag_init,
1287 pairing_data.authReqs_resp, pairing_data.ioCap_resp, pairing_data.oobFlag_resp);
1288
1290 }
1291 } break;
1292
1293 // Phase 2: SMP Authentication and Encryption
1294
1296 // 2a
1297 [[fallthrough]];
1299 // 2b
1300 [[fallthrough]];
1302 // 2c
1303 [[fallthrough]];
1305 // 2d
1306 pmode = old_pmode;
1308 break;
1309
1310 case SMPPDUMsg::Opcode::PAIRING_FAILED: { /* Next: disconnect(..) by user or auto-mode */
1311 // 2e
1312 pmode = PairingMode::NONE;
1313 pstate = SMPPairingState::FAILED;
1314 } break;
1315
1316 // Phase 3: SMP Key & Value Distribution phase
1317
1318 case SMPPDUMsg::Opcode::ENCRYPTION_INFORMATION: { /* Legacy: 1 */
1319 // 3a
1320 // LTK: First part for SMPKeyDistFormat::ENC_KEY, followed by MASTER_IDENTIFICATION (EDIV + RAND)
1321 const SMPEncInfoMsg & msg1 = *static_cast<const SMPEncInfoMsg *>( &msg );
1322 if( !msg_from_initiator ) {
1323 // from responder (LL slave)
1324 pairing_data.ltk_resp.properties |= SMPLongTermKey::Property::RESPONDER;
1325 if( BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn ) {
1326 pairing_data.ltk_resp.properties |= SMPLongTermKey::Property::AUTH;
1327 }
1328 if( pairing_data.use_sc ) {
1329 pairing_data.ltk_resp.properties |= SMPLongTermKey::Property::SC;
1330 }
1331 pairing_data.ltk_resp.enc_size = pairing_data.maxEncsz_resp;
1332 pairing_data.ltk_resp.ltk = msg1.getLTK();
1333 } else {
1334 // from initiator (LL master)
1335 // pairing_data.ltk_resp.properties |= SMPLongTermKeyInfo::Property::INITIATOR;
1336 if( BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn ) {
1337 pairing_data.ltk_init.properties |= SMPLongTermKey::Property::AUTH;
1338 }
1339 if( pairing_data.use_sc ) {
1340 pairing_data.ltk_init.properties |= SMPLongTermKey::Property::SC;
1341 }
1342 pairing_data.ltk_init.enc_size = pairing_data.maxEncsz_init;
1343 pairing_data.ltk_init.ltk = msg1.getLTK();
1344 }
1345 } break;
1346
1347 case SMPPDUMsg::Opcode::MASTER_IDENTIFICATION: { /* Legacy: 2 */
1348 // 3b
1349 // EDIV + RAND, completing SMPKeyDistFormat::ENC_KEY
1350 const SMPMasterIdentMsg & msg1 = *static_cast<const SMPMasterIdentMsg *>( &msg );
1351 if( !msg_from_initiator ) {
1352 // from responder (LL slave)
1353 pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY;
1354 pairing_data.ltk_resp.ediv = msg1.getEDIV();
1355 pairing_data.ltk_resp.rand = msg1.getRand();
1356 } else {
1357 // from initiator (LL master)
1358 pairing_data.keys_init_has |= SMPKeyType::ENC_KEY;
1359 pairing_data.ltk_init.ediv = msg1.getEDIV();
1360 pairing_data.ltk_init.rand = msg1.getRand();
1361 }
1362 check_pairing_complete = true;
1363 } break;
1364
1365 case SMPPDUMsg::Opcode::IDENTITY_INFORMATION: { /* Legacy: 3; SC: 1 */
1366 // 3c
1367 // IRK: SMPKeyDist::ID_KEY, followed by IDENTITY_ADDRESS_INFORMATION
1368 const SMPIdentInfoMsg & msg1 = *static_cast<const SMPIdentInfoMsg *>( &msg );
1369 if( !msg_from_initiator ) {
1370 // from responder (LL slave)
1371 pairing_data.keys_resp_has |= SMPKeyType::ID_KEY;
1372 pairing_data.irk_resp.properties |= SMPIdentityResolvingKey::Property::RESPONDER;
1373 pairing_data.irk_resp.irk = msg1.getIRK();
1374 if( BDAddressType::BDADDR_UNDEFINED != pairing_data.id_address_resp.type ) {
1375 pairing_data.irk_resp.id_address = pairing_data.id_address_resp.address;
1376 }
1377 } else {
1378 // from initiator (LL master)
1379 pairing_data.keys_init_has |= SMPKeyType::ID_KEY;
1380 pairing_data.irk_init.irk = msg1.getIRK();
1381 if( BDAddressType::BDADDR_UNDEFINED != pairing_data.id_address_init.type ) {
1382 pairing_data.irk_init.id_address = pairing_data.id_address_init.address;
1383 }
1384 }
1385 } break;
1386
1387 case SMPPDUMsg::Opcode::IDENTITY_ADDRESS_INFORMATION:{/* Lecacy: 4; SC: 2 */
1388 // 3d
1389 // Public device or static random, complementing IRK SMPKeyDist::ID_KEY
1390 const SMPIdentAddrInfoMsg& msg1 = *static_cast<const SMPIdentAddrInfoMsg *>( &msg );
1392
1393 // self_is_responder == true: responder's IRK info (LL slave), else the initiator's (LL master)
1394 const bool self_is_responder = BTRole::Slave == btRole;
1395 const BDAddressAndType& responderAddress = self_is_responder ? msg_addr : adapter.getAddressAndType(); // msg_addr, b/c its the ID address
1396 const BDAddressAndType& initiatorAddress = self_is_responder ? adapter.getAddressAndType() : msg_addr; // while addressAndType may still be an rpa
1397
1398 if( !msg_from_initiator ) {
1399 // from responder (LL slave)
1400 // pairing_data.keys_resp_has |= SMPKeyType::ID_KEY;
1401 if( msg_addr != responderAddress ) {
1402 DBG_PRINT("BTDevice:hci:SMP.id: Responder ID Address Mismatch: msg %s != responder %s", msg1.toString().c_str(), responderAddress.toString().c_str());
1403 } else {
1404 pairing_data.id_address_resp = msg_addr;
1405 if( is_set(pairing_data.keys_resp_has, SMPKeyType::ID_KEY) ) {
1406 pairing_data.irk_resp.id_address = pairing_data.id_address_resp.address;
1407 }
1408 if( !msg_sent ) { // received
1409 updateIdentityAddress(pairing_data.id_address_resp, true /* sendEvent */); // update to resolved static/public address
1410 }
1411 }
1412 } else {
1413 // from initiator (LL master)
1414 // pairing_data.keys_init_has |= SMPKeyType::ID_KEY;
1415 if( msg_addr != initiatorAddress ) {
1416 DBG_PRINT("BTDevice:hci:SMP.id: Initiator ID Address Mismatch: msg %s != initiator %s", msg1.toString().c_str(), initiatorAddress.toString().c_str());
1417 } else {
1418 pairing_data.id_address_init = msg_addr;
1419 if( is_set(pairing_data.keys_init_has, SMPKeyType::ID_KEY) ) {
1420 pairing_data.irk_init.id_address = pairing_data.id_address_init.address;
1421 }
1422 if( !msg_sent ) { // received
1423 updateIdentityAddress(pairing_data.id_address_init, true /* sendEvent */); // update to resolved static/public address
1424 }
1425 }
1426 }
1427 } break;
1428
1429 case SMPPDUMsg::Opcode::SIGNING_INFORMATION: { /* Legacy: 5 (last value); SC: 3 */
1430 // 3e
1431 // CSRK
1432 const SMPSignInfoMsg & msg1 = *static_cast<const SMPSignInfoMsg *>( &msg );
1433 if( !msg_from_initiator ) {
1434 // from responder (LL slave)
1435 pairing_data.keys_resp_has |= SMPKeyType::SIGN_KEY;
1436
1437 pairing_data.csrk_resp.properties |= SMPSignatureResolvingKey::Property::RESPONDER;
1438 if( BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn ) {
1439 pairing_data.csrk_resp.properties |= SMPSignatureResolvingKey::Property::AUTH;
1440 }
1441 pairing_data.csrk_resp.csrk = msg1.getCSRK();
1442 } else {
1443 // from initiator (LL master)
1444 pairing_data.keys_init_has |= SMPKeyType::SIGN_KEY;
1445
1446 // pairing_data.csrk_init.properties |= SMPSignatureResolvingKeyInfo::Property::INITIATOR;
1447 if( BTSecurityLevel::ENC_AUTH <= pairing_data.sec_level_conn ) {
1448 pairing_data.csrk_init.properties |= SMPSignatureResolvingKey::Property::AUTH;
1449 }
1450 pairing_data.csrk_init.csrk = msg1.getCSRK();
1451 }
1452 check_pairing_complete = true;
1453 } break;
1454
1455 default:
1456 break;
1457 }
1458
1459 if( SMPPairingState::FAILED == old_pstate ) { // no recovery from FAILED via SMP
1460 pstate = SMPPairingState::FAILED;
1461 check_pairing_complete = false;
1462 }
1463
1464 // 4
1465 if( check_pairing_complete && checkPairingKeyDistributionComplete() ) {
1467 is_device_ready = true;
1468 pairing_data.is_pre_paired = true;
1469 pairing_data.sec_level_conn = l2cap_att->getBTSecurityLevel();
1470 }
1471
1473 if( old_pstate == pstate /* && old_pmode == pmode */ ) {
1474 jau::PLAIN_PRINT(false, "[%s] BTDevice:hci:SMP.4a: Unchanged: address%s",
1475 timestamp.c_str(),
1476 addressAndType.toString().c_str());
1477 } else {
1478 jau::PLAIN_PRINT(false, "[%s] BTDevice:hci:SMP.4b: Updated: address%s",
1479 timestamp.c_str(),
1480 addressAndType.toString().c_str());
1481 }
1482 jau::PLAIN_PRINT(false, "[%s] - state %s -> %s, mode %s -> %s, ready %d",
1483 timestamp.c_str(),
1484 to_string(old_pstate).c_str(), to_string(pstate).c_str(),
1485 to_string(old_pmode).c_str(), to_string(pmode).c_str(),
1486 is_device_ready);
1487 }
1488
1489 // 5
1490 if( old_pstate == pstate /* && old_pmode == pmode */ ) {
1491 // 5a
1492 if( jau::environment::get().debug ) {
1493 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1494 jau::PLAIN_PRINT(false, "[%s] BTDevice:hci:SMP.5a: %s", timestamp.c_str(),
1495 pairing_data.toString(adapter.dev_id, addressAndType, btRole).c_str());
1496 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1497 }
1498 return;
1499 }
1500
1501 if( is_device_ready && BTRole::Master == btRole &&
1502 pairing_data.sec_level_user >= BTSecurityLevel::ENC_ONLY ) {
1503 // Validate encryption and authentication requirements in server mode!
1504 if( ( pairing_data.sec_level_user == BTSecurityLevel::ENC_ONLY &&
1505 pairing_data.sec_level_conn < BTSecurityLevel::ENC_ONLY ) ||
1506
1507 ( pairing_data.sec_level_user >= BTSecurityLevel::ENC_AUTH &&
1508 pairing_data.sec_level_conn < BTSecurityLevel::ENC_AUTH )
1509 )
1510 {
1511 pstate = SMPPairingState::FAILED;
1512 is_device_ready = false;
1513 DBG_PRINT("BTDevice:hci:SMP:Sec-Failure: Requested Sec-Level %s > Actual %s",
1514 to_string(pairing_data.sec_level_user).c_str(),
1515 to_string(pairing_data.sec_level_conn).c_str());
1516 }
1517 }
1518
1519 // 5b
1520 pairing_data.mode = pmode;
1521 pairing_data.state = pstate;
1522
1523 if( jau::environment::get().debug ) {
1524 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1525 jau::PLAIN_PRINT(false, "[%s] BTDevice:hci:SMP.5b: %s", timestamp.c_str(),
1526 pairing_data.toString(adapter.dev_id, addressAndType, btRole).c_str());
1527 jau::PLAIN_PRINT(false, "[%s] ", timestamp.c_str());
1528 }
1529
1530 adapter.sendDevicePairingState(sthis, pstate, pmode, msg.getTimestamp());
1531
1532 if( is_device_ready ) {
1533 smp_events = 0;
1534 adapter.notifyPairingStageDone(sthis, msg.getTimestamp());
1535 std::thread dc(&BTDevice::processDeviceReady, this, sthis, msg.getTimestamp()); // @suppress("Invalid arguments")
1536 dc.detach();
1537 }
1538
1539 if( jau::environment::get().debug ) {
1540 jau::PLAIN_PRINT(false, "[%s] Debug: BTDevice:hci:SMP.5b: End", timestamp.c_str());
1541 jau::PLAIN_PRINT(false, "[%s] - %s", timestamp.c_str(), toString().c_str());
1542 }
1543 lock_pairing.unlock(); // unlock mutex before notify_all to avoid pessimistic re-block of notified wait() thread.
1544 cv_pairing_state_changed.notify_all();
1545}
1546
1547SMPKeyType BTDevice::getAvailableSMPKeys(const bool responder) const noexcept {
1549 if( responder ) {
1550 return pairing_data.keys_resp_has;
1551 } else {
1552 return pairing_data.keys_init_has;
1553 }
1554}
1555
1556bool BTDevice::setSMPKeyBin(const SMPKeyBin& bin) noexcept {
1557 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
1558
1559 if( !isValidInstance() ) {
1560 ERR_PRINT("Device invalid: %p", jau::to_hexstring((void*)this).c_str());
1561 return false;
1562 }
1563
1564 if( bin.getLocalAddrAndType() != getAdapter().getAddressAndType() ) {
1565 DBG_PRINT("SMPKeyBin::readAndApply: Local address mismatch: Has %s, SMPKeyBin %s: %s",
1566 getAdapter().getAddressAndType().toString().c_str(),
1567 bin.getLocalAddrAndType().toString().c_str(),
1568 bin.toString().c_str());
1569 return false;
1570 }
1571 if( bin.getRemoteAddrAndType() != getAddressAndType() ) {
1572 DBG_PRINT("SMPKeyBin::readAndApply: Remote address mismatch: Has %s, SMPKeyBin %s: %s",
1573 getAddressAndType().toString().c_str(),
1574 bin.getRemoteAddrAndType().toString().c_str(),
1575 bin.toString().c_str());
1576 return false;
1577 }
1578
1579 // Must be a valid SMPKeyBin instance and at least one LTK key if using encryption.
1580 // Also validates IRKs' id_address, if contained
1581 if( !bin.isValid() || ( BTSecurityLevel::NONE != bin.getSecLevel() && !bin.hasLTKInit() && !bin.hasLTKResp() ) ) {
1582 DBG_PRINT("BTDevice::setSMPKeyBin(): Apply SMPKeyBin failed, all invalid or sec level w/o LTK: %s, %s",
1583 bin.toString().c_str(), toString().c_str());
1584 return false;
1585 }
1586
1587 const BTRole btRoleAdapter = !btRole;
1588 if( btRoleAdapter != bin.getLocalRole() ) {
1589 DBG_PRINT("BTDevice::setSMPKeyBin(): Apply SMPKeyBin failed, local adapter role %s mismatch: %s, %s",
1590 to_string(btRoleAdapter).c_str(), bin.toString().c_str(), toString().c_str());
1591 return false;
1592 }
1593
1594 {
1595 if( SMPIOCapability::UNSET != pairing_data.io_cap_auto ||
1596 ( SMPPairingState::COMPLETED != pairing_data.state &&
1597 SMPPairingState::NONE != pairing_data.state ) )
1598 {
1599 DBG_PRINT("BTDevice::setSMPKeyBin: Failure, pairing in progress: %s, %s",
1600 pairing_data.toString(adapter.dev_id, addressAndType, btRole).c_str(), toString().c_str());
1601 return false;
1602 }
1603
1604 if( getConnected() ) {
1605 DBG_PRINT("BTDevice::setSMPKeyBin: Failure, device connected: %s", toString().c_str());
1606 return false;
1607 }
1608
1609 const BTSecurityLevel applySecLevel = BTSecurityLevel::NONE == bin.getSecLevel() ?
1611 if( !setConnSecurity(applySecLevel, SMPIOCapability::NO_INPUT_NO_OUTPUT) ) {
1612 DBG_PRINT("BTDevice::setSMPKeyBin: Setting security failed: Device Connected/ing: %s, %s", bin.toString().c_str(), toString().c_str());
1613 return false;
1614 }
1615 }
1616 pairing_data.use_sc = bin.uses_SC();
1617
1618 if( bin.hasLTKInit() ) {
1619 setLongTermKey( bin.getLTKInit() );
1620 }
1621 if( bin.hasLTKResp() ) {
1622 setLongTermKey( bin.getLTKResp() );
1623 }
1624
1625 if( bin.hasIRKInit() ) {
1626 setIdentityResolvingKey( bin.getIRKInit() );
1627 }
1628 if( bin.hasIRKResp() ) {
1629 setIdentityResolvingKey( bin.getIRKResp() );
1630 }
1631
1632 if( bin.hasCSRKInit() ) {
1633 setSignatureResolvingKey( bin.getCSRKInit() );
1634 }
1635 if( bin.hasCSRKResp() ) {
1636 setSignatureResolvingKey( bin.getCSRKResp() );
1637 }
1638
1639 if( bin.hasLKInit() ) {
1640 setLinkKey( bin.getLKInit() );
1641 }
1642 if( bin.hasLKResp() ) {
1643 setLinkKey( bin.getLKResp() );
1644 }
1645 DBG_PRINT("BTDevice::setSMPKeyBin.OK: %s", pairing_data.toString(adapter.dev_id, addressAndType, btRole).c_str());
1646 return true;
1647}
1648
1650 if( isConnected ) {
1651 ERR_PRINT("Already connected: %s", toString().c_str());
1653 }
1654 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
1655 if constexpr ( USE_LINUX_BT_SECURITY ) {
1656 const BTManagerRef& mngr = adapter.getManager();
1658
1659 // LTKs
1660 {
1662 if( is_set(pairing_data.keys_init_has, SMPKeyType::ENC_KEY) ) {
1663 ltks.push_back(pairing_data.ltk_init);
1664 }
1665 if( is_set(pairing_data.keys_resp_has, SMPKeyType::ENC_KEY) ) {
1666 ltks.push_back(pairing_data.ltk_resp);
1667 }
1668 if( ltks.size() > 0 ) {
1669 res = mngr->uploadLongTermKey(!btRole, adapter.dev_id, addressAndType, ltks);
1670 DBG_PRINT("BTDevice::uploadKeys.LTK[adapter %s]: %s", to_string(!btRole).c_str(), to_string(res).c_str());
1671 if( HCIStatusCode::SUCCESS != res ) {
1672 return res;
1673 }
1674 }
1675 }
1676
1677 // IRKs
1678 {
1680 if( is_set(pairing_data.keys_init_has, SMPKeyType::ID_KEY) ) {
1681 irks.push_back(pairing_data.irk_init);
1682 }
1683 if( is_set(pairing_data.keys_resp_has, SMPKeyType::ID_KEY) ) {
1684 irks.push_back(pairing_data.irk_resp);
1685 }
1686 if( irks.size() > 0 ) {
1687 res = mngr->uploadIdentityResolvingKey(adapter.dev_id, irks);
1688 DBG_PRINT("BTDevice::uploadKeys.IRK: %s", to_string(res).c_str());
1689 if( HCIStatusCode::SUCCESS != res ) {
1690 return res;
1691 }
1692 }
1693 }
1694
1696 // Not supported
1697 DBG_PRINT("BTDevice::uploadKeys: Upload LK for LE address not supported -> ignored: %s", toString().c_str());
1698 pairing_data.is_pre_paired = true;
1700 }
1701
1702 if( BTRole::Slave == btRole ) {
1703 // Remote device is slave (peripheral, responder), we are master (initiator)
1704 if( is_set(pairing_data.keys_init_has, SMPKeyType::LINK_KEY) ) {
1705 res = mngr->uploadLinkKey(adapter.dev_id, addressAndType, pairing_data.lk_init);
1706 DBG_PRINT("BTDevice::uploadKeys.LK[adapter master]: %s", to_string(res).c_str());
1707 }
1708 } else {
1709 // Remote device is master (initiator), we are slave (peripheral, responder)
1710 if( is_set(pairing_data.keys_resp_has, SMPKeyType::LINK_KEY) ) {
1711 res = mngr->uploadLinkKey(adapter.dev_id, addressAndType, pairing_data.lk_resp);
1712 DBG_PRINT("BTDevice::uploadKeys.LK[adapter slave]: %s", to_string(res).c_str());
1713 }
1714 }
1715 if( HCIStatusCode::SUCCESS == res ) {
1716 pairing_data.is_pre_paired = true;
1717 }
1718 return res;
1719 } else if constexpr ( SMP_SUPPORTED_BY_OS ) {
1721 } else {
1723 }
1724}
1725
1726SMPLongTermKey BTDevice::getLongTermKey(const bool responder) const noexcept {
1728 return responder ? pairing_data.ltk_resp : pairing_data.ltk_init;
1729}
1730
1731void BTDevice::setLongTermKey(const SMPLongTermKey& ltk) noexcept {
1733 if( ltk.isResponder() ) {
1734 pairing_data.ltk_resp = ltk;
1735 pairing_data.keys_resp_has |= SMPKeyType::ENC_KEY;
1736 pairing_data.keys_resp_exp |= SMPKeyType::ENC_KEY;
1737 } else {
1738 pairing_data.ltk_init = ltk;
1739 pairing_data.keys_init_has |= SMPKeyType::ENC_KEY;
1740 pairing_data.keys_init_exp |= SMPKeyType::ENC_KEY;
1741 }
1742}
1743
1746 return responder ? pairing_data.irk_resp : pairing_data.irk_init;
1747}
1748
1751 if( irk.isResponder() ) {
1752 pairing_data.irk_resp = irk;
1753 pairing_data.keys_resp_has |= SMPKeyType::ID_KEY;
1754 pairing_data.keys_resp_exp |= SMPKeyType::ID_KEY;
1755 pairing_data.id_address_resp = BDAddressAndType(irk.id_address, BDAddressType::BDADDR_LE_PUBLIC);
1756 } else {
1757 pairing_data.irk_init = irk;
1758 pairing_data.keys_init_has |= SMPKeyType::ID_KEY;
1759 pairing_data.keys_init_exp |= SMPKeyType::ID_KEY;
1760 pairing_data.id_address_init = BDAddressAndType(irk.id_address, BDAddressType::BDADDR_LE_PUBLIC);
1761 }
1762}
1763
1764bool BTDevice::matches_irk(const BDAddressAndType& rpa) noexcept {
1765 // self_is_responder == true: responder's IRK info (LL slave), else the initiator's (LL master)
1766 const bool self_is_responder = BTRole::Slave == btRole;
1767 if( is_set(self_is_responder ? pairing_data.keys_resp_has : pairing_data.keys_init_has, SMPKeyType::ID_KEY) ) {
1768 SMPIdentityResolvingKey irk = getIdentityResolvingKey(self_is_responder);
1769 return irk.matches(rpa.address); // irk.id_address == this->addressAndType
1770 } else {
1771 return false;
1772 }
1773}
1774
1777 return responder ? pairing_data.csrk_resp : pairing_data.csrk_init;
1778}
1779
1782 if( csrk.isResponder() ) {
1783 pairing_data.csrk_resp = csrk;
1784 pairing_data.keys_resp_has |= SMPKeyType::SIGN_KEY;
1785 pairing_data.keys_resp_exp |= SMPKeyType::SIGN_KEY;
1786 } else {
1787 pairing_data.csrk_init = csrk;
1788 pairing_data.keys_init_has |= SMPKeyType::SIGN_KEY;
1789 pairing_data.keys_init_exp |= SMPKeyType::SIGN_KEY;
1790 }
1791}
1792
1793SMPLinkKey BTDevice::getLinkKey(const bool responder) const noexcept {
1795 return responder ? pairing_data.lk_resp : pairing_data.lk_init;
1796}
1797
1798void BTDevice::setLinkKey(const SMPLinkKey& lk) noexcept {
1800 if( lk.isResponder() ) {
1801 pairing_data.lk_resp = lk;
1802 pairing_data.keys_resp_has |= SMPKeyType::LINK_KEY;
1803 pairing_data.keys_resp_exp |= SMPKeyType::LINK_KEY;
1804 } else {
1805 pairing_data.lk_init = lk;
1806 pairing_data.keys_init_has |= SMPKeyType::LINK_KEY;
1807 pairing_data.keys_init_exp |= SMPKeyType::LINK_KEY;
1808 }
1809}
1810
1812 jau::sc_atomic_critical sync(sync_data);
1813 return pairing_data.sec_level_conn;
1814}
1815
1817 jau::sc_atomic_critical sync(sync_data);
1818 return pairing_data.io_cap_conn;
1819}
1820
1821void BTDevice::validateConnectedSecParam(BTSecurityLevel& res_sec_level, SMPIOCapability& res_io_cap) const noexcept {
1822 const BTSecurityLevel sec_level_conn = pairing_data.sec_level_conn;
1823 const SMPIOCapability io_cap_conn = pairing_data.io_cap_conn;
1824
1825 // Connection is already established, hence io_cap immutable
1826 res_io_cap = io_cap_conn;
1827
1828 if( sec_level_conn != BTSecurityLevel::UNSET ) {
1829 DBG_PRINT("BTDevice::validateConnectedSecParam: dev_id %u, sec_lvl %s, io_cap %s (preset)", adapter.dev_id,
1830 to_string(sec_level_conn).c_str(),
1831 to_string(io_cap_conn).c_str());
1832 res_sec_level = sec_level_conn;
1833 return;
1834 }
1835 const BTSecurityLevel sec_level_user = pairing_data.sec_level_user;
1836 const SMPIOCapability io_cap_user = pairing_data.io_cap_user;
1837
1838 const bool responderLikesEncryption = pairing_data.res_requested_sec || is_set(le_features, LE_Features::LE_Encryption);
1839 if( BTSecurityLevel::UNSET != sec_level_user ) {
1840 // Prio set and validated user values even over remote device caps
1841 res_sec_level = sec_level_user;
1842 } else if( responderLikesEncryption ) {
1843 // No preset, but remote likes encryption
1844 if( hasSMPIOCapabilityAnyIO( io_cap_conn ) ) {
1845 if( adapter.hasSecureConnections() ) {
1846 res_sec_level = BTSecurityLevel::ENC_AUTH_FIPS;
1847 } else {
1848 res_sec_level = BTSecurityLevel::ENC_AUTH;
1849 }
1850 } else {
1851 res_sec_level = BTSecurityLevel::ENC_ONLY;
1852 }
1853 } else {
1854 res_sec_level = BTSecurityLevel::NONE;
1855 }
1856
1857 DBG_PRINT("BTDevice::validateConnectedSecParam: dev_id %u, user[sec_lvl %s, io_cap %s], conn[sec_lvl %s, io_cap %s] -> sec_lvl %s, io_cap %s",
1858 adapter.dev_id,
1859 to_string(sec_level_user).c_str(), to_string(io_cap_user).c_str(),
1860 to_string(sec_level_conn).c_str(), to_string(io_cap_conn).c_str(),
1861 to_string(res_sec_level).c_str(), to_string(res_io_cap).c_str());
1862}
1863
1865 BTSecurityLevel& res_sec_level, SMPIOCapability& res_io_cap) noexcept
1866{
1867 if( BTSecurityLevel::UNSET < sec_level ) {
1868 if( BTSecurityLevel::NONE == sec_level ||
1869 BTSecurityLevel::ENC_ONLY == sec_level )
1870 {
1871 // No authentication, maybe encryption
1872 res_sec_level = sec_level;
1874 } else if( hasSMPIOCapabilityAnyIO( io_cap ) ) {
1875 // Authentication w/ IO
1876 res_sec_level = sec_level;
1877 res_io_cap = io_cap;
1878 } else if( SMPIOCapability::NO_INPUT_NO_OUTPUT == io_cap ) {
1879 // Fall back: auto -> encryption only
1880 res_sec_level = BTSecurityLevel::ENC_ONLY;
1882 } else {
1883 // Use auth w/ SMPIOCapability::UNSET
1884 res_sec_level = sec_level;
1885 res_io_cap = io_cap;
1886 }
1887 } else {
1888 res_sec_level = BTSecurityLevel::UNSET;
1889 res_io_cap = io_cap;
1890 }
1891 DBG_PRINT("BTDevice::validateSecurityParams: lvl %s -> %s, io %s -> %s",
1892 to_string(sec_level).c_str(), to_string(res_sec_level).c_str(),
1893 to_string(io_cap).c_str(), to_string(res_io_cap).c_str());
1894}
1895
1896bool BTDevice::setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap) noexcept {
1897 if( !isValidInstance() ) {
1898 ERR_PRINT("Device invalid: %p", jau::to_hexstring((void*)this).c_str());
1899 return false;
1900 }
1901 if( isConnected || allowDisconnect ) {
1902 ERR_PRINT("Invalid State: Connected: dev_id %u, lvl %s, io %s failed, %s",
1903 adapter.dev_id, to_string(sec_level).c_str(),
1904 to_string(io_cap).c_str(), toString().c_str());
1905 return false;
1906 }
1908
1909 if( !pairing_data.is_pre_paired ) {
1910 validateSecParam(sec_level, io_cap, pairing_data.sec_level_user, pairing_data.io_cap_user);
1911 }
1912 pairing_data.io_cap_auto = SMPIOCapability::UNSET; // disable auto
1913
1914 DBG_PRINT("BTDevice::setConnSecurity: dev_id %u, pre-paired %d: lvl %s -> %s, io %s -> %s, %s",
1915 adapter.dev_id, pairing_data.is_pre_paired,
1916 to_string(sec_level).c_str(), to_string(pairing_data.sec_level_user).c_str(),
1917 to_string(io_cap).c_str(), to_string(pairing_data.io_cap_user).c_str(),
1918 toString().c_str());
1919
1920 return true;
1921}
1922
1923bool BTDevice::setConnSecurityAuto(const SMPIOCapability iocap_auto) noexcept {
1924 if( !isValidInstance() ) {
1925 ERR_PRINT("Device invalid: %p", jau::to_hexstring((void*)this).c_str());
1926 return false;
1927 }
1928 if( isConnected || allowDisconnect ) {
1929 ERR_PRINT("Invalid State: Connected: dev_id %d, io %s failed, %s",
1930 adapter.dev_id, to_string(iocap_auto).c_str(), toString().c_str());
1931 return false;
1932 }
1933 if( pairing_data.is_pre_paired ) {
1934 DBG_PRINT("BTDevice::setConnSecurityAuto: io %s failed, is pre-paired: %s",
1935 to_string(iocap_auto).c_str(),
1936 toString().c_str());
1937 return false;
1938 }
1939 if( BTSecurityLevel::UNSET != pairing_data.sec_level_user ||
1940 SMPIOCapability::UNSET != pairing_data.io_cap_user )
1941 {
1942 DBG_PRINT("BTDevice::setConnSecurityAuto: io %s failed, user connection sec_level %s or io %s set %s",
1943 to_string(iocap_auto).c_str(),
1944 to_string(pairing_data.sec_level_user).c_str(),
1945 to_string(pairing_data.io_cap_user).c_str(),
1946 toString().c_str());
1947 return false;
1948 }
1949 if( BTRole::Master == getRole() ) {
1950 DBG_PRINT("BTDevice::setConnSecurityAuto: Not allowed with remote device in master mode: %s",
1951 to_string(iocap_auto).c_str(), toString().c_str());
1952 return false;
1953 }
1954
1956 const bool res = true;
1957 pairing_data.io_cap_auto = iocap_auto;
1958 DBG_PRINT("BTDevice::setConnSecurityAuto: result %d: io %s, %s", res,
1959 to_string(iocap_auto).c_str(), toString().c_str());
1960 return res;
1961}
1962
1964 jau::sc_atomic_critical sync(sync_data);
1965 return SMPIOCapability::UNSET != pairing_data.io_cap_auto;
1966}
1967
1968HCIStatusCode BTDevice::setPairingPINCode(const std::string& pinCode) noexcept {
1969 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
1970
1973 {
1974 WARN_PRINT("BTDevice:mgmt:SMP: PINCODE '%s', state %s, wrong state", pinCode.c_str(), to_string(pairing_data.state).c_str());
1975 }
1976 if constexpr ( USE_LINUX_BT_SECURITY ) {
1977 const BTManagerRef& mngr = adapter.getManager();
1978 MgmtStatus res = mngr->userPINCodeReply(adapter.dev_id, addressAndType, pinCode);
1979 DBG_PRINT("BTDevice:mgmt:SMP: PINCODE '%s', state %s, result %s",
1980 pinCode.c_str(), to_string(pairing_data.state).c_str(), to_string(res).c_str());
1982 } else if constexpr ( SMP_SUPPORTED_BY_OS ) {
1984 } else {
1986 }
1987}
1988
1990 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
1991
1994 {
1995 WARN_PRINT("BTDevice:mgmt:SMP: PINCODE_NEGATIVE, state %s, wrong state", to_string(pairing_data.state).c_str());
1996 }
1997 if constexpr ( USE_LINUX_BT_SECURITY ) {
1998 const BTManagerRef& mngr = adapter.getManager();
1999 MgmtStatus res = mngr->userPINCodeNegativeReply(adapter.dev_id, addressAndType);
2000 DBG_PRINT("BTDevice:mgmt:SMP: PINCODE NEGATIVE, state %s, result %s",
2001 to_string(pairing_data.state).c_str(), to_string(res).c_str());
2003 } else if constexpr ( SMP_SUPPORTED_BY_OS ) {
2005 } else {
2007 }
2008}
2009
2010HCIStatusCode BTDevice::setPairingPasskey(const uint32_t passkey) noexcept {
2011 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
2012
2015 {
2016 WARN_PRINT("BTDevice:mgmt:SMP: PASSKEY '%u', state %s, wrong state", passkey, to_string(pairing_data.state).c_str());
2017 }
2018 if constexpr ( USE_LINUX_BT_SECURITY ) {
2019 const BTManagerRef& mngr = adapter.getManager();
2020 MgmtStatus res = mngr->userPasskeyReply(adapter.dev_id, addressAndType, passkey);
2021 DBG_PRINT("BTDevice:mgmt:SMP: PASSKEY '%u', state %s, result %s",
2022 passkey, to_string(pairing_data.state).c_str(), to_string(res).c_str());
2024 } else if constexpr ( SMP_SUPPORTED_BY_OS ) {
2026 } else {
2028 }
2029}
2030
2032 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
2033
2036 {
2037 WARN_PRINT("BTDevice:mgmt:SMP: PASSKEY_NEGATIVE, state %s, wrong state", to_string(pairing_data.state).c_str());
2038 }
2039 if constexpr ( USE_LINUX_BT_SECURITY ) {
2040 const BTManagerRef& mngr = adapter.getManager();
2041 MgmtStatus res = mngr->userPasskeyNegativeReply(adapter.dev_id, addressAndType);
2042 DBG_PRINT("BTDevice:mgmt:SMP: PASSKEY NEGATIVE, state %s, result %s",
2043 to_string(pairing_data.state).c_str(), to_string(res).c_str());
2045 } else if constexpr ( SMP_SUPPORTED_BY_OS ) {
2047 } else {
2049 }
2050}
2051
2053 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
2054
2057 {
2058 WARN_PRINT("BTDevice:mgmt:SMP: CONFIRM '%d', state %s, wrong state", positive, to_string(pairing_data.state).c_str());
2059 }
2060 if constexpr ( USE_LINUX_BT_SECURITY ) {
2061 const BTManagerRef& mngr = adapter.getManager();
2062 MgmtStatus res = mngr->userConfirmReply(adapter.dev_id, addressAndType, positive);
2063 DBG_PRINT("BTDevice:mgmt:SMP: CONFIRM '%d', state %s, result %s",
2064 positive, to_string(pairing_data.state).c_str(), to_string(res).c_str());
2066 } else if constexpr ( SMP_SUPPORTED_BY_OS ) {
2068 } else {
2070 }
2071}
2072
2074 jau::sc_atomic_critical sync(sync_data);
2075 return pairing_data.mode;
2076}
2077
2079 jau::sc_atomic_critical sync(sync_data);
2080 return pairing_data.state;
2081}
2082
2083void BTDevice::clearSMPStates(const bool connected) noexcept {
2084 // Issued at ctor(), manual unpair() and notifyDisconnect()
2085 // notifyDisconnect() will be called at all times, even if disconnect() fails!
2086 const std::unique_lock<std::recursive_mutex> lock_pairing(mtx_pairing); // RAII-style acquire and relinquish via destructor
2087
2088 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
2089 DBG_PRINT("BTDevice::clearSMPStates(connected %d): %s", connected, toString().c_str());
2090
2091 if( !connected ) {
2092 // needs to survive connected, or will be set right @ connected
2093 pairing_data.io_cap_user = SMPIOCapability::UNSET;
2094 pairing_data.io_cap_conn = SMPIOCapability::UNSET;
2095 pairing_data.sec_level_user = BTSecurityLevel::UNSET;
2096 // Keep alive: pairing_data.ioCap_auto = SMPIOCapability::UNSET;
2097 }
2098 pairing_data.sec_level_conn = BTSecurityLevel::UNSET;
2099
2100 pairing_data.is_pre_paired = false;
2101 pairing_data.state = SMPPairingState::NONE;
2102 pairing_data.mode = PairingMode::NONE;
2103 pairing_data.res_requested_sec = false;
2104 pairing_data.use_sc = false;
2105 pairing_data.encryption_enabled = false;
2106
2107 pairing_data.passKey_resp = 0;
2108
2109 pairing_data.authReqs_resp = SMPAuthReqs::NONE;
2110 pairing_data.ioCap_resp = SMPIOCapability::NO_INPUT_NO_OUTPUT;
2111 pairing_data.oobFlag_resp = SMPOOBDataFlag::OOB_AUTH_DATA_NOT_PRESENT;
2112 pairing_data.maxEncsz_resp = 0;
2113 pairing_data.keys_resp_exp = SMPKeyType::NONE;
2114 pairing_data.keys_resp_has = SMPKeyType::NONE;
2115 pairing_data.ltk_resp.clear();
2116 pairing_data.irk_resp.clear();
2117 pairing_data.id_address_resp.clear();
2118 pairing_data.csrk_resp.clear();
2119 pairing_data.lk_resp.clear();
2120
2121 pairing_data.authReqs_init = SMPAuthReqs::NONE;
2122 pairing_data.ioCap_init = SMPIOCapability::NO_INPUT_NO_OUTPUT;
2123 pairing_data.oobFlag_init = SMPOOBDataFlag::OOB_AUTH_DATA_NOT_PRESENT;
2124 pairing_data.maxEncsz_init = 0;
2125 pairing_data.keys_init_exp = SMPKeyType::NONE;
2126 pairing_data.keys_init_has = SMPKeyType::NONE;
2127 pairing_data.ltk_init.clear();
2128 pairing_data.irk_init.clear();
2129 pairing_data.id_address_init.clear();
2130 pairing_data.csrk_init.clear();
2131 pairing_data.lk_init.clear();
2132}
2133
2134void BTDevice::disconnectSMP(const int caller) noexcept {
2135 if constexpr ( SMP_SUPPORTED_BY_OS ) {
2136 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_smpHandler);
2137 if( nullptr != smpHandler ) {
2138 DBG_PRINT("BTDevice::disconnectSMP: start (has smpHandler, caller %d)", caller);
2139 smpHandler->disconnect(false /* disconnect_device */, false /* ioerr_cause */);
2140 } else {
2141 DBG_PRINT("BTDevice::disconnectSMP: start (nil smpHandler, caller %d)", caller);
2142 }
2143 smpHandler = nullptr;
2144 DBG_PRINT("BTDevice::disconnectSMP: end");
2145 } else {
2146 (void)caller;
2147 }
2148}
2149
2150bool BTDevice::connectSMP(std::shared_ptr<BTDevice> sthis, const BTSecurityLevel sec_level) noexcept {
2151 if constexpr ( SMP_SUPPORTED_BY_OS ) {
2152 if( !isConnected || !allowDisconnect) {
2153 ERR_PRINT("connectSMP(%u): Device not connected: %s", sec_level, toString().c_str());
2154 return false;
2155 }
2156
2158 DBG_PRINT("BTDevice::connectSMP(%u): SMP Not supported by OS (1): %s", sec_level, toString().c_str());
2159 return false;
2160 }
2161
2162 if( BTSecurityLevel::NONE >= sec_level ) {
2163 return false;
2164 }
2165
2166 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_gattHandler);
2167 if( nullptr != smpHandler ) {
2168 if( smpHandler->isConnected() ) {
2169 return smpHandler->establishSecurity(sec_level);
2170 }
2171 smpHandler = nullptr;
2172 }
2173
2174 smpHandler = std::make_shared<SMPHandler>(sthis);
2175 if( !smpHandler->isConnected() ) {
2176 ERR_PRINT("Connection failed");
2177 smpHandler = nullptr;
2178 return false;
2179 }
2180 return smpHandler->establishSecurity(sec_level);
2181 } else {
2182 DBG_PRINT("BTDevice::connectSMP: SMP Not supported by OS (0): %s", toString().c_str());
2183 (void)sthis;
2184 (void)sec_level;
2185 return false;
2186 }
2187}
2188
2189void BTDevice::disconnectGATT(const int caller) noexcept {
2190 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_gattHandler);
2191 if( nullptr != gattHandler ) {
2192 DBG_PRINT("BTDevice::disconnectGATT: start (has gattHandler, caller %d)", caller);
2193 gattHandler->disconnect(false /* disconnect_device */, false /* ioerr_cause */);
2194 } else {
2195 DBG_PRINT("BTDevice::disconnectGATT: start (nil gattHandler, caller %d)", caller);
2196 }
2197 gattHandler = nullptr;
2198 DBG_PRINT("BTDevice::disconnectGATT: end");
2199}
2200
2201bool BTDevice::connectGATT(const std::shared_ptr<BTDevice>& sthis) noexcept {
2202 if( !isConnected || !allowDisconnect) {
2203 ERR_PRINT("Device not connected: %s", toString().c_str());
2204 return false;
2205 }
2206 if( !l2cap_att->is_open() ) {
2207 ERR_PRINT("L2CAP not open: %s", toString().c_str());
2208 return false;
2209 }
2210
2211 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_gattHandler);
2212 if( nullptr != gattHandler ) {
2213 if( gattHandler->isConnected() ) {
2214 return true;
2215 }
2216 gattHandler = nullptr;
2217 }
2218
2219 DBG_PRINT("BTDevice::connectGATT: Start: %s", toString().c_str());
2220
2221 // GATT MTU only consumes around 20ms - 100ms
2222 gattHandler = std::make_shared<BTGattHandler>(sthis, *l2cap_att, supervision_timeout);
2223 if( !gattHandler->isConnected() ) {
2224 ERR_PRINT2("Connection failed");
2225 gattHandler = nullptr;
2226 return false;
2227 } else if ( BTRole::Master == btRole ) {
2228 DBG_PRINT("BTDevice::connectGATT: Local GATT Server: Done: %s", toString().c_str());
2229 } else {
2230 DBG_PRINT("BTDevice::connectGATT: Local GATT Client: Done: %s", toString().c_str());
2231 }
2232 return true;
2233}
2234
2235std::shared_ptr<BTGattHandler> BTDevice::getGattHandler() noexcept {
2236 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_gattHandler);
2237 return gattHandler;
2238}
2239
2241 std::shared_ptr<BTGattHandler> gh = getGattHandler();
2242 if( nullptr == gh ) {
2243 ERR_PRINT("GATTHandler nullptr: %s", toString().c_str());
2245 }
2246 if( BTRole::Slave != getRole() ) {
2247 // Remote device is not a slave (peripheral, responder) - hence no GATT services
2248 ERR_PRINT("Remote device not a GATT server: ", toString().c_str());
2250 }
2251
2252 bool gatt_already_init = false;
2253 if( !gh->initClientGatt(gh, gatt_already_init) ) {
2254 ERR_PRINT2("Client GATT Initialization failed");
2255 return jau::darray<BTGattServiceRef>(); // return zero size
2256 }
2257 if( gatt_already_init ) {
2258 return gh->getServices(); // copy previous discovery result
2259 }
2260
2261 GattServiceList_t result = gh->getServices(); // copy
2262 if( result.size() == 0 ) { // nothing discovered, actually a redundant check done @ BTGattHandler::initClientGatt() 1st
2263 ERR_PRINT2("No primary services discovered");
2264 return jau::darray<BTGattServiceRef>(); // return zero size
2265 }
2266
2267 // discovery success, parse GenericAccess
2268 std::shared_ptr<GattGenericAccessSvc> gattGenericAccess = gh->getGenericAccess();
2269 if( nullptr == gattGenericAccess ) {
2270 // no GenericAccess discovered, actually a redundant check done @ BTGattHandler::initClientGatt() 1st
2271 ERR_PRINT2("No GenericAccess: %s", toString().c_str());
2272 return jau::darray<BTGattServiceRef>(); // return zero size
2273 }
2274
2275 const uint64_t ts = jau::getCurrentMilliseconds();
2276 EIRDataType updateMask = update(*gattGenericAccess, ts);
2277 DBG_PRINT("BTDevice::getGattServices: GenericAccess updated %s:\n %s\n -> %s",
2278 to_string(updateMask).c_str(), gattGenericAccess->toString().c_str(), toString().c_str());
2279 if( EIRDataType::NONE != updateMask ) {
2280 std::shared_ptr<BTDevice> sharedInstance = getSharedInstance();
2281 if( nullptr == sharedInstance ) {
2282 ERR_PRINT("Device unknown to adapter and not tracked: %s", toString().c_str());
2283 } else {
2284 adapter.sendDeviceUpdated("getGattServices", sharedInstance, ts, updateMask);
2285 }
2286 }
2287 return result; // return the copy, copy elision shall be used
2288}
2289
2290std::shared_ptr<GattGenericAccessSvc> BTDevice::getGattGenericAccess() {
2291 std::shared_ptr<BTGattHandler> gh = getGattHandler();
2292 if( nullptr == gh ) {
2293 ERR_PRINT("GATTHandler nullptr");
2294 return nullptr;
2295 }
2296 return gh->getGenericAccess();
2297}
2298
2300 const jau::darray<std::shared_ptr<BTGattService>> & services = getGattServices(); // reference of the GATTHandler's list
2301 for(const BTGattServiceRef& s : services) {
2302 if ( nullptr != s && service_uuid.equivalent( *(s->type) ) ) {
2303 return s;
2304 }
2305 }
2306 return nullptr;
2307}
2308
2309BTGattCharRef BTDevice::findGattChar(const jau::uuid_t& service_uuid, const jau::uuid_t& char_uuid) noexcept {
2310 BTGattServiceRef service = findGattService(service_uuid);
2311 if( nullptr == service ) {
2312 return nullptr;
2313 }
2314 return service->findGattChar(char_uuid);
2315}
2316
2318 const jau::darray<std::shared_ptr<BTGattService>> & services = getGattServices(); // reference of the GATTHandler's list
2319 for(const BTGattServiceRef& s : services) {
2320 if ( nullptr != s ) {
2321 BTGattCharRef c = s->findGattChar(char_uuid);
2322 if ( nullptr != c ) {
2323 return c;
2324 }
2325 }
2326 }
2327 return nullptr;
2328}
2329
2330bool BTDevice::sendNotification(const uint16_t char_value_handle, const jau::TROOctets & value) noexcept {
2331 if( !isValidInstance() ) {
2332 ERR_PRINT("Device invalid: %p", jau::to_hexstring((void*)this).c_str());
2333 return false;
2334 }
2335 std::shared_ptr<BTGattHandler> gh = getGattHandler();
2336 if( nullptr == gh || !gh->isConnected() ) {
2337 WARN_PRINT("GATTHandler not connected -> disconnected on %s", toString().c_str());
2338 return false;
2339 }
2340 return gh->sendNotification(char_value_handle, value);
2341}
2342
2343bool BTDevice::sendIndication(const uint16_t char_value_handle, const jau::TROOctets & value) noexcept {
2344 if( !isValidInstance() ) {
2345 ERR_PRINT("Device invalid: %p", jau::to_hexstring((void*)this).c_str());
2346 return false;
2347 }
2348 std::shared_ptr<BTGattHandler> gh = getGattHandler();
2349 if( nullptr == gh || !gh->isConnected() ) {
2350 WARN_PRINT("GATTHandler not connected -> disconnected on %s", toString().c_str());
2351 return false;
2352 }
2353 return gh->sendIndication(char_value_handle, value);
2354}
2355
2356
2357bool BTDevice::pingGATT() noexcept {
2358 std::shared_ptr<BTGattHandler> gh = getGattHandler();
2359 if( nullptr == gh || !gh->isConnected() ) {
2360 jau::INFO_PRINT("BTDevice::pingGATT: GATTHandler not connected -> disconnected on %s", toString().c_str());
2362 return false;
2363 }
2364 return gh->ping();
2365}
2366
2368 std::shared_ptr<BTGattHandler> gatt = getGattHandler();
2369 if( nullptr == gatt ) {
2370 ERR_PRINT("Device's GATTHandle not connected: %s", toString().c_str());
2371 return false;
2372 }
2373 return gatt->addCharListener(l);
2374}
2375
2377 std::shared_ptr<BTGattHandler> gatt = getGattHandler();
2378 if( nullptr == gatt ) {
2379 ERR_PRINT("Device's GATTHandle not connected: %s", toString().c_str());
2380 return false;
2381 }
2382 return gatt->addCharListener(l, d);
2383}
2384
2386 std::shared_ptr<BTGattHandler> gatt = getGattHandler();
2387 if( nullptr == gatt ) {
2388 // OK to have GATTHandler being shutdown @ disable
2389 DBG_PRINT("Device's GATTHandle not connected: %s", toString().c_str());
2390 return false;
2391 }
2392 return gatt->removeCharListener(l);
2393}
2394
2396 std::shared_ptr<BTGattHandler> gatt = getGattHandler();
2397 if( nullptr == gatt ) {
2398 // OK to have GATTHandler being shutdown @ disable
2399 DBG_PRINT("Device's GATTHandle not connected: %s", toString().c_str());
2400 return false;
2401 }
2402 return gatt->removeAllAssociatedCharListener( associatedCharacteristic );
2403}
2404
2406 std::shared_ptr<BTGattHandler> gatt = getGattHandler();
2407 if( nullptr == gatt ) {
2408 // OK to have GATTHandler being shutdown @ disable
2409 DBG_PRINT("Device's GATTHandle not connected: %s", toString().c_str());
2410 return false;
2411 }
2412 return gatt->removeAllAssociatedCharListener( associatedCharacteristic );
2413}
2414
2416 std::shared_ptr<BTGattHandler> gatt = getGattHandler();
2417 if( nullptr == gatt ) {
2418 // OK to have GATTHandler being shutdown @ disable
2419 DBG_PRINT("Device's GATTHandle not connected: %s", toString().c_str());
2420 return 0;
2421 }
2422 return gatt->removeAllCharListener();
2423}
2424
2426 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_connect); // RAII-style acquire and relinquish via destructor
2427
2428 if( !isConnected ) { // should not happen
2430 }
2431
2432 if( 0 == hciConnHandle ) {
2434 }
2435
2436 if( !adapter.isPowered() ) { // isValid() && hci.isOpen() && POWERED
2437 return HCIStatusCode::NOT_POWERED; // powered-off
2438 }
2439
2440 HCIHandler &hci = adapter.getHCI();
2441 HCIStatusCode res = hci.le_read_phy(hciConnHandle, addressAndType, resTx, resRx);
2442 if( HCIStatusCode::SUCCESS == res ) {
2443 le_phy_tx = resTx;
2444 le_phy_rx = resRx;
2445 }
2446 return res;
2447}
2448
2450 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_connect); // RAII-style acquire and relinquish via destructor
2451
2452 if( !isConnected ) { // should not happen
2454 }
2455
2456 if( 0 == hciConnHandle ) {
2458 }
2459
2460 if( !adapter.isPowered() ) { // isValid() && hci.isOpen() && POWERED
2461 return HCIStatusCode::NOT_POWERED; // powered-off
2462 }
2463
2464 HCIHandler &hci = adapter.getHCI();
2465 return hci.le_set_phy(hciConnHandle, addressAndType, Tx, Rx);
2466}
2467
2468void BTDevice::notifyDisconnected() noexcept {
2469 // coming from disconnect callback, ensure cleaning up!
2470 DBG_PRINT("BTDevice::notifyDisconnected: handle %s -> zero, %s",
2471 jau::to_hexstring(hciConnHandle).c_str(), toString().c_str());
2472 allowDisconnect = false;
2473 supervision_timeout = 0;
2474 isConnected = false;
2475 hciConnHandle = 0;
2476 smp_events = 0;
2477 unpair(); // -> clearSMPStates(false /* connected */);
2478 disconnectGATT(1);
2479 disconnectSMP(1);
2480 l2cap_att->close();
2481 // clearData(); to be performed after notifying listener and if !isConnSecurityAutoEnabled()
2482}
2483
2484void BTDevice::sendMgmtEvDeviceDisconnected(std::unique_ptr<MgmtEvent> evt) noexcept {
2485 adapter.mgmtEvDeviceDisconnectedHCI(*evt);
2486}
2487
2489 // Avoid disconnect re-entry lock-free
2490 bool expConn = true; // C++11, exp as value since C++20
2491 if( !allowDisconnect.compare_exchange_strong(expConn, false) ) {
2492 // Not connected or disconnect already in process.
2493 DBG_PRINT("BTDevice::disconnect: Not connected: isConnected %d/%d, reason 0x%X (%s), gattHandler %d, hciConnHandle %s",
2494 allowDisconnect.load(), isConnected.load(),
2495 static_cast<uint8_t>(reason), to_string(reason).c_str(),
2496 (nullptr != gattHandler), jau::to_hexstring(hciConnHandle).c_str());
2498 }
2499 if( !isConnected ) { // should not happen
2500 WARN_PRINT("allowConnect true -> false, but !isConnected on %s", toString().c_str());
2502 }
2503
2504 // Disconnect GATT and SMP before device, keeping reversed initialization order intact if possible.
2505 // This outside mtx_connect, keeping same mutex lock order intact as well
2506 disconnectGATT(0);
2507 disconnectSMP(0);
2508
2509 // Lock to avoid other threads connecting while disconnecting
2510 const std::lock_guard<std::recursive_mutex> lock_conn(mtx_connect); // RAII-style acquire and relinquish via destructor
2511
2512 WORDY_PRINT("BTDevice::disconnect: Start: isConnected %d/%d, reason 0x%X (%s), gattHandler %d, hciConnHandle %s",
2513 allowDisconnect.load(), isConnected.load(),
2514 static_cast<uint8_t>(reason), to_string(reason).c_str(),
2515 (nullptr != gattHandler), jau::to_hexstring(hciConnHandle).c_str());
2516
2517 HCIHandler &hci = adapter.getHCI();
2519
2520 if( 0 == hciConnHandle ) {
2522 goto exit;
2523 }
2524
2525 if( !adapter.isPowered() ) { // isValid() && hci.isOpen() && POWERED
2526 WARN_PRINT("Adapter not powered: %s, %s", adapter.toString().c_str(), toString().c_str());
2527 res = HCIStatusCode::NOT_POWERED; // powered-off
2528 goto exit;
2529 }
2530
2531 res = hci.disconnect(hciConnHandle, addressAndType, reason);
2532 if( HCIStatusCode::SUCCESS != res ) {
2533 ERR_PRINT("status %s, handle 0x%X, isConnected %d/%d: errno %d %s on %s",
2534 to_string(res).c_str(), hciConnHandle.load(),
2535 allowDisconnect.load(), isConnected.load(),
2536 errno, strerror(errno),
2537 toString().c_str());
2538 }
2539
2540exit:
2541 {
2542 // Start resolving from scratch
2543 HCIStatusCode res2 = hci.le_del_from_resolv_list(addressAndType);
2544 if( HCIStatusCode::SUCCESS != res2 ) {
2545 jau::INFO_PRINT("BTDevice::disconnect: DEL FROM RESOLV LIST failed %s for %s",
2546 to_string(res2).c_str(), toString().c_str());
2547 }
2548 }
2549
2550 if( HCIStatusCode::SUCCESS != res ) {
2551 // In case of an already pulled or disconnected HCIHandler (e.g. power-off)
2552 // or in case the hci->disconnect() itself fails,
2553 // send the DISCONN_COMPLETE event directly.
2554 // SEND_EVENT: Perform off-thread to avoid potential deadlock w/ application callbacks (similar when sent from HCIHandler's reader-thread)
2555 std::thread bg(&BTDevice::sendMgmtEvDeviceDisconnected, this, // @suppress("Invalid arguments")
2556 std::make_unique<MgmtEvtDeviceDisconnected>(adapter.dev_id, addressAndType, reason, hciConnHandle) );
2557 bg.detach();
2558 // adapter.mgmtEvDeviceDisconnectedHCI( std::unique_ptr<MgmtEvent>( new MgmtEvtDeviceDisconnected(adapter.dev_id, address, addressType, reason, hciConnHandle) ) );
2559 }
2560 WORDY_PRINT("BTDevice::disconnect: End: status %s, handle 0x%X, isConnected %d/%d on %s",
2561 to_string(res).c_str(),
2562 hciConnHandle.load(), allowDisconnect.load(), isConnected.load(),
2563 toString().c_str());
2564
2565 return res;
2566}
2567
2568std::uint32_t BTDevice::getResponderSMPPassKey() const noexcept {
2569 return pairing_data.passKey_resp;
2570}
2571
2573 if constexpr ( USE_LINUX_BT_SECURITY ) {
2574 const BTManagerRef& mngr = adapter.getManager();
2575 const HCIStatusCode res = mngr->unpairDevice(adapter.dev_id, addressAndType, false /* disconnect */);
2576 if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) {
2577 DBG_PRINT("BTDevice::unpair(): Unpair device failed: %s, %s", to_string(res).c_str(), toString().c_str());
2578 }
2579 clearSMPStates(getConnected() /* connected */);
2580 return res;
2581 } else if constexpr ( SMP_SUPPORTED_BY_OS ) {
2583 } else {
2585 }
2586}
2587
2588void BTDevice::remove() noexcept {
2589 adapter.removeDevice(*this);
2590}
static const SMPKeyType _key_mask_legacy
Definition: BTDevice.cpp:760
static const SMPKeyType _key_mask_sc
Definition: BTDevice.cpp:761
#define E_FILE_LINE
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:175
static BLERandomAddressType getBLERandomAddressType(const jau::EUI48 &address, const BDAddressType addressType) noexcept
Returns the BLERandomAddressType.
Definition: BTTypes0.cpp:162
constexpr bool isLEAddress() const noexcept
Returns true if the BDAddressType is a LE address type.
Definition: BTAddress.hpp:216
std::string toString() const noexcept
Definition: BTTypes0.cpp:186
BTAdapter represents one local Bluetooth Controller.
Definition: BTAdapter.hpp:324
const uint16_t dev_id
Adapter's internal temporary device id.
Definition: BTAdapter.hpp:363
BTRole getRole() const noexcept
Return the current BTRole of this adapter.
Definition: BTAdapter.hpp:676
const BTManagerRef & getManager() const noexcept
Returns a reference to the used singleton BTManager instance, used to create this adapter.
Definition: BTAdapter.hpp:932
BDAddressAndType addressAndType
Device's unique mac address and type tuple, might be its initially reported (resolvable) random addre...
Definition: BTDevice.hpp:311
std::shared_ptr< GattGenericAccessSvc > getGattGenericAccess()
Returns the shared GenericAccess instance, retrieved by getGattServices() or nullptr if not available...
Definition: BTDevice.cpp:2290
HCIStatusCode setPairingPINCodeNegative() noexcept
Definition: BTDevice.cpp:1989
bool removeCharListener(const BTGattCharListenerRef &l) noexcept
Remove the given BTGattCharListener from the listener list.
Definition: BTDevice.cpp:2385
bool setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap=SMPIOCapability::UNSET) noexcept
Sets the given BTSecurityLevel and SMPIOCapability used to connect to this device on the upcoming con...
Definition: BTDevice.cpp:1896
bool isConnSecurityAutoEnabled() const noexcept
Returns true if automatic security negotiation has been enabled via setConnSecurityAuto(),...
Definition: BTDevice.cpp:1963
SMPLongTermKey getLongTermKey(const bool responder) const noexcept
Returns a copy of the Long Term Key (LTK), valid after connection and SMP pairing has been completed.
Definition: BTDevice.cpp:1726
bool pingGATT() noexcept
Issues a GATT ping to the device, validating whether it is still reachable.
Definition: BTDevice.cpp:2357
void remove() noexcept
Disconnects this device via disconnect(..) if getConnected()==true and explicitly removes its shared ...
Definition: BTDevice.cpp:2588
BTRole getRole() const noexcept
Return the fixed BTRole of this remote BTDevice.
Definition: BTDevice.hpp:343
SMPSignatureResolvingKey getSignatureResolvingKey(const bool responder) const noexcept
Returns a copy of the Signature Resolving Key (CSRK), valid after connection and SMP pairing has been...
Definition: BTDevice.cpp:1775
EInfoReportRef getEIRInd() noexcept
Return the latest advertised EInfoReport AD_IND variant for this remote device.
Definition: BTDevice.cpp:123
HCIStatusCode setPairingPasskeyNegative() noexcept
Method replies with a negative passkey response, i.e.
Definition: BTDevice.cpp:2031
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
bool setSMPKeyBin(const SMPKeyBin &bin) noexcept
Copy all keys from the given SMPKeyBin into this BTDevice.
Definition: BTDevice.cpp:1556
HCIStatusCode connectBREDR(const uint16_t pkt_type=HCI_DM1|HCI_DM3|HCI_DM5|HCI_DH1|HCI_DH3|HCI_DH5, const uint16_t clock_offset=0x0000, const uint8_t role_switch=0x01) noexcept
Establish a HCI BDADDR_BREDR connection to this device.
Definition: BTDevice.cpp:523
HCIStatusCode unpair() noexcept
Unpair this device from the adapter while staying connected.
Definition: BTDevice.cpp:2572
bool removeStatusListener(const AdapterStatusListenerRef &l) noexcept
Remove the given listener from the list.
Definition: BTDevice.cpp:290
SMPPairingState getPairingState() const noexcept
Returns the current SMPPairingState.
Definition: BTDevice.cpp:2078
SMPIdentityResolvingKey getIdentityResolvingKey(const bool responder) const noexcept
Returns a copy of the Identity Resolving Key (IRK), valid after connection and SMP pairing has been c...
Definition: BTDevice.cpp:1744
std::shared_ptr< ConnectionInfo > getConnectionInfo() noexcept
Retrieves the current connection info for this device and returns the ConnectionInfo reference if suc...
Definition: BTDevice.cpp:294
bool setConnSecurityAuto(const SMPIOCapability iocap_auto) noexcept
Set automatic security negotiation of BTSecurityLevel and SMPIOCapability pairing mode.
Definition: BTDevice.cpp:1923
HCIStatusCode connectLE(const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint16_t conn_interval_min=8, const uint16_t conn_interval_max=12, const uint16_t conn_latency=0, const uint16_t conn_supervision_timeout=getHCIConnSupervisorTimeout(0, 15)) noexcept
Establish a HCI BDADDR_LE_PUBLIC or BDADDR_LE_RANDOM connection to this device.
Definition: BTDevice.cpp:321
PairingMode getPairingMode() const noexcept
Returns the current PairingMode used by the device.
Definition: BTDevice.cpp:2073
HCIStatusCode setConnectedLE_PHY(const LE_PHYs Tx, const LE_PHYs Rx) noexcept
Sets preference of used LE_PHYs for the given connection.
Definition: BTDevice.cpp:2449
std::string const getName() const noexcept
Returns the remote device name.
Definition: BTDevice.cpp:112
EInfoReportRef getEIR() noexcept
Return the merged advertised EInfoReport for this remote device.
Definition: BTDevice.cpp:118
HCIStatusCode getConnectedLE_PHY(LE_PHYs &resTx, LE_PHYs &resRx) noexcept
Request and return LE_PHYs bit for the given connection.
Definition: BTDevice.cpp:2425
void setSignatureResolvingKey(const SMPSignatureResolvingKey &csrk) noexcept
Sets the Signature Resolving Key (CSRK) of this device for pre-paired encryption.
Definition: BTDevice.cpp:1780
size_type removeAllAssociatedCharListener(const BTGattCharRef &associatedCharacteristic) noexcept
Remove all BTGattCharListener from the list, which are associated to the given BTGattChar.
Definition: BTDevice.cpp:2395
EInfoReportRef getEIRScanRsp() noexcept
Return the latest advertised EInfoReport AD_SCAN_RSP for this remote device.
Definition: BTDevice.cpp:128
std::shared_ptr< BTDevice > getSharedInstance() const noexcept
Returns the shared pointer of this instance managed by the adapter.
Definition: BTDevice.cpp:100
bool sendIndication(const uint16_t char_value_handle, const jau::TROOctets &value) noexcept
Send an indication event consisting out of the given value representing the given characteristic valu...
Definition: BTDevice.cpp:2343
BTSecurityLevel getConnSecurityLevel() const noexcept
Return the BTSecurityLevel, determined when the connection is established.
Definition: BTDevice.cpp:1811
SMPKeyType getAvailableSMPKeys(const bool responder) const noexcept
Returns the available SMPKeyType mask for the responder (LL slave) or initiator (LL master).
Definition: BTDevice.cpp:1547
GattServiceList_t getGattServices() noexcept
Returns a complete list of shared BTGattService available on this device, initially retrieved via GAT...
Definition: BTDevice.cpp:2240
void setIdentityResolvingKey(const SMPIdentityResolvingKey &irk) noexcept
Sets the Identity Resolving Key (IRK) of this device for pre-paired encryption.
Definition: BTDevice.cpp:1749
void setLinkKey(const SMPLinkKey &lk) noexcept
Sets the Link Key (LK) of this device for pre-paired encryption.
Definition: BTDevice.cpp:1798
~BTDevice() noexcept override
Releases this instance after calling remove().
Definition: BTDevice.cpp:94
HCIStatusCode connectDefault() noexcept
Establish a default HCI connection to this device, using certain default parameter.
Definition: BTDevice.cpp:563
std::uint32_t getResponderSMPPassKey() const noexcept
Returns the responder SMP passkey, ranging from [0..999999].
Definition: BTDevice.cpp:2568
std::shared_ptr< BTGattHandler > getGattHandler() noexcept
Returns the connected GATTHandler or nullptr, see connectGATT(), getGattServices() and disconnect().
Definition: BTDevice.cpp:2235
BTGattServiceRef findGattService(const jau::uuid_t &service_uuid) noexcept
Find a BTGattService by its service_uuid.
Definition: BTDevice.cpp:2299
jau::nsize_t size_type
Definition: BTDevice.hpp:304
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
bool matches_irk(const BDAddressAndType &rpa) noexcept
Returns true if this remote device's IRK matches the given random private address (rpa)
Definition: BTDevice.cpp:1764
SMPIOCapability getConnIOCapability() const noexcept
Return the set SMPIOCapability value, determined when the connection is established.
Definition: BTDevice.cpp:1816
SMPLinkKey getLinkKey(const bool responder) const noexcept
Returns a copy of the Link Key (LK), valid after connection and SMP pairing has been completed.
Definition: BTDevice.cpp:1793
BTGattCharRef findGattChar(const jau::uuid_t &service_uuid, const jau::uuid_t &char_uuid) noexcept
Find a BTGattChar by its service_uuid and char_uuid.
Definition: BTDevice.cpp:2309
bool addCharListener(const BTGattCharListenerRef &l) noexcept
Add the given BTGattCharListener to the listener list if not already present.
Definition: BTDevice.cpp:2367
HCIStatusCode setPairingNumericComparison(const bool equal) noexcept
Method sets the numeric comparison result, see PairingMode::NUMERIC_COMPARE_ini.
Definition: BTDevice.cpp:2052
bool addStatusListener(const AdapterStatusListenerRef &l) noexcept
Add the given AdapterStatusListener to the list if not already present, intended to listen only for e...
Definition: BTDevice.cpp:286
HCIStatusCode uploadKeys() noexcept
Upload all set keys to the adapter for pre-pairing.
Definition: BTDevice.cpp:1649
bool getConnected() noexcept
Return true if the device has been successfully connected, otherwise false.
Definition: BTDevice.hpp:526
GATTRole getLocalGATTRole() const noexcept
Return the local GATTRole operating for the remote BTDevice.
Definition: BTDevice.cpp:104
void setLongTermKey(const SMPLongTermKey &ltk) noexcept
Sets the Long Term Key (LTK) of this device for pre-paired encryption.
Definition: BTDevice.cpp:1731
std::string toString() const noexcept override
Definition: BTDevice.hpp:470
HCIStatusCode setPairingPasskey(const uint32_t passkey) noexcept
Method sets the given passkey entry, see PairingMode::PASSKEY_ENTRY_ini.
Definition: BTDevice.cpp:2010
bool sendNotification(const uint16_t char_value_handle, const jau::TROOctets &value) noexcept
Send a notification event consisting out of the given value representing the given characteristic val...
Definition: BTDevice.cpp:2330
size_type removeAllCharListener() noexcept
Remove all BTGattCharListener from the list.
Definition: BTDevice.cpp:2415
HCIStatusCode setPairingPINCode(const std::string &pinCode) noexcept
Definition: BTDevice.cpp:1968
Representing a Gatt Characteristic object from the GATTRole::Client perspective.
Definition: BTGattChar.hpp:94
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
bool isSet(EIRDataType bit) const noexcept
Definition: BTTypes0.hpp:1110
@ AD_SCAN_RSP
(Extended) Advertising Data (AD or EAD) Scan Response, i.e.
@ AD_IND
(Extended) Advertising Data (AD or EAD) Indication Variant, i.e.
Generic Access Service is a mandatory GATT service all peripherals are required to implement.
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 isOpen() const noexcept
Returns true if this mgmt instance is open, connected and hence valid, otherwise false.
Definition: HCIHandler.hpp:405
HCIStatusCode create_conn(const EUI48 &bdaddr, const uint16_t pkt_type=HCI_DM1|HCI_DM3|HCI_DM5|HCI_DH1|HCI_DH3|HCI_DH5, const uint16_t clock_offset=0x0000, const uint8_t role_switch=0x01) noexcept
Establish a connection to the given BREDR (non LE).
HCIStatusCode le_set_phy(const uint16_t conn_handle, const BDAddressAndType &peerAddressAndType, const LE_PHYs Tx, const LE_PHYs Rx) noexcept
Sets preference of used LE_PHYs for the given connection.
HCIStatusCode le_read_phy(const uint16_t conn_handle, const BDAddressAndType &peerAddressAndType, LE_PHYs &resTx, LE_PHYs &resRx) noexcept
Request and return LE_PHYs bit for the given connection.
HCIStatusCode le_del_from_resolv_list(const BDAddressAndType &peerIdentityAddressAndType) noexcept
BT Core Spec v5.2: Vol 4, Part E HCI: 7.8.39 LE Remove Device From Resolving List command.
const HCIEnv & env
Definition: HCIHandler.hpp:185
HCIStatusCode le_create_conn(const EUI48 &peer_bdaddr, const HCILEPeerAddressType peer_mac_type=HCILEPeerAddressType::PUBLIC, const HCILEOwnAddressType own_mac_type=HCILEOwnAddressType::PUBLIC, const uint16_t le_scan_interval=24, const uint16_t le_scan_window=24, const uint16_t conn_interval_min=8, const uint16_t conn_interval_max=12, const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 15)) noexcept
Establish a connection to the given LE peer.
HCIStatusCode disconnect(const uint16_t conn_handle, const BDAddressAndType &peerAddressAndType, const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION) noexcept
Disconnect an established connection.
L2CAP read/write communication channel to remote device.
Definition: L2CAPComm.hpp:195
uint16_t opcode, uint16_t dev-id, uint16_t param_size
Definition: MgmtTypes.hpp:1402
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.7.65.5 LE Long Term Key Request event.
Definition: MgmtTypes.hpp:2550
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 }, uint32_t passkey uint8_t entered
Definition: MgmtTypes.hpp:2066
Vol 3, Part H: 3.6.2 Encryption Information message.
Definition: SMPTypes.hpp:1679
constexpr jau::uint128dp_t getLTK() const noexcept
Returns the 128-bit Long Term Key (16 octets)
Definition: SMPTypes.hpp:1706
static bool IS_SUPPORTED_BY_OS
Linux/BlueZ prohibits access to the existing SMP implementation via L2CAP (socket).
Definition: SMPHandler.hpp:168
Vol 3, Part H: 3.6.5 Identity Address Information message.
Definition: SMPTypes.hpp:1897
EUI48 getAddress() const noexcept
Returns the device address.
Definition: SMPTypes.hpp:1926
Vol 3, Part H: 3.6.4 Identify Information message.
Definition: SMPTypes.hpp:1830
constexpr jau::uint128dp_t getIRK() const noexcept
Returns the 128-bit Identity Resolving Key (IRK, 16 octets)
Definition: SMPTypes.hpp:1857
Storage for SMP keys including required connection parameter per local adapter and remote device.
Definition: SMPKeyBin.hpp:79
Vol 3, Part H: 3.6.3 Master Identification message.
Definition: SMPTypes.hpp:1750
constexpr uint16_t getEDIV() const noexcept
Returns the 16-bit EDIV value (2 octets) being distributed.
Definition: SMPTypes.hpp:1777
constexpr uint64_t getRand() const noexcept
Returns the 64-bit Rand value (8 octets) being distributed.
Definition: SMPTypes.hpp:1785
Handles the Security Manager Protocol (SMP) using Protocol Data Unit (PDU) encoded messages over L2CA...
Definition: SMPTypes.hpp:842
virtual std::string toString() const noexcept
Definition: SMPTypes.hpp:1004
Opcode
SMP Command Codes Vol 3, Part H (SM): 3.3.
Definition: SMPTypes.hpp:845
Vol 3, Part H: 3.5.1 Pairing Request message.
Definition: SMPTypes.hpp:1085
constexpr SMPAuthReqs getAuthReqMask() const noexcept
Returns the Authentication Requirements mask.
Definition: SMPTypes.hpp:1156
constexpr SMPKeyType getInitKeyDist() const noexcept
Returns the Initiator Key Distribution field, which defines which keys the initiator shall distribute...
Definition: SMPTypes.hpp:1180
constexpr SMPKeyType getRespKeyDist() const noexcept
Return the Responder Key Distribution field, which defines which keys the responder shall distribute ...
Definition: SMPTypes.hpp:1193
constexpr SMPIOCapability getIOCapability() const noexcept
Returns the IO capability bit field.
Definition: SMPTypes.hpp:1133
constexpr uint8_t getMaxEncryptionKeySize() const noexcept
This value defines the maximum encryption key size in octets that the device can support.
Definition: SMPTypes.hpp:1167
constexpr SMPOOBDataFlag getOOBDataFlag() const noexcept
Returns the OBB authenticate data flag.
Definition: SMPTypes.hpp:1144
Vol 3, Part H: 3.6.6 Signing Information message.
Definition: SMPTypes.hpp:1970
constexpr jau::uint128dp_t getCSRK() const noexcept
Returns the 128-bit Connection Signature Resolving Key (CSRK, 16 octets)
Definition: SMPTypes.hpp:1997
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
Implementation of a dynamic linear array storage, aka vector.
Definition: darray.hpp:148
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
const bool debug
Debug logging enabled or disabled.
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...
static uint64_t getElapsedMillisecond() noexcept
Returns current elapsed monotonic time in milliseconds since module startup, see startupTimeMilliseco...
constexpr int_type to_ms() const noexcept
Convenient shortcut to to_num_of(1_ms)
std::chrono::duration< Rep, Period > to_duration(const std::chrono::duration< Rep, Period > &dur_ref, bool *overflow_ptr=nullptr) const noexcept
Convert this fraction into std::chrono::duration with given Rep and Period.
This class provides a RAII-style Sequentially Consistent (SC) data race free (DRF) critical block.
static const uint16_t le_scan_interval
static const uint16_t le_scan_window
static jau::sc_atomic_bool sync_data
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:68
#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
std::string to_string(const alphabet &v) noexcept
Definition: base_codec.hpp:97
constexpr const bool CONSIDER_HCI_CMD_FOR_SMP_STATE
Definition: DBTConst.hpp:62
constexpr bool hasSMPPairingFinished(const SMPPairingState state) noexcept
Returns true if the given SMPPairingState indicates a finished pairing process, i....
Definition: SMPTypes.hpp:178
constexpr const bool SMP_SUPPORTED_BY_OS
Definition: DBTConst.hpp:58
constexpr bool hasSMPIOCapabilityFullInput(const SMPIOCapability ioc) noexcept
Definition: SMPTypes.hpp:244
constexpr bool isSMPPairingAllowingInput(const SMPPairingState state, const SMPPairingState inputSpec) noexcept
Returns true if the given SMPPairingState indicates a pairing process waiting for user input,...
Definition: SMPTypes.hpp:200
SMPPairingState
SMP Pairing Process state definition.
Definition: SMPTypes.hpp:107
SMPKeyType
SMP Key Type for Distribution, indicates keys distributed in the Transport Specific Key Distribution ...
Definition: SMPTypes.hpp:415
constexpr bool hasSMPIOCapabilityAnyIO(const SMPIOCapability ioc) noexcept
Definition: SMPTypes.hpp:233
std::string toPassKeyString(const std::uint32_t passKey) noexcept
Returns given passKey ranging [0..999999] as a canonical string, e.g.
Definition: SMPTypes.cpp:72
SMPIOCapability
Vol 3, Part H, 2.3.2 IO capabilities.
Definition: SMPTypes.hpp:209
PairingMode getPairingMode(const bool use_sc, const SMPAuthReqs authReqs_ini, const SMPIOCapability ioCap_ini, const SMPOOBDataFlag oobFlag_ini, const SMPAuthReqs authReqs_res, const SMPIOCapability ioCap_res, const SMPOOBDataFlag oobFlag_res) noexcept
Returns the PairingMode derived from both devices' sets of SMPAuthReqs, SMPIOCapability and SMPOOBDat...
Definition: SMPTypes.cpp:153
constexpr const bool USE_LINUX_BT_SECURITY
Definition: DBTConst.hpp:60
constexpr bool hasSMPIOCapabilityBinaryInput(const SMPIOCapability ioc) noexcept
Definition: SMPTypes.hpp:239
@ SECURE_CONNECTIONS
If LE Secure Connections pairing is supported by the device, then the SC field shall be set to 1,...
@ OOB_EXPECTED
Phase 2: Authentication (MITM) OOB data expected now, see PairingMode::OUT_OF_BAND.
@ REQUESTED_BY_RESPONDER
Phase 0: Pairing requested by responding (slave) device via SMPSecurityReqMsg.
@ 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.
@ FEATURE_EXCHANGE_STARTED
Phase 1: Pairing requested by initiating (master) device via SMPPairingMsg.
@ FEATURE_EXCHANGE_COMPLETED
Phase 1: Pairing responded by responding (slave) device via SMPPairingMsg.
@ KEY_DISTRIBUTION
Phase 3: Key & value distribution started after SMPPairConfirmMsg or SMPPairPubKeyMsg (LE Secure Conn...
@ LINK_KEY
SMP on the LE transport: Indicate that the device would like to derive the Link Key from the LTK.
@ SIGN_KEY
Indicates that the device shall distribute CSRK using the Signing Information command.
@ ENC_KEY
LE legacy pairing: Indicates device shall distribute LTK using the Encryption Information command,...
@ ID_KEY
Indicates that the device shall distribute IRK using the Identity Information command followed by its...
@ OOB_AUTH_DATA_NOT_PRESENT
OOB_AUTH_DATA_NOT_PRESENT.
@ 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
BLERandomAddressType
BT Core Spec v5.2: Vol 6 LE, Part B Link Layer Specification: 1.3 Device Address.
Definition: BTAddress.hpp:98
std::shared_ptr< AdapterStatusListener > AdapterStatusListenerRef
Definition: BTAdapter.hpp:297
LE_Features
HCI Supported Commands.
Definition: BTTypes0.hpp:162
constexpr void set(EAD_Event_Type &mask, const EAD_Event_Type bit) noexcept
Definition: BTTypes0.hpp:465
std::string to_string(const DiscoveryPolicy v) noexcept
Definition: BTAdapter.cpp:58
LE_PHYs
LE Transport PHY bit values.
Definition: BTTypes0.hpp:231
HCILEPeerAddressType
HCI LE Address-Type is PUBLIC: 0x00, RANDOM: 0x01.
Definition: BTAddress.hpp:135
std::shared_ptr< EInfoReport > EInfoReportRef
Definition: BTTypes0.hpp:1164
GATTRole
Bluetooth GATT roles.
Definition: BTTypes0.hpp:96
BTRole
Bluetooth roles from the perspective of the link layer (connection initiator).
Definition: BTTypes0.hpp:69
L2CAP_PSM
Protocol Service Multiplexers (PSM) Assigned numbers https://www.bluetooth.com/specifications/assigne...
Definition: BTTypes0.hpp:514
BTSecurityLevel
Bluetooth Security Level.
Definition: BTTypes0.hpp:267
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
@ RESOLVABLE_PRIVAT
Resolvable private random device address 0b01.
@ UNRESOLVABLE_PRIVAT
Non-resolvable private random device address 0b00.
@ STATIC_PUBLIC
Static public 'random' device address 0b11.
@ LE_Encryption
LE_Encryption.
@ RANDOM
Random Device Address.
@ PUBLIC
Public Device Address.
@ Client
Local GATT client role to a remote BTDevice BTRole::Slave running a GATTRole::Server.
@ None
Undefined role.
@ Server
Local GATT server role to a remote BTDevice BTRole::Master running a GATTRole::Client.
@ BDADDR_BREDR
Bluetooth BREDR address.
@ BDADDR_LE_RANDOM
Bluetooth LE random address, see BLERandomAddressType.
@ BDADDR_LE_PUBLIC
Bluetooth LE public address.
@ 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.
@ UNSET
Security Level not set, value 0.
@ ENC_AUTH
Encryption and authentication (MITM).
@ ENC_ONLY
Encryption and no authentication (no MITM).
@ NONE
No encryption and no authentication.
@ ENC_AUTH_FIPS
Authenticated Secure Connections.
@ PRE_PAIRED
Reusing encryption keys from previous pairing.
@ NEGOTIATING
Pairing mode in negotiating, i.e.
@ OUT_OF_BAND
Utilizing a second factor secret to be used as a secret, e.g.
@ PASSKEY_ENTRY_ini
Passkey Entry input by initiator.
@ NUMERIC_COMPARE_ini
Visual comparison of digit sequence (PIN) input by initiator, shown on both devices.
@ NONE
No pairing mode, implying no secure connections, no encryption and no MITM protection.
std::shared_ptr< BTGattCharListener > BTGattCharListenerRef
Definition: BTGattChar.hpp:70
std::shared_ptr< BTGattChar > BTGattCharRef
Definition: BTGattChar.hpp:410
std::shared_ptr< BTGattService > BTGattServiceRef
Definition: BTGattChar.hpp:67
void sync() noexcept
Synchronizes filesystems, i.e.
Definition: file_util.cpp:1932
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
std::string to_decstring(const value_type &v, const char separator=',', const nsize_t width=0) noexcept
Produce a decimal string representation of an integral integer value.
constexpr const jau::fraction_i64 zero(0l, 1lu)
zero is 0/1
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
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.
STL namespace.
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.
Used for MgmtLoadLinkKeyCmd and MgmtEvtNewLinkKey.
Definition: MgmtTypes.hpp:345
SMPLinkKey toSMPLinkKeyInfo(const bool isResponder) const noexcept
Definition: MgmtTypes.hpp:345
Used for MgmtLoadLongTermKeyCmd and MgmtEvtNewLongTermKey.
Definition: MgmtTypes.hpp:264
SMPLongTermKey toSMPLongTermKey(const BTRole adapterRole) const noexcept
Definition: MgmtTypes.hpp:264
SMP Identity Resolving Key, used for platform agnostic persistence.
Definition: SMPTypes.hpp:636
bool matches(const EUI48 &rpa) noexcept
Returns true if this IRK matches the given random private address (RPA).
Definition: SMPTypes.cpp:314
Local SMP Link Key, used for platform agnostic persistence, mapping to platform specific MgmtLoadLink...
Definition: SMPTypes.hpp:824
bool isResponder() const noexcept
Definition: SMPTypes.hpp:824
constexpr bool isValid() const noexcept
Definition: SMPTypes.hpp:824
constexpr bool isCombiKey() const noexcept
Definition: SMPTypes.hpp:824
SMP Long Term Key, used for platform agnostic persistence.
Definition: SMPTypes.hpp:552
constexpr bool isValid() const noexcept
Definition: SMPTypes.hpp:552
bool isResponder() const noexcept
Definition: SMPTypes.cpp:294
@ SC
Secure Connection used.
uint8_t enc_size
Encryption Size, zero if key is invalid.
Definition: SMPTypes.hpp:552
Property properties
SMPLongTermKey::Property bit mask.
Definition: SMPTypes.hpp:552
jau::uint128dp_t ltk
Long Term Key (LTK)
Definition: SMPTypes.hpp:552
SMP Signature Resolving Key, used for platform agnostic persistence.
Definition: SMPTypes.hpp:712