Direct-BT v3.3.0-1-gc2d430c
Direct-BT - Direct Bluetooth Programming.
BTManager.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 PERF_PRINT_ON 1
35// PERF3_PRINT_ON for close
36// #define PERF3_PRINT_ON 1
37#include <jau/debug.hpp>
38
39#include <jau/basic_algos.hpp>
40
41#include "BTIoctl.hpp"
42
43#include "HCIIoctl.hpp"
44#include "HCIComm.hpp"
45#include "BTTypes1.hpp"
46#include "SMPHandler.hpp"
47
48#include "BTAdapter.hpp"
49#include "BTManager.hpp"
50#include "DBTConst.hpp"
51
52extern "C" {
53 #include <inttypes.h>
54 #include <unistd.h>
55 #include <poll.h>
56 #include <signal.h>
57}
58
59using namespace direct_bt;
60using namespace jau::fractions_i64_literals;
61
62MgmtEnv::MgmtEnv() noexcept
63: DEBUG_GLOBAL( jau::environment::get("direct_bt").debug ),
64 exploding( jau::environment::getExplodingProperties("direct_bt.mgmt") ),
65 MGMT_READER_THREAD_POLL_TIMEOUT( jau::environment::getFractionProperty("direct_bt.mgmt.reader.timeout", 10_s, 1500_ms /* min */, 365_d /* max */) ),
66 MGMT_COMMAND_REPLY_TIMEOUT( jau::environment::getFractionProperty("direct_bt.mgmt.cmd.timeout", 3_s, 1500_ms /* min */, 365_d /* max */) ),
67 MGMT_SET_POWER_COMMAND_TIMEOUT( jau::environment::getFractionProperty("direct_bt.mgmt.setpower.timeout",
68 jau::max(MGMT_COMMAND_REPLY_TIMEOUT, 6_s) /* default */,
69 MGMT_COMMAND_REPLY_TIMEOUT /* min */, 365_d /* max */) ),
70 MGMT_EVT_RING_CAPACITY( jau::environment::getInt32Property("direct_bt.mgmt.ringsize", 64, 64 /* min */, 1024 /* max */) ),
71 DEBUG_EVENT( jau::environment::getBooleanProperty("direct_bt.debug.mgmt.event", false) ),
72 MGMT_READ_PACKET_MAX_RETRY( MGMT_EVT_RING_CAPACITY )
73{
74 // Kick off singleton initialization of all environments.
79}
80
81void BTManager::mgmtReaderWork(jau::service_runner& sr) noexcept {
82 jau::snsize_t len;
83 if( !comm.is_open() ) {
84 // not open
85 ERR_PRINT("BTManager::reader: Not connected");
86 sr.set_shall_stop();
87 return;
88 }
89
90 len = comm.read(rbuffer.get_wptr(), rbuffer.size(), env.MGMT_READER_THREAD_POLL_TIMEOUT);
91 if( 0 < len ) {
92 const jau::nsize_t len2 = static_cast<jau::nsize_t>(len);
93 const jau::nsize_t paramSize = len2 >= MGMT_HEADER_SIZE ? rbuffer.get_uint16_nc(4) : 0;
94 if( len2 < MGMT_HEADER_SIZE + paramSize ) {
95 WARN_PRINT("BTManager::reader: length mismatch %zu < MGMT_HEADER_SIZE(%u) + %u, %s", len2, MGMT_HEADER_SIZE, paramSize, rbuffer.toString().c_str());
96 return; // discard data
97 }
98 std::unique_ptr<MgmtEvent> event = MgmtEvent::getSpecialized(rbuffer.get_ptr(), len2);
99 const MgmtEvent::Opcode opc = event->getOpcode();
101 COND_PRINT(env.DEBUG_EVENT, "BTManager-IO RECV (CMD) %s", event->toString().c_str());
102 if( mgmtEventRing.isFull() ) {
103 const jau::nsize_t dropCount = mgmtEventRing.capacity()/4;
104 mgmtEventRing.drop(dropCount);
105 WARN_PRINT("BTManager-IO RECV Drop (%u oldest elements of %u capacity, ring full)", dropCount, mgmtEventRing.capacity());
106 }
107 if( !mgmtEventRing.putBlocking( std::move( event ), 0_s ) ) {
108 ERR_PRINT2("mgmtEventRing put: %s", mgmtEventRing.toString().c_str());
109 sr.set_shall_stop();
110 return;
111 }
112 } else if( MgmtEvent::Opcode::INDEX_ADDED == opc ) {
113 COND_PRINT(env.DEBUG_EVENT, "BTManager-IO RECV (ADD) %s", event->toString().c_str());
114 std::thread adapterAddedThread(&BTManager::processAdapterAdded, this, std::move( event) ); // @suppress("Invalid arguments")
115 adapterAddedThread.detach();
116 } else if( MgmtEvent::Opcode::INDEX_REMOVED == opc ) {
117 COND_PRINT(env.DEBUG_EVENT, "BTManager-IO RECV (REM) %s", event->toString().c_str());
118 std::thread adapterRemovedThread(&BTManager::processAdapterRemoved, this, std::move( event ) ); // @suppress("Invalid arguments")
119 adapterRemovedThread.detach();
120 } else {
121 // issue a callback
122 COND_PRINT(env.DEBUG_EVENT, "BTManager-IO RECV (CB) %s", event->toString().c_str());
123 sendMgmtEvent( *event );
124 }
125 } else if( 0 > len && ETIMEDOUT != errno && !comm.interrupted() ) { // expected exits
126 ERR_PRINT("BTManager::reader: HCIComm read: Error res %d, %s", len, toString().c_str());
127 // Keep alive - sr.set_shall_stop();
128 } else if( ETIMEDOUT != errno && !comm.interrupted() ) { // expected TIMEOUT if idle
129 WORDY_PRINT("BTManager::reader: HCIComm read: IRQed res %d, %s", len, toString().c_str());
130 }
131}
132
133void BTManager::mgmtReaderEndLocked(jau::service_runner& sr) noexcept {
134 (void)sr;
135 WORDY_PRINT("BTManager::reader: Ended. Ring has %u entries flushed", mgmtEventRing.size());
136 mgmtEventRing.clear();
137}
138
139void BTManager::sendMgmtEvent(const MgmtEvent& event) noexcept {
140 const uint16_t dev_id = event.getDevID();
141 MgmtAdapterEventCallbackList & mgmtEventCallbackList = mgmtAdapterEventCallbackLists[static_cast<uint16_t>(event.getOpcode())];
142 int invokeCount = 0;
143
144 jau::for_each_fidelity(mgmtEventCallbackList, [&](MgmtAdapterEventCallback &cb) {
145 if( 0 > cb.getDevID() || dev_id == cb.getDevID() ) {
146 try {
147 cb.getCallback()(event);
148 } catch (std::exception &e) {
149 ERR_PRINT("BTManager::sendMgmtEvent-CBs %d/%zd: MgmtAdapterEventCallback %s : Caught exception %s",
150 invokeCount+1, mgmtEventCallbackList.size(),
151 cb.toString().c_str(), e.what());
152 }
153 invokeCount++;
154 }
155 });
156
157 COND_PRINT(env.DEBUG_EVENT, "BTManager::sendMgmtEvent: Event %s -> %d/%zd callbacks", event.toString().c_str(), invokeCount, mgmtEventCallbackList.size());
158 (void)invokeCount;
159}
160
161bool BTManager::send(MgmtCommand &req) noexcept {
162 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
163 COND_PRINT(env.DEBUG_EVENT, "BTManager-IO SENT %s", req.toString().c_str());
164 jau::TROOctets & pdu = req.getPDU();
165 if ( comm.write( pdu.get_ptr(), pdu.size() ) < 0 ) {
166 ERR_PRINT("BTManager::sendWithReply: HCIComm write error, req %s", req.toString().c_str());
167 return false;
168 }
169 return true;
170}
171
172std::unique_ptr<MgmtEvent> BTManager::sendWithReply(MgmtCommand &req, const jau::fraction_i64& timeout) noexcept {
173 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
174 if( !send(req) ) {
175 return nullptr;
176 }
177
178 // Ringbuffer read is thread safe
179 int32_t retryCount = 0;
180 while( retryCount < env.MGMT_READ_PACKET_MAX_RETRY ) {
181 /* timeoutMS default: env.MGMT_COMMAND_REPLY_TIMEOUT */
182 std::unique_ptr<MgmtEvent> res;
183 if( !mgmtEventRing.getBlocking(res, timeout) || nullptr == res ) {
184 errno = ETIMEDOUT;
185 ERR_PRINT("BTManager::sendWithReply.X: nullptr result (timeout -> abort): req %s", req.toString().c_str());
186 return nullptr;
187 } else if( !res->validate(req) ) {
188 // This could occur due to an earlier timeout w/ a nullptr == res (see above),
189 // i.e. the pending reply processed here and naturally not-matching.
190 COND_PRINT(env.DEBUG_EVENT, "BTManager-IO RECV sendWithReply: res mismatch (drop evt, retryCount %d): res %s; req %s",
191 retryCount, res->toString().c_str(), req.toString().c_str());
192 retryCount++;
193 } else {
194 COND_PRINT(env.DEBUG_EVENT, "BTManager-IO RECV sendWithReply: res %s; req %s", res->toString().c_str(), req.toString().c_str());
195 return res;
196 }
197 }
198 return nullptr;
199}
200
201std::unique_ptr<AdapterInfo> BTManager::readAdapterInfo(const uint16_t dev_id) noexcept {
202 std::unique_ptr<AdapterInfo> adapterInfo(nullptr); // nullptr
204 {
205 std::unique_ptr<MgmtEvent> res = sendWithReply(req0);
206 if( nullptr == res ) {
207 goto fail;
208 }
209 if( MgmtEvent::Opcode::CMD_COMPLETE != res->getOpcode() || res->getTotalSize() < MgmtEvtAdapterInfo::getRequiredTotalSize()) {
210 ERR_PRINT("Insufficient data for adapter info: req %d, res %s", MgmtEvtAdapterInfo::getRequiredTotalSize(), res->toString().c_str());
211 goto fail;
212 }
213 const MgmtEvtAdapterInfo * res1 = static_cast<MgmtEvtAdapterInfo*>(res.get());
214 adapterInfo = res1->toAdapterInfo();
215 if( dev_id != adapterInfo->dev_id ) {
216 ABORT("readAdapterSettings dev_id=%d != dev_id=%d: %s", adapterInfo->dev_id, dev_id, adapterInfo->toString().c_str());
217 }
218 }
219 DBG_PRINT("readAdapterSettings[%d]: End: %s", dev_id, adapterInfo->toString().c_str());
220
221fail:
222 return adapterInfo;
223}
224
225HCIStatusCode BTManager::initializeAdapter(AdapterInfo& adapterInfo, const uint16_t dev_id,
226 const BTMode btMode, const bool powerOn) noexcept {
227 /**
228 * We set BTManager::defaultIOCapability, i.e. SMPIOCapability::NO_INPUT_NO_OUTPUT,
229 * which may be overridden for each connection by BTDevice/BTAdapter!
230 *
231 * BT Core Spec v5.2: Vol 3, Part H (SM): 2.3.5.1 Selecting key generation method Table 2.8
232 *
233 * See SMPTypes.cpp: getPairingMode(const bool le_sc_pairing, const SMPIOCapability ioCap_init, const SMPIOCapability ioCap_resp) noexcept
234 */
235 constexpr const uint8_t debug_keys = 0;
236 constexpr const uint8_t ssp_on_param = 0x01; // SET_SSP 0x00 disabled, 0x01 enable Secure Simple Pairing. SSP only available for BREDR >= 2.1 not single-mode LE.
237 constexpr const uint8_t sc_on_param = 0x01; // SET_SECURE_CONN 0x00 disabled, 0x01 enables SC mixed, 0x02 enables SC only mode
238
239 AdapterSetting current_settings;
241 {
242 std::unique_ptr<MgmtEvent> res = sendWithReply(req0);
243 if( nullptr == res ) {
244 goto fail;
245 }
246 if( MgmtEvent::Opcode::CMD_COMPLETE != res->getOpcode() || res->getTotalSize() < MgmtEvtAdapterInfo::getRequiredTotalSize()) {
247 ERR_PRINT("Insufficient data for adapter info: req %d, res %s", MgmtEvtAdapterInfo::getRequiredTotalSize(), res->toString().c_str());
248 goto fail;
249 }
250 const MgmtEvtAdapterInfo * res1 = static_cast<MgmtEvtAdapterInfo*>(res.get());
251 res1->updateAdapterInfo(adapterInfo);
252 if( dev_id != adapterInfo.dev_id ) {
253 ABORT("initializeAdapter dev_id=%d != dev_id=%d: %s", adapterInfo.dev_id, dev_id, adapterInfo.toString().c_str());
254 }
255 }
256 DBG_PRINT("initializeAdapter[%d, BTMode %s]: Start: %s", dev_id, to_string(btMode).c_str(), adapterInfo.toString().c_str());
257
258 {
259 HCIStatusCode res0 = clearIdentityResolvingKeys(dev_id);
260 DBG_PRINT("initializeAdapter[%d]: clearIdentityResolvingKeys: %s", dev_id, to_string(res0).c_str());
261 }
262
263 current_settings = adapterInfo.getCurrentSettingMask();
264
265 setMode(dev_id, MgmtCommand::Opcode::SET_POWERED, 0, current_settings);
266 {
267 jau::uint128dp_t zero_privacy_irk;
268 zero_privacy_irk.clear();
269 setPrivacy(dev_id, 0x00, zero_privacy_irk, current_settings);
270 }
271
272 switch ( btMode ) {
273 case BTMode::DUAL:
274 setMode(dev_id, MgmtCommand::Opcode::SET_BREDR, 1, current_settings);
275 setDiscoverable(dev_id, 0, 0, current_settings);
276 setMode(dev_id, MgmtCommand::Opcode::SET_LE, 1, current_settings);
277 if constexpr ( USE_LINUX_BT_SECURITY ) {
278 setMode(dev_id, MgmtCommand::Opcode::SET_SSP, ssp_on_param, current_settings);
279 setMode(dev_id, MgmtCommand::Opcode::SET_SECURE_CONN, sc_on_param, current_settings);
280 }
281 break;
282 case BTMode::BREDR:
283 setMode(dev_id, MgmtCommand::Opcode::SET_BREDR, 1, current_settings);
284 setDiscoverable(dev_id, 0, 0, current_settings);
285 setMode(dev_id, MgmtCommand::Opcode::SET_LE, 0, current_settings);
286 if constexpr ( USE_LINUX_BT_SECURITY ) {
287 setMode(dev_id, MgmtCommand::Opcode::SET_SSP, ssp_on_param, current_settings);
288 setMode(dev_id, MgmtCommand::Opcode::SET_SECURE_CONN, 0, current_settings);
289 }
290 break;
291 case BTMode::NONE:
292 [[fallthrough]]; // map NONE -> LE
293 case BTMode::LE:
294 setMode(dev_id, MgmtCommand::Opcode::SET_BREDR, 0, current_settings);
295 setMode(dev_id, MgmtCommand::Opcode::SET_LE, 1, current_settings);
296 if constexpr ( USE_LINUX_BT_SECURITY ) {
297 setMode(dev_id, MgmtCommand::Opcode::SET_SSP, 0, current_settings); // SSP not available in LE single mode
298 setMode(dev_id, MgmtCommand::Opcode::SET_SECURE_CONN, sc_on_param, current_settings);
299 }
300 break;
301 }
302
303 if constexpr ( USE_LINUX_BT_SECURITY ) {
304 setMode(dev_id, MgmtCommand::Opcode::SET_DEBUG_KEYS, debug_keys, current_settings);
305 setMode(dev_id, MgmtCommand::Opcode::SET_IO_CAPABILITY, direct_bt::number(BTManager::defaultIOCapability), current_settings);
306 setMode(dev_id, MgmtCommand::Opcode::SET_BONDABLE, 1, current_settings); // required for pairing
307 } else {
308 setMode(dev_id, MgmtCommand::Opcode::SET_SSP, 0, current_settings);
309 setMode(dev_id, MgmtCommand::Opcode::SET_SECURE_CONN, 0, current_settings);
310 setMode(dev_id, MgmtCommand::Opcode::SET_DEBUG_KEYS, 0, current_settings);
311 setMode(dev_id, MgmtCommand::Opcode::SET_BONDABLE, 0, current_settings);
312 }
313
314#if 0
315 if( BTRole::Slave == btRole ) {
316 setMode(dev_id, MgmtCommand::Opcode::SET_CONNECTABLE, 1, current_settings);
317 } else {
318 setMode(dev_id, MgmtCommand::Opcode::SET_CONNECTABLE, 0, current_settings);
319 }
320#else
321 setMode(dev_id, MgmtCommand::Opcode::SET_CONNECTABLE, 0, current_settings); // '1' not required for BTRole::Slave
322#endif
323 setMode(dev_id, MgmtCommand::Opcode::SET_FAST_CONNECTABLE, 0, current_settings);
324
325 removeDeviceFromWhitelist(dev_id, BDAddressAndType::ANY_BREDR_DEVICE); // flush whitelist!
326
327 if( jau::environment::get().debug ) {
328 std::vector<MgmtDefaultParam> params = readDefaultSysParam(dev_id);
329 DBG_PRINT("BTManager::initializeAdapter[%d]: SysParam-Pre: %zd", dev_id, params.size());
330 for(size_t i=0; i<params.size(); ++i) {
331 jau::PLAIN_PRINT(true, "[%2.2zd]: %s", i, params[i].toString().c_str());
332 }
333 }
334 setDefaultConnParam(dev_id); // using our defaults, exceeding BlueZ/Linux on the lower-end a bit
335 if( jau::environment::get().debug ) {
336 std::vector<MgmtDefaultParam> params = readDefaultSysParam(dev_id);
337 DBG_PRINT("BTManager::initializeAdapter[%d]: SysParam-Post: %zd", dev_id, params.size());
338 for(size_t i=0; i<params.size(); ++i) {
339 jau::PLAIN_PRINT(true, "[%2.2zd]: %s", i, params[i].toString().c_str());
340 }
341 }
342
343 if( powerOn ) {
344 setMode(dev_id, MgmtCommand::Opcode::SET_POWERED, 1, current_settings);
345 }
346
347 /**
348 * Update AdapterSettings post settings
349 */
350 if( AdapterSetting::NONE != current_settings ) {
351 adapterInfo.setCurrentSettingMask(current_settings);
352 } else {
353 std::unique_ptr<MgmtEvent> res = sendWithReply(req0);
354 if( nullptr == res ) {
355 goto fail;
356 }
357 if( MgmtEvent::Opcode::CMD_COMPLETE != res->getOpcode() || res->getTotalSize() < MgmtEvtAdapterInfo::getRequiredTotalSize()) {
358 ERR_PRINT("Insufficient data for adapter info: req %d, res %s", MgmtEvtAdapterInfo::getRequiredTotalSize(), res->toString().c_str());
359 goto fail;
360 }
361 const MgmtEvtAdapterInfo * res1 = static_cast<MgmtEvtAdapterInfo*>(res.get());
362 res1->updateAdapterInfo(adapterInfo);
363 if( dev_id != adapterInfo.dev_id ) {
364 ABORT("initializeAdapter dev_id=%d != dev_id=%d: %s", adapterInfo.dev_id, dev_id, adapterInfo.toString().c_str());
365 }
366 }
367 if( powerOn && !adapterInfo.isCurrentSettingBitSet(AdapterSetting::POWERED) ) {
368 ERR_PRINT("initializeAdapter[%d, BTMode %s]: Fail: Couldn't power-on: %s",
369 dev_id, to_string(btMode).c_str(), adapterInfo.toString().c_str());
370 goto fail;
371 }
372 DBG_PRINT("initializeAdapter[%d, BTMode %s]: OK: %s", dev_id, to_string(btMode).c_str(), adapterInfo.toString().c_str());
374
375fail:
377}
378
379BTManager::BTManager() noexcept
380: env(MgmtEnv::get()),
381 rbuffer(ClientMaxMTU, jau::lb_endian_t::little), comm(HCI_DEV_NONE, HCI_CHANNEL_CONTROL),
382 mgmt_reader_service("HCIHandler::reader", THREAD_SHUTDOWN_TIMEOUT_MS,
383 jau::bind_member(this, &BTManager::mgmtReaderWork),
384 jau::service_runner::Callback() /* init */,
385 jau::bind_member(this, &BTManager::mgmtReaderEndLocked)),
386 mgmtEventRing(env.MGMT_EVT_RING_CAPACITY),
387 allowClose( comm.is_open() )
388{
390 ERR_PRINT("BTManager::ctor: Setting sighandler");
391 }
392 WORDY_PRINT("BTManager.ctor: pid %d", jau::service_runner::pid_self);
393 if( !allowClose ) {
394 ERR_PRINT("BTManager::open: Could not open mgmt control channel");
395 return;
396 }
397}
398
399bool BTManager::initialize(const std::shared_ptr<BTManager>& self) noexcept {
400 comm.set_interrupted_query( jau::bind_member(&mgmt_reader_service, &jau::service_runner::shall_stop2) );
401 mgmt_reader_service.start();
402
403 PERF_TS_T0();
404
405 // Mandatory
406 {
408 std::unique_ptr<MgmtEvent> res = sendWithReply(req0);
409 if( nullptr == res ) {
410 goto fail;
411 }
412 if( MgmtEvent::Opcode::CMD_COMPLETE != res->getOpcode() || res->getDataSize() < 3) {
413 ERR_PRINT("Wrong version response: %s", res->toString().c_str());
414 goto fail;
415 }
416 const uint8_t *data = res->getData();
417 const uint8_t version = data[0];
418 const uint16_t revision = jau::get_uint16(data + 1, jau::lb_endian_t::little);
419 WORDY_PRINT("Bluetooth version %d.%d", version, revision);
420 if( version < 1 ) {
421 ERR_PRINT("Bluetooth version >= 1.0 required");
422 goto fail;
423 }
424 }
425 // Optional
426 {
428 std::unique_ptr<MgmtEvent> res = sendWithReply(req0);
429 if( nullptr == res ) {
430 goto next1;
431 }
432 if( MgmtEvent::Opcode::CMD_COMPLETE == res->getOpcode() && res->getDataSize() >= 4) {
433 const uint8_t *data = res->getData();
434 const uint16_t num_commands = jau::get_uint16(data + 0, jau::lb_endian_t::little);
435 const uint16_t num_events = jau::get_uint16(data + 2, jau::lb_endian_t::little);
436 WORDY_PRINT("Bluetooth %d commands, %d events", num_commands, num_events);
437#ifdef VERBOSE_ON
438 const int expDataSize = 4 + num_commands * 2 + num_events * 2;
439 if( res->getDataSize() >= expDataSize ) {
440 for(int i=0; i< num_commands; i++) {
441 const MgmtCommand::Opcode op = static_cast<MgmtCommand::Opcode>( get_uint16(data, 4+i*2, jau::lb_endian::little) );
442 DBG_PRINT("kernel op %d: %s", i, toString(op).c_str());
443 }
444 }
445#endif
446 }
447 }
448
449next1:
450 // Mandatory
451 {
453 std::unique_ptr<MgmtEvent> res = sendWithReply(req0);
454 if( nullptr == res ) {
455 goto fail;
456 }
457 if( MgmtEvent::Opcode::CMD_COMPLETE != res->getOpcode() || res->getDataSize() < 2) {
458 ERR_PRINT("Insufficient data for adapter index: res %s", res->toString().c_str());
459 goto fail;
460 }
461 const uint8_t *data = res->getData();
462 const uint16_t num_adapter = jau::get_uint16(data + 0, jau::lb_endian_t::little);
463 WORDY_PRINT("Bluetooth %d adapter", num_adapter);
464
465 const jau::nsize_t expDataSize = 2 + num_adapter * 2;
466 if( res->getDataSize() < expDataSize ) {
467 ERR_PRINT("Insufficient data for %d adapter indices: res %s", num_adapter, res->toString().c_str());
468 goto fail;
469 }
470 for(jau::nsize_t i=0; i < num_adapter; i++) {
471 const uint16_t dev_id = jau::get_uint16(data + 2+i*2, jau::lb_endian_t::little);
472 std::unique_ptr<AdapterInfo> adapterInfo = readAdapterInfo(dev_id);
473 if( nullptr != adapterInfo ) {
474 std::shared_ptr<BTAdapter> adapter = BTAdapter::make_shared(self, *adapterInfo);
475 adapters.push_back( adapter );
476 adapterIOCapability.push_back(BTManager::defaultIOCapability);
477 DBG_PRINT("BTManager::adapters %d/%d: dev_id %d: %s", i, num_adapter, dev_id, adapter->toString().c_str());
478 } else {
479 DBG_PRINT("BTManager::adapters %d/%d: dev_id %d: FAILED", i, num_adapter, dev_id);
480 }
481 }
482 }
483
484 addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_SETTINGS, jau::bind_member(this, &BTManager::mgmtEvNewSettingsCB));
485
486 if( jau::environment::get().debug ) {
487 addMgmtEventCallback(-1, MgmtEvent::Opcode::CONTROLLER_ERROR, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
488 addMgmtEventCallback(-1, MgmtEvent::Opcode::CLASS_OF_DEV_CHANGED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
489 addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_LINK_KEY, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
490 addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_LONG_TERM_KEY, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
491 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_CONNECTED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
492 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
493 addMgmtEventCallback(-1, MgmtEvent::Opcode::CONNECT_FAILED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
494 addMgmtEventCallback(-1, MgmtEvent::Opcode::PIN_CODE_REQUEST, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
495 addMgmtEventCallback(-1, MgmtEvent::Opcode::USER_CONFIRM_REQUEST, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
496 addMgmtEventCallback(-1, MgmtEvent::Opcode::USER_PASSKEY_REQUEST, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
497 addMgmtEventCallback(-1, MgmtEvent::Opcode::PASSKEY_NOTIFY, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
498 addMgmtEventCallback(-1, MgmtEvent::Opcode::AUTH_FAILED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
499 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_FOUND, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
500 addMgmtEventCallback(-1, MgmtEvent::Opcode::DISCOVERING, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
501 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_BLOCKED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
502 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_UNBLOCKED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
503 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_UNPAIRED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
504 addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_IRK, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
505 addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_CSRK, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
506 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_WHITELIST_ADDED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
507 addMgmtEventCallback(-1, MgmtEvent::Opcode::DEVICE_WHITELIST_REMOVED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
508 addMgmtEventCallback(-1, MgmtEvent::Opcode::NEW_CONN_PARAM, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
509
510 addMgmtEventCallback(-1, MgmtEvent::Opcode::LOCAL_OOB_DATA_UPDATED, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
511 addMgmtEventCallback(-1, MgmtEvent::Opcode::PAIR_DEVICE_COMPLETE, jau::bind_member(this, &BTManager::mgmtEventAnyCB));
512 }
513 PERF_TS_TD("BTManager::ctor.ok");
514 DBG_PRINT("BTManager::ctor: OK");
515 return true;
516
517fail:
518 close();
519 PERF_TS_TD("BTManager::ctor.fail");
520 DBG_PRINT("BTManager::ctor: FAIL");
521 return false;
522}
523
526 close();
527}
528
529void BTManager::close() noexcept {
530 // Avoid disconnect re-entry -> potential deadlock
531 bool expConn = true; // C++11, exp as value since C++20
532 if( !allowClose.compare_exchange_strong(expConn, false) ) {
533 // not open
534 const bool mgmt_service_stopped = mgmt_reader_service.join(); // [data] race: wait until disconnecting thread has stopped service
535 // NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.VirtualCall)
536 DBG_PRINT("BTManager::close: Not open: stopped %d, %s", mgmt_service_stopped, toString().c_str());
537 return;
538 }
539 PERF3_TS_T0();
540
541 const std::lock_guard<std::recursive_mutex> lock(mtx_sendReply); // RAII-style acquire and relinquish via destructor
542 DBG_PRINT("BTManager::close: Start");
545
546 {
547 int i=0;
548 jau::for_each_fidelity(adapters, [&](std::shared_ptr<BTAdapter> & a) {
549 DBG_PRINT("BTManager::close -> adapter::close(): %d/%d processing: %s", i, adapters.size(), a->toString().c_str());
550 a->close(); // also issues removeMgmtEventCallback(dev_id);
551 ++i;
552 });
553 }
554
555 adapters.clear();
556 adapterIOCapability.clear();
557
558 PERF3_TS_TD("BTManager::close.1");
559 mgmt_reader_service.stop();
560 comm.close();
561 PERF3_TS_TD("BTManager::close.2");
562
564 ERR_PRINT("BTManager.sigaction: Resetting sighandler");
565 }
566
567 PERF3_TS_TD("BTManager::close.X");
568 DBG_PRINT("BTManager::close: End");
569}
570
571std::shared_ptr<BTAdapter> BTManager::getDefaultAdapter() const noexcept {
572 typename adapters_t::const_iterator it = adapters.cbegin();
573 for (; !it.is_end(); ++it) {
574 if( (*it)->isPowered() ) {
575 return *it;
576 }
577 }
578 return nullptr;
579}
580
581std::shared_ptr<BTAdapter> BTManager::getAdapter(const uint16_t dev_id) const noexcept {
582 typename adapters_t::const_iterator it = adapters.cbegin();
583 for (; !it.is_end(); ++it) {
584 if ( (*it)->dev_id == dev_id ) {
585 return *it;
586 }
587 }
588 return nullptr;
589}
590
591std::shared_ptr<BTAdapter> BTManager::addAdapter(const AdapterInfo& ai ) noexcept {
592 typename adapters_t::iterator it = adapters.begin(); // lock mutex and copy_store
593 for (; !it.is_end(); ++it) {
594 if ( (*it)->dev_id == ai.dev_id ) {
595 break;
596 }
597 }
598 if( it.is_end() ) {
599 // new entry
600 std::shared_ptr<BTAdapter> adapter = BTAdapter::make_shared(BTManager::get(), ai);
601 it.push_back( adapter );
602 adapterIOCapability.push_back(BTManager::defaultIOCapability);
603 DBG_PRINT("BTManager::addAdapter: Adding new: %s", adapter->toString().c_str())
604 it.write_back();
605 return adapter;
606 } else {
607 // already existing
608 std::shared_ptr<BTAdapter> adapter = *it;
609 WARN_PRINT("BTManager::addAdapter: Already existing %s, overwriting %s", ai.toString().c_str(), adapter->toString().c_str())
610 adapter->adapterInfo = ai;
611 return adapter;
612 }
613}
614
615std::shared_ptr<BTAdapter> BTManager::removeAdapter(const uint16_t dev_id) noexcept {
616 typename adapters_t::iterator it = adapters.begin(); // lock mutex and copy_store
617 for(; !it.is_end(); ++it ) {
618 std::shared_ptr<BTAdapter> & ai = *it;
619 if( ai->dev_id == dev_id ) {
620 adapterIOCapability.erase( adapterIOCapability.cbegin() + it.dist_begin() );
621 std::shared_ptr<BTAdapter> res = ai; // copy
622 DBG_PRINT("BTManager::removeAdapter: Remove: %s", res->toString().c_str())
623 it.erase();
624 it.write_back();
625 return res;
626 }
627 }
628 DBG_PRINT("BTManager::removeAdapter: Not found: dev_id %d", dev_id)
629 return nullptr;
630}
631
632bool BTManager::removeAdapter(BTAdapter* adapter) noexcept {
633 typename adapters_t::iterator it = adapters.begin(); // lock mutex and copy_store
634 for(; !it.is_end(); ++it ) {
635 std::shared_ptr<BTAdapter> & ai = *it;
636 if( ai.get() == adapter ) {
637 adapterIOCapability.erase( adapterIOCapability.cbegin() + it.dist_begin() );
638 DBG_PRINT("BTManager::removeAdapter: Remove: %p -> %s", adapter, ai->toString().c_str())
639 it.erase();
640 it.write_back();
641 return true;
642 }
643 }
644 DBG_PRINT("BTManager::removeAdapter: Not found: %p", adapter)
645 return false;
646}
647
648bool BTManager::setIOCapability(const uint16_t dev_id, const SMPIOCapability io_cap, SMPIOCapability& pre_io_cap) noexcept {
649 if( SMPIOCapability::UNSET != io_cap ) {
650 if constexpr ( USE_LINUX_BT_SECURITY ) {
651 typename adapters_t::const_iterator it = adapters.cbegin();
652 for (; !it.is_end(); ++it) {
653 if( (*it)->dev_id == dev_id ) {
654 const typename adapters_t::difference_type index = it.dist_begin();
655 const SMPIOCapability o = adapterIOCapability.at(index);
656 AdapterSetting current_settings { AdapterSetting::NONE }; // throw away return value, unchanged on SET_IO_CAPABILITY
657 if( setMode(dev_id, MgmtCommand::Opcode::SET_IO_CAPABILITY, direct_bt::number(io_cap), current_settings) ) {
658 adapterIOCapability.at(index) = io_cap;
659 pre_io_cap = o;
660 return true;
661 } else {
662 return false;
663 }
664 }
665 }
666 }
667 }
668 return false;
669}
670
671SMPIOCapability BTManager::getIOCapability(const uint16_t dev_id) const noexcept {
672 typename adapters_t::const_iterator it = adapters.cbegin();
673 for (; !it.is_end(); ++it) {
674 if( (*it)->dev_id == dev_id ) {
675 return adapterIOCapability.at( it.dist_begin() );
676 }
677 }
679}
680
681MgmtStatus BTManager::handleCurrentSettingsReply(std::unique_ptr<MgmtEvent>&& reply, AdapterSetting& current_settings) noexcept {
682 if( nullptr != reply ) {
683 if( reply->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
684 const MgmtEvtCmdComplete &reply1 = *static_cast<const MgmtEvtCmdComplete *>(reply.get());
685 MgmtStatus res = reply1.getStatus();
686 if( MgmtStatus::SUCCESS == res ) {
687 reply1.getCurrentSettings(current_settings);
688 }
689 return res;
690 } else if( reply->getOpcode() == MgmtEvent::Opcode::CMD_STATUS ) {
691 const MgmtEvtCmdStatus &reply1 = *static_cast<const MgmtEvtCmdStatus *>(reply.get());
692 return reply1.getStatus();
693 } else {
695 }
696 } else {
697 return MgmtStatus::TIMEOUT;
698 }
699}
700
701bool BTManager::setMode(const uint16_t dev_id, const MgmtCommand::Opcode opc, const uint8_t mode, AdapterSetting& current_settings) noexcept {
702 const jau::fraction_i64& timeout = MgmtCommand::Opcode::SET_POWERED == opc ? env.MGMT_SET_POWER_COMMAND_TIMEOUT : env.MGMT_COMMAND_REPLY_TIMEOUT;
703 MgmtUint8Cmd req(opc, dev_id, mode);
704 MgmtStatus res = handleCurrentSettingsReply(sendWithReply(req, timeout), current_settings);
705 DBG_PRINT("BTManager::setMode[%d, %s]: %s, result %s %s", dev_id,
706 MgmtCommand::getOpcodeString(opc).c_str(), jau::to_hexstring(mode).c_str(),
707 to_string(res).c_str(), to_string(current_settings).c_str());
708 return MgmtStatus::SUCCESS == res;
709}
710
711MgmtStatus BTManager::setDiscoverable(const uint16_t dev_id, const uint8_t state, const uint16_t timeout_sec, AdapterSetting& current_settings) noexcept {
712 MgmtSetDiscoverableCmd req(dev_id, state, timeout_sec);
713 MgmtStatus res = handleCurrentSettingsReply(sendWithReply(req), current_settings);
714 DBG_PRINT("BTManager::setDiscoverable[%d]: %s, result %s %s", dev_id,
715 req.toString().c_str(), to_string(res).c_str(), to_string(current_settings).c_str());
716 return res;
717}
718
719std::vector<MgmtDefaultParam> BTManager::readDefaultSysParam(const uint16_t dev_id) noexcept {
720 MgmtReadDefaultSysParamCmd req(dev_id);
721 std::unique_ptr<MgmtEvent> res = sendWithReply(req);
722 DBG_PRINT("BTManager::readDefaultSysParam[%d]: %s, result %s", dev_id,
723 req.toString().c_str(), res->toString().c_str());
724 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
725 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
726 if( MgmtStatus::SUCCESS == res1.getStatus() ) {
728 }
729 }
730 return std::vector<MgmtDefaultParam>();
731}
732
733HCIStatusCode BTManager::setPrivacy(const uint16_t dev_id, const uint8_t privacy, const jau::uint128dp_t& irk, AdapterSetting& current_settings) noexcept {
734 MgmtSetPrivacyCmd req(dev_id, privacy, irk);
735 MgmtStatus res = handleCurrentSettingsReply(sendWithReply(req), current_settings);
736 DBG_PRINT("BTManager::setPrivacy[%d]: %s, result %s %s", dev_id,
737 req.toString().c_str(), to_string(res).c_str(), to_string(current_settings).c_str());
738 return to_HCIStatusCode( res );
739}
740
742 const uint16_t conn_min_interval, const uint16_t conn_max_interval,
743 const uint16_t conn_latency, const uint16_t supervision_timeout) noexcept {
745 conn_min_interval, conn_max_interval,
746 conn_latency, supervision_timeout);
747 std::unique_ptr<MgmtEvent> res = sendWithReply(req);
748 DBG_PRINT("BTManager::setDefaultConnParam[%d]: %s, result %s", dev_id,
749 req.toString().c_str(), res->toString().c_str());
750 if( nullptr != res ) {
751 if( res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
752 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
753 return to_HCIStatusCode( res1.getStatus() );
754 } else if( res->getOpcode() == MgmtEvent::Opcode::CMD_STATUS ) {
755 const MgmtEvtCmdStatus &res1 = *static_cast<const MgmtEvtCmdStatus *>(res.get());
756 return to_HCIStatusCode( res1.getStatus() );
757 }
758 }
760}
761
762HCIStatusCode BTManager::uploadConnParam(const uint16_t dev_id, const BDAddressAndType & addressAndType,
763 const uint16_t conn_min_interval, const uint16_t conn_max_interval,
764 const uint16_t conn_latency, const uint16_t supervision_timeout) noexcept {
765 MgmtConnParam connParam{ addressAndType.address, addressAndType.type, conn_min_interval, conn_max_interval, conn_latency, supervision_timeout };
766 MgmtLoadConnParamCmd req(dev_id, connParam);
767 std::unique_ptr<MgmtEvent> res = sendWithReply(req);
768 if( nullptr != res ) {
769 if( res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
770 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
771 return to_HCIStatusCode( res1.getStatus() );
772 } else if( res->getOpcode() == MgmtEvent::Opcode::CMD_STATUS ) {
773 const MgmtEvtCmdStatus &res1 = *static_cast<const MgmtEvtCmdStatus *>(res.get());
774 return to_HCIStatusCode( res1.getStatus() );
775 }
776 }
778}
779
780bool BTManager::isValidLongTermKeyAddressAndType(const EUI48 &address, const BDAddressType &address_type) const noexcept {
781 if constexpr ( USE_LINUX_BT_SECURITY ) {
782 // Linux Kernel `load_long_term_keys(..)` (mgmt.c) require either `BDAddressType::BDADDR_LE_PUBLIC` or
783 // `BDAddressType::BDADDR_LE_RANDOM` and `BLERandomAddressType::STATIC_PUBLIC`
784 // in ltk_is_valid(..) (mgmt.c).
785 if( BDAddressType::BDADDR_LE_PUBLIC == address_type ) {
786 return true;
787 } else if( BDAddressType::BDADDR_LE_RANDOM == address_type &&
789 return true;
790 } else {
791 return false;
792 }
793 } else {
794 return true;
795 }
796}
797
798HCIStatusCode BTManager::uploadLongTermKey(const uint16_t dev_id, const jau::darray<MgmtLongTermKey> &keys) noexcept {
799 if constexpr ( USE_LINUX_BT_SECURITY ) {
800 // const bool is_valid_ltk_addr = isValidLongTermKeyAddressAndType(key.address, key.address_type);
801 MgmtLoadLongTermKeyCmd req(dev_id, keys);
802 HCIStatusCode res;
803 std::unique_ptr<MgmtEvent> reply = sendWithReply(req);
804 if( nullptr != reply ) {
805 if( reply->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
806 res = to_HCIStatusCode( static_cast<const MgmtEvtCmdComplete *>(reply.get())->getStatus() );
807 } else if( reply->getOpcode() == MgmtEvent::Opcode::CMD_STATUS ) {
808 res = to_HCIStatusCode( static_cast<const MgmtEvtCmdStatus *>(reply.get())->getStatus() );
809 } else {
811 }
812 } else {
814 }
815 if( HCIStatusCode::SUCCESS != res ) {
816 WARN_PRINT("(dev_id %d): %s, result %s", dev_id,
817 req.toString().c_str(), to_string(res).c_str());
818 } else {
819 DBG_PRINT("BTManager::uploadLongTermKeyInfo(dev_id %d): %s, result %s", dev_id,
820 req.toString().c_str(), to_string(res).c_str());
821 }
822 return res;
823 } else {
825 }
826}
827
829 const uint16_t dev_id, const BDAddressAndType & addressAndType,
830 const jau::darray<SMPLongTermKey>& ltks) noexcept
831{
832 if constexpr ( USE_LINUX_BT_SECURITY ) {
834 for(SMPLongTermKey ltk : ltks) {
835 const MgmtLTKType key_type = to_MgmtLTKType(ltk.properties);
836 mgmt_keys.push_back( { addressAndType.address, addressAndType.type, key_type,
837 MgmtLongTermKey::to_role( adapterRole, ltk.isResponder() ),
838 ltk.enc_size, ltk.ediv, ltk.rand, ltk.ltk } );
839 }
840 return uploadLongTermKey(dev_id, mgmt_keys);
841 } else {
843 }
844}
845
847 if constexpr ( USE_LINUX_BT_SECURITY ) {
848 MgmtLoadIdentityResolvingKeyCmd req(dev_id, keys);
849 HCIStatusCode res;
850 std::unique_ptr<MgmtEvent> reply = sendWithReply(req);
851 if( nullptr != reply ) {
852 if( reply->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
853 res = to_HCIStatusCode( static_cast<const MgmtEvtCmdComplete *>(reply.get())->getStatus() );
854 } else if( reply->getOpcode() == MgmtEvent::Opcode::CMD_STATUS ) {
855 res = to_HCIStatusCode( static_cast<const MgmtEvtCmdStatus *>(reply.get())->getStatus() );
856 } else {
858 }
859 } else {
861 }
862 if( HCIStatusCode::SUCCESS != res ) {
863 WARN_PRINT("(dev_id %d): %s, result %s", dev_id,
864 req.toString().c_str(), to_string(res).c_str());
865 } else {
866 DBG_PRINT("BTManager::uploadIdentityResolvingKeyInfo(dev_id %d): %s, result %s", dev_id,
867 req.toString().c_str(), to_string(res).c_str());
868 }
869 return res;
870 } else {
872 }
873}
874
876{
877 if constexpr ( USE_LINUX_BT_SECURITY ) {
879 for(SMPIdentityResolvingKey irk : irks) {
880 mgmt_keys.push_back( { irk.id_address, BDAddressType::BDADDR_LE_PUBLIC, irk.irk } );
881 }
882 return uploadIdentityResolvingKey(dev_id, mgmt_keys);
883 } else {
885 }
886}
887
889 if constexpr ( USE_LINUX_BT_SECURITY ) {
890 jau::darray<MgmtIdentityResolvingKey> mgmt_keys; // intentionally empty
891 // BDAddressAndType addressAndType(jau::EUI48(), BDAddressType::BDADDR_LE_PUBLIC);
892 // mgmt_keys.push_back( { addressAndType.address, addressAndType.type, jau::uint128_t() } );
893 return uploadIdentityResolvingKey(dev_id, mgmt_keys); // MgmtLoadIdentityResolvingKeyCmd -> hci_smp_irks_clear
894 } else {
896 }
897}
898
899HCIStatusCode BTManager::uploadLinkKey(const uint16_t dev_id, const MgmtLinkKeyInfo &key) noexcept {
900 if constexpr ( USE_LINUX_BT_SECURITY ) {
901 MgmtLoadLinkKeyCmd req(dev_id, false /* debug_keys */, key);
902 HCIStatusCode res;
903 std::unique_ptr<MgmtEvent> reply = sendWithReply(req);
904 if( nullptr != reply ) {
905 if( reply->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
906 res = to_HCIStatusCode( static_cast<const MgmtEvtCmdComplete *>(reply.get())->getStatus() );
907 } else if( reply->getOpcode() == MgmtEvent::Opcode::CMD_STATUS ) {
908 res = to_HCIStatusCode( static_cast<const MgmtEvtCmdStatus *>(reply.get())->getStatus() );
909 } else {
911 }
912 } else {
914 }
915 if( HCIStatusCode::SUCCESS != res ) {
916 WARN_PRINT("(dev_id %d): %s, result %s", dev_id,
917 req.toString().c_str(), to_string(res).c_str());
918 } else {
919 DBG_PRINT("BTManager::uploadLinkKeyInfo(dev_id %d): %s, result %s", dev_id,
920 req.toString().c_str(), to_string(res).c_str());
921 }
922 return res;
923 } else {
925 }
926}
927
928HCIStatusCode BTManager::uploadLinkKey(const uint16_t dev_id, const BDAddressAndType & addressAndType, const SMPLinkKey& lk) noexcept {
929 if constexpr ( USE_LINUX_BT_SECURITY ) {
930 const MgmtLinkKeyInfo mgmt_lk_info { addressAndType.address, addressAndType.type, static_cast<MgmtLinkKeyType>(lk.type),
931 lk.key, lk.pin_length };
932 return uploadLinkKey(dev_id, mgmt_lk_info);
933 } else {
935 }
936}
937
938MgmtStatus BTManager::userPINCodeReply(const uint16_t dev_id, const BDAddressAndType & addressAndType, const std::string& pinCode) noexcept {
939 if constexpr ( USE_LINUX_BT_SECURITY ) {
940 MgmtPinCodeReplyCmd cmd(dev_id, addressAndType, pinCode);
941 std::unique_ptr<MgmtEvent> res = sendWithReply(cmd);
942 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
943 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
944 // FIXME: Analyze address + addressType result?
945 return res1.getStatus();
946 }
947 return MgmtStatus::TIMEOUT;
948 } else {
950 }
951}
952
953MgmtStatus BTManager::userPINCodeNegativeReply(const uint16_t dev_id, const BDAddressAndType & addressAndType) noexcept {
954 if constexpr ( USE_LINUX_BT_SECURITY ) {
955 MgmtPinCodeNegativeReplyCmd cmd(dev_id, addressAndType);
956 std::unique_ptr<MgmtEvent> res = sendWithReply(cmd);
957 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
958 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
959 // FIXME: Analyze address + addressType result?
960 return res1.getStatus();
961 }
962 return MgmtStatus::TIMEOUT;
963 } else {
965 }
966}
967
968MgmtStatus BTManager::userPasskeyReply(const uint16_t dev_id, const BDAddressAndType & addressAndType, const uint32_t passkey) noexcept {
969 if constexpr ( USE_LINUX_BT_SECURITY ) {
970 MgmtUserPasskeyReplyCmd cmd(dev_id, addressAndType, passkey);
971 std::unique_ptr<MgmtEvent> res = sendWithReply(cmd);
972 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
973 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
974 // FIXME: Analyze address + addressType result?
975 return res1.getStatus();
976 }
977 return MgmtStatus::TIMEOUT;
978 } else {
980 }
981}
982
983MgmtStatus BTManager::userPasskeyNegativeReply(const uint16_t dev_id, const BDAddressAndType & addressAndType) noexcept {
984 if constexpr ( USE_LINUX_BT_SECURITY ) {
985 MgmtUserPasskeyNegativeReplyCmd cmd(dev_id, addressAndType);
986 std::unique_ptr<MgmtEvent> res = sendWithReply(cmd);
987 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
988 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
989 // FIXME: Analyze address + addressType result?
990 return res1.getStatus();
991 }
992 return MgmtStatus::TIMEOUT;
993 } else {
995 }
996}
997
998MgmtStatus BTManager::userConfirmReply(const uint16_t dev_id, const BDAddressAndType & addressAndType, const bool positive) noexcept {
999 if constexpr ( USE_LINUX_BT_SECURITY ) {
1000 std::unique_ptr<MgmtEvent> res;
1001 if( positive ) {
1002 MgmtUserConfirmReplyCmd cmd(dev_id, addressAndType);
1003 res = sendWithReply(cmd);
1004 } else {
1005 MgmtUserConfirmNegativeReplyCmd cmd(dev_id, addressAndType);
1006 res = sendWithReply(cmd);
1007 }
1008 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
1009 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
1010 // FIXME: Analyze address + addressType result?
1011 return res1.getStatus();
1012 }
1013 return MgmtStatus::TIMEOUT;
1014 } else {
1016 }
1017}
1018
1019HCIStatusCode BTManager::unpairDevice(const uint16_t dev_id, const BDAddressAndType & addressAndType, const bool disconnect) noexcept {
1020 if constexpr ( USE_LINUX_BT_SECURITY ) {
1021 MgmtUnpairDeviceCmd cmd(dev_id, addressAndType, disconnect);
1022 std::unique_ptr<MgmtEvent> res = sendWithReply(cmd);
1023
1024 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
1025 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
1026 // FIXME: Analyze address + addressType result?
1027 return to_HCIStatusCode( res1.getStatus() );
1028 }
1030 } else {
1032 }
1033}
1034
1035bool BTManager::isDeviceWhitelisted(const uint16_t dev_id, const BDAddressAndType & addressAndType) noexcept {
1036 auto it = whitelist.cbegin();
1037 for( auto end = whitelist.cend(); it != end; ++it) {
1038 std::shared_ptr<WhitelistElem> wle = *it;
1039 if( wle->dev_id == dev_id && wle->address_and_type == addressAndType ) {
1040 return true;
1041 }
1042 }
1043 return false;
1044}
1045
1046bool BTManager::addDeviceToWhitelist(const uint16_t dev_id, const BDAddressAndType & addressAndType, const HCIWhitelistConnectType ctype) noexcept {
1047 MgmtAddDeviceToWhitelistCmd req(dev_id, addressAndType, ctype);
1048
1049 // Check if already exist in our local whitelist first, reject if so ..
1050 if( isDeviceWhitelisted(dev_id, addressAndType) ) {
1051 ERR_PRINT("BTManager::addDeviceToWhitelist: Already in local whitelist, remove first: %s", req.toString().c_str());
1052 return false;
1053 }
1054 std::unique_ptr<MgmtEvent> res = sendWithReply(req);
1055 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
1056 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
1057 if( MgmtStatus::SUCCESS == res1.getStatus() ) {
1058 whitelist.push_back( std::make_shared<WhitelistElem>(dev_id, addressAndType, ctype) );
1059 return true;
1060 }
1061 }
1062 return false;
1063}
1064
1066#if 0
1067 jau::darray<std::shared_ptr<WhitelistElem>> whitelist_copy = whitelist;
1068 int count = 0;
1069 DBG_PRINT("BTManager::removeAllDevicesFromWhitelist.A: Start %zd elements", whitelist_copy.size());
1070
1071 for(auto it = whitelist_copy.cbegin(); it != whitelist_copy.cend(); ++it) {
1072 std::shared_ptr<WhitelistElem> wle = *it;
1073 removeDeviceFromWhitelist(wle->dev_id, wle->address, wle->address_type);
1074 ++count;
1075 }
1076#else
1077 size_type count = 0;
1078 DBG_PRINT("BTManager::removeAllDevicesFromWhitelist.B: Start %d elements", count);
1079 whitelist.clear();
1080 jau::for_each_const(adapters, [&](const std::shared_ptr<BTAdapter> & a) {
1081 if( removeDeviceFromWhitelist(a->dev_id, BDAddressAndType::ANY_BREDR_DEVICE) ) { // flush whitelist!
1082 ++count;
1083 }
1084 });
1085#endif
1086
1087 DBG_PRINT("BTManager::removeAllDevicesFromWhitelist: End: Removed %zu elements, remaining %zd elements",
1088 (size_t)count, whitelist.size());
1089 return count;
1090}
1091
1092bool BTManager::removeDeviceFromWhitelist(const uint16_t dev_id, const BDAddressAndType & addressAndType) noexcept {
1093 // Remove from our local whitelist first
1094 {
1095 auto it = whitelist.cbegin();
1096 for( auto end = whitelist.cend(); it != end; ) {
1097 std::shared_ptr<WhitelistElem> wle = *it;
1098 if( wle->dev_id == dev_id && wle->address_and_type == addressAndType ) {
1099 it = whitelist.erase(it);
1100 } else {
1101 ++it;
1102 }
1103 }
1104 }
1105
1106 // Actual removal
1107 MgmtRemoveDeviceFromWhitelistCmd req(dev_id, addressAndType);
1108 std::unique_ptr<MgmtEvent> res = sendWithReply(req);
1109 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
1110 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
1111 if( MgmtStatus::SUCCESS == res1.getStatus() ) {
1112 return true;
1113 }
1114 }
1115 return false;
1116}
1117
1118std::shared_ptr<ConnectionInfo> BTManager::getConnectionInfo(const uint16_t dev_id, const BDAddressAndType& addressAndType) noexcept {
1119 MgmtGetConnectionInfoCmd req(dev_id, addressAndType);
1120 std::unique_ptr<MgmtEvent> res = sendWithReply(req);
1121 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
1122 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
1123 if( MgmtStatus::SUCCESS == res1.getStatus() ) {
1124 std::shared_ptr<ConnectionInfo> result = res1.toConnectionInfo();
1125 return result;
1126 }
1127 }
1128 return nullptr;
1129}
1130
1131std::shared_ptr<NameAndShortName> BTManager::setLocalName(const uint16_t dev_id, const std::string & name, const std::string & short_name) noexcept {
1132 MgmtSetLocalNameCmd req (static_cast<uint16_t>(dev_id), name, short_name);
1133 std::unique_ptr<MgmtEvent> res = sendWithReply(req);
1134 if( nullptr != res && res->getOpcode() == MgmtEvent::Opcode::CMD_COMPLETE ) {
1135 const MgmtEvtCmdComplete &res1 = *static_cast<const MgmtEvtCmdComplete *>(res.get());
1136 if( MgmtStatus::SUCCESS == res1.getStatus() ) {
1137 std::shared_ptr<NameAndShortName> result = res1.toNameAndShortName();
1138
1139 // explicit LocalNameChanged event
1140 MgmtEvtLocalNameChanged e(static_cast<uint16_t>(dev_id), result->getName(), result->getShortName());
1141 sendMgmtEvent(e);
1142 return result;
1143 }
1144 }
1145 return nullptr;
1146}
1147
1148/***
1149 *
1150 * MgmtEventCallback section
1151 *
1152 */
1153
1155 [](const MgmtAdapterEventCallback &a, const MgmtAdapterEventCallback &b) -> bool { return a == b; };
1156
1158 [](const MgmtAdapterEventCallback &a, const MgmtAdapterEventCallback &b) -> bool { return a.getCallback() == b.getCallback(); };
1159
1161 [](const MgmtAdapterEventCallback &a, const MgmtAdapterEventCallback &b) -> bool { return a.getDevID() == b.getDevID(); };
1162
1163bool BTManager::addMgmtEventCallback(const int dev_id, const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept {
1164 if( !isValidMgmtEventCallbackListsIndex(opc) ) {
1165 ERR_PRINT("Opcode %s >= %d", MgmtEvent::getOpcodeString(opc).c_str(), mgmtAdapterEventCallbackLists.size());
1166 return false;
1167 }
1168 MgmtAdapterEventCallbackList &l = mgmtAdapterEventCallbackLists[static_cast<uint16_t>(opc)];
1169 /* const bool added = */ l.push_back_unique(MgmtAdapterEventCallback(dev_id, opc, cb), _mgmtAdapterEventCallbackEqComp_ID_CB);
1170 return true;
1171}
1173 if( !isValidMgmtEventCallbackListsIndex(opc) ) {
1174 ERR_PRINT("Opcode %s >= %d", MgmtEvent::getOpcodeString(opc).c_str(), mgmtAdapterEventCallbackLists.size());
1175 return 0;
1176 }
1177 MgmtAdapterEventCallbackList &l = mgmtAdapterEventCallbackLists[static_cast<uint16_t>(opc)];
1179 true /* all_matching */, _mgmtAdapterEventCallbackEqComp_CB);
1180}
1182 if( 0 > dev_id ) {
1183 // skip dev_id -1 case, use clearAllMgmtEventCallbacks() here
1184 return 0;
1185 }
1186 size_type count = 0;
1187 for(auto & l : mgmtAdapterEventCallbackLists) {
1188 count += l.erase_matching( MgmtAdapterEventCallback( dev_id, MgmtEvent::Opcode::INVALID, MgmtEventCallback() ),
1189 true /* all_matching */, _mgmtAdapterEventCallbackEqComp_ID);
1190 }
1191 return count;
1192}
1194 if( !isValidMgmtEventCallbackListsIndex(opc) ) {
1195 ERR_PRINT("Opcode %s >= %d", MgmtEvent::getOpcodeString(opc).c_str(), mgmtAdapterEventCallbackLists.size());
1196 return;
1197 }
1198 mgmtAdapterEventCallbackLists[static_cast<uint16_t>(opc)].clear();
1199}
1201 for(auto & mgmtAdapterEventCallbackList : mgmtAdapterEventCallbackLists) {
1202 mgmtAdapterEventCallbackList.clear();
1203 }
1204 mgmtChangedAdapterSetCallbackList.clear();
1205}
1206
1207void BTManager::processAdapterAdded(std::unique_ptr<MgmtEvent> e) noexcept {
1208 const uint16_t dev_id = e->getDevID();
1209
1210 std::unique_ptr<AdapterInfo> adapterInfo = readAdapterInfo(dev_id);
1211
1212 if( nullptr != adapterInfo ) {
1213 std::shared_ptr<BTAdapter> adapter = addAdapter( *adapterInfo );
1214 DBG_PRINT("BTManager::Adapter[%d] Added: Start %s, added %d", dev_id, adapter->toString().c_str());
1215 sendMgmtEvent(*e);
1216 DBG_PRINT("BTManager::Adapter[%d] Added: User_ %s", dev_id, adapter->toString().c_str());
1217 jau::for_each_fidelity(mgmtChangedAdapterSetCallbackList, [&](ChangedAdapterSetCallback &cb) {
1218 cb(true /* added */, adapter);
1219 });
1220 DBG_PRINT("BTManager::Adapter[%d] Added: End__ %s", dev_id, adapter->toString().c_str());
1221 } else {
1222 DBG_PRINT("BTManager::Adapter[%d] Added: InitAI failed", dev_id);
1223 }
1224}
1225void BTManager::processAdapterRemoved(std::unique_ptr<MgmtEvent> e) noexcept {
1226 const uint16_t dev_id = e->getDevID();
1227 std::shared_ptr<BTAdapter> ai = removeAdapter(dev_id);
1228 if( nullptr != ai ) {
1229 DBG_PRINT("BTManager::Adapter[%d] Removed: Start: %s", dev_id, ai->toString().c_str());
1230 sendMgmtEvent(*e);
1231 DBG_PRINT("BTManager::Adapter[%d] Removed: User_: %s", dev_id, ai->toString().c_str());
1232 jau::for_each_fidelity(mgmtChangedAdapterSetCallbackList, [&](ChangedAdapterSetCallback &cb) {
1233 cb(false /* added */, ai);
1234 });
1235 ai->close(); // issuing dtor on DBTAdapter
1236 DBG_PRINT("BTManager::Adapter[%d] Removed: End__: %s", dev_id, ai->toString().c_str());
1237 } else {
1238 DBG_PRINT("BTManager::Adapter[%d] Removed: RemoveAI failed", dev_id);
1239 }
1240}
1241void BTManager::mgmtEvNewSettingsCB(const MgmtEvent& e) noexcept {
1242 const MgmtEvtNewSettings &event = *static_cast<const MgmtEvtNewSettings *>(&e);
1243 std::shared_ptr<BTAdapter> adapter = getAdapter(event.getDevID());
1244 if( nullptr != adapter ) {
1245 const AdapterSetting old_settings = adapter->adapterInfo.getCurrentSettingMask();
1246 const AdapterSetting new_settings = adapter->adapterInfo.setCurrentSettingMask(event.getSettings());
1247 DBG_PRINT("BTManager:mgmt:NewSettings: Adapter[%d] %s -> %s - %s",
1248 event.getDevID(),
1249 to_string(old_settings).c_str(),
1250 to_string(new_settings).c_str(),
1251 e.toString().c_str());
1252 } else {
1253 DBG_PRINT("BTManager:mgmt:NewSettings: Adapter[%d] %s -> adapter not present - %s",
1254 event.getDevID(),
1255 to_string(event.getSettings()).c_str(),
1256 e.toString().c_str());
1257 }
1258}
1259
1260void BTManager::mgmtEventAnyCB(const MgmtEvent& e) noexcept {
1261 DBG_PRINT("BTManager:mgmt:Any: %s", e.toString().c_str());
1262 (void)e;
1263}
1264
1265/**
1266 * ChangedAdapterSetCallback handling
1267 */
1268
1270 [](const ChangedAdapterSetCallback& a, const ChangedAdapterSetCallback& b) -> bool { return a == b; };
1271
1272
1275 mgmtChangedAdapterSetCallbackList.push_back(l);
1276
1277 jau::for_each_fidelity(adapters, [&](std::shared_ptr<BTAdapter>& ai) {
1278 (*l_p)(true /* added */, ai);
1279 });
1280}
1282 return mgmtChangedAdapterSetCallbackList.erase_matching(l, true /* all_matching */, _changedAdapterSetCallbackEqComp);
1283}
1284
1288}
1291 return mgmtChangedAdapterSetCallbackList.erase_matching(l, true /* all_matching */, _changedAdapterSetCallbackEqComp);
1292}
1293
1295 const size_type count = mgmtChangedAdapterSetCallbackList.size();
1296 mgmtChangedAdapterSetCallbackList.clear();
1297 return count;
1298}
static MgmtAdapterEventCallbackList::equal_comparator _mgmtAdapterEventCallbackEqComp_CB
Definition: BTManager.cpp:1157
static MgmtAdapterEventCallbackList::equal_comparator _mgmtAdapterEventCallbackEqComp_ID_CB
Definition: BTManager.cpp:1154
static MgmtAdapterEventCallbackList::equal_comparator _mgmtAdapterEventCallbackEqComp_ID
Definition: BTManager.cpp:1160
static ChangedAdapterSetCallbackList::equal_comparator _changedAdapterSetCallbackEqComp
ChangedAdapterSetCallback handling.
Definition: BTManager.cpp:1269
Unique Bluetooth EUI48 address and BDAddressType tuple.
Definition: BTAddress.hpp:175
BLERandomAddressType getBLERandomAddressType() const noexcept
Returns the BLERandomAddressType.
Definition: BTAddress.hpp:297
static const BDAddressAndType ANY_BREDR_DEVICE
Using EUI48::ANY_DEVICE and BDAddressType::BDADDR_BREDR to match any BREDR device.
Definition: BTAddress.hpp:178
BTAdapter represents one local Bluetooth Controller.
Definition: BTAdapter.hpp:324
static BTGattEnv & get() noexcept
A thread safe singleton handler of the BTAdapter manager, e.g.
Definition: BTManager.hpp:204
bool addDeviceToWhitelist(const uint16_t dev_id, const BDAddressAndType &addressAndType, const HCIWhitelistConnectType ctype) noexcept
Add the given device to the adapter's autoconnect whitelist.
Definition: BTManager.cpp:1046
HCIStatusCode uploadLinkKey(const uint16_t dev_id, const MgmtLinkKeyInfo &key) noexcept
Definition: BTManager.cpp:899
MgmtStatus userPINCodeNegativeReply(const uint16_t dev_id, const BDAddressAndType &addressAndType) noexcept
Definition: BTManager.cpp:953
std::shared_ptr< ConnectionInfo > getConnectionInfo(const uint16_t dev_id, const BDAddressAndType &addressAndType) noexcept
Definition: BTManager.cpp:1118
void clearAllCallbacks() noexcept
Removes all MgmtEventCallbacks from all MgmtEvent::Opcode lists.
Definition: BTManager.cpp:1200
~BTManager() noexcept override
Definition: BTManager.cpp:524
HCIStatusCode clearIdentityResolvingKeys(const uint16_t dev_id) noexcept
Definition: BTManager.cpp:888
void close() noexcept
Definition: BTManager.cpp:529
size_type removeMgmtEventCallback(const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept
Returns count of removed given MgmtEventCallback from the named MgmtEvent::Opcode list.
Definition: BTManager.cpp:1172
HCIStatusCode setPrivacy(const uint16_t dev_id, const uint8_t privacy, const jau::uint128dp_t &irk, AdapterSetting &current_settings) noexcept
Definition: BTManager.cpp:733
MgmtStatus setDiscoverable(const uint16_t dev_id, const uint8_t state, const uint16_t timeout, AdapterSetting &current_settings) noexcept
Definition: BTManager.cpp:711
HCIStatusCode uploadConnParam(const uint16_t dev_id, const BDAddressAndType &addressAndType, const uint16_t conn_interval_min=12, const uint16_t conn_interval_max=12, const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 15)) noexcept
Uploads given connection parameter for given device to the kernel.
Definition: BTManager.cpp:762
HCIStatusCode unpairDevice(const uint16_t dev_id, const BDAddressAndType &addressAndType, const bool disconnect) noexcept
Definition: BTManager.cpp:1019
MgmtStatus userPasskeyReply(const uint16_t dev_id, const BDAddressAndType &addressAndType, const uint32_t passkey) noexcept
Definition: BTManager.cpp:968
bool setMode(const uint16_t dev_id, const MgmtCommand::Opcode opc, const uint8_t mode, AdapterSetting &current_settings) noexcept
Definition: BTManager.cpp:701
jau::nsize_t size_type
Definition: BTManager.hpp:211
std::shared_ptr< NameAndShortName > setLocalName(const uint16_t dev_id, const std::string &name, const std::string &short_name) noexcept
Definition: BTManager.cpp:1131
std::shared_ptr< BTAdapter > getAdapter(const uint16_t dev_id) const noexcept
Returns the DBTAdapter with the given dev_id, or nullptr if not found.
Definition: BTManager.cpp:581
void sendMgmtEvent(const MgmtEvent &event) noexcept
Manually send a MgmtEvent to all of its listeners.
Definition: BTManager.cpp:139
MgmtStatus userPasskeyNegativeReply(const uint16_t dev_id, const BDAddressAndType &addressAndType) noexcept
Definition: BTManager.cpp:983
HCIStatusCode uploadIdentityResolvingKey(const uint16_t dev_id, const jau::darray< MgmtIdentityResolvingKey > &keys) noexcept
Definition: BTManager.cpp:846
size_type removeAllDevicesFromWhitelist() noexcept
Remove all previously added devices from the autoconnect whitelist.
Definition: BTManager.cpp:1065
HCIStatusCode uploadLongTermKey(const uint16_t dev_id, const jau::darray< MgmtLongTermKey > &keys) noexcept
Definition: BTManager.cpp:798
MgmtStatus userPINCodeReply(const uint16_t dev_id, const BDAddressAndType &addressAndType, const std::string &pinCode) noexcept
Definition: BTManager.cpp:938
void addChangedAdapterSetCallback(const ChangedAdapterSetCallback &l)
ChangedAdapterSetCallback handling.
Definition: BTManager.cpp:1273
size_type removeAllChangedAdapterSetCallbacks() noexcept
Remove all added ChangedAdapterSetCallback entries from this manager.
Definition: BTManager.cpp:1294
HCIStatusCode initializeAdapter(AdapterInfo &adapterInfo, const uint16_t dev_id, const BTMode btMode, const bool powerOn) noexcept
Initialize the adapter with default values, including power-on.
Definition: BTManager.cpp:225
HCIStatusCode setDefaultConnParam(const uint16_t dev_id, const uint16_t conn_interval_min=8, const uint16_t conn_interval_max=40, const uint16_t conn_latency=0, const uint16_t supervision_timeout=getHCIConnSupervisorTimeout(0, 50)) noexcept
Set default connection parameter for given adapter to the kernel.
Definition: BTManager.cpp:741
bool isValidLongTermKeyAddressAndType(const EUI48 &address, const BDAddressType &address_type) const noexcept
Security commands.
Definition: BTManager.cpp:780
std::string toString() const noexcept override
Definition: BTManager.hpp:370
static const std::shared_ptr< BTManager > & get() noexcept
Retrieves the singleton instance.
Definition: BTManager.hpp:339
SMPIOCapability getIOCapability(const uint16_t dev_id) const noexcept
Definition: BTManager.cpp:671
std::vector< MgmtDefaultParam > readDefaultSysParam(const uint16_t dev_id) noexcept
Read default connection parameter for given adapter to the kernel.
Definition: BTManager.cpp:719
std::shared_ptr< BTAdapter > getDefaultAdapter() const noexcept
Returns the default AdapterInfo.
Definition: BTManager.cpp:571
bool addMgmtEventCallback(const int dev_id, const MgmtEvent::Opcode opc, const MgmtEventCallback &cb) noexcept
MgmtEventCallback handling
Definition: BTManager.cpp:1163
bool isDeviceWhitelisted(const uint16_t dev_id, const BDAddressAndType &addressAndType) noexcept
Returns true, if the adapter's device is already whitelisted.
Definition: BTManager.cpp:1035
size_type removeChangedAdapterSetCallback(const ChangedAdapterSetCallback &l)
Remove the given ChangedAdapterSetCallback from this manager.
Definition: BTManager.cpp:1281
void clearMgmtEventCallbacks(const MgmtEvent::Opcode opc) noexcept
Removes all MgmtEventCallbacks from the to the named MgmtEvent::Opcode list.
Definition: BTManager.cpp:1193
bool removeDeviceFromWhitelist(const uint16_t dev_id, const BDAddressAndType &addressAndType) noexcept
Remove the given device from the adapter's autoconnect whitelist.
Definition: BTManager.cpp:1092
MgmtStatus userConfirmReply(const uint16_t dev_id, const BDAddressAndType &addressAndType, const bool positive) noexcept
Definition: BTManager.cpp:998
void close() noexcept
Closing the HCI channel, locking mutex_write().
Definition: HCIComm.cpp:118
static HCIEnv & get() noexcept
Definition: HCIHandler.hpp:150
A thread safe singleton handler of the HCI control channel to one controller (BT adapter)
Definition: HCIHandler.hpp:179
static L2CAPEnv & get() noexcept
Definition: L2CAPComm.hpp:104
int getDevID() const noexcept
Unique adapter index filter or -1 to listen for all adapter.
Definition: MgmtTypes.hpp:2940
MgmtEventCallback & getCallback() noexcept
MgmtEventCallback reference.
Definition: MgmtTypes.hpp:2943
mgmt_addr_info { EUI48, uint8_t type }, uint8_t action
Definition: MgmtTypes.hpp:1119
static std::string getOpcodeString(const Opcode op) noexcept
Definition: MgmtTypes.cpp:251
std::string toString() const noexcept override
Definition: MgmtTypes.hpp:559
@ SET_SECURE_CONN
LE Secure Connections: 0x01 enables SC mixed, 0x02 enables SC only mode; Core Spec >= 4....
@ SET_SSP
Secure Simple Pairing: 0x00 disabled, 0x01 enable.
Managment Singleton runtime environment properties.
Definition: BTManager.hpp:67
uint16_t opcode, uint16_t dev-id, uint16_t param_size
Definition: MgmtTypes.hpp:1402
static std::unique_ptr< MgmtEvent > getSpecialized(const uint8_t *buffer, jau::nsize_t const buffer_size) noexcept
Return a newly created specialized instance pointer to base class.
Definition: MgmtTypes.cpp:409
static std::string getOpcodeString(const Opcode opc) noexcept
Definition: MgmtTypes.cpp:401
bool updateAdapterInfo(AdapterInfo &info) const noexcept
Definition: MgmtTypes.cpp:590
std::unique_ptr< AdapterInfo > toAdapterInfo() const noexcept
Definition: MgmtTypes.cpp:580
static jau::nsize_t getRequiredTotalSize() noexcept
Definition: MgmtTypes.hpp:2896
static MgmtStatus getStatus(const uint8_t *data)
Definition: MgmtTypes.hpp:1595
std::shared_ptr< NameAndShortName > toNameAndShortName() const noexcept
Convert this instance into ConnectionInfo if getCmdOpcode() == SET_LOCAL_NAME, getStatus() == SUCCESS...
Definition: MgmtTypes.cpp:553
const uint8_t * getData() const noexcept override
Definition: MgmtTypes.hpp:1612
bool getCurrentSettings(AdapterSetting &current_settings) const noexcept
Returns AdapterSetting if getCmdOpcode() expects a single 4-octet AdapterSetting and hence getDataSiz...
Definition: MgmtTypes.cpp:482
std::shared_ptr< ConnectionInfo > toConnectionInfo() const noexcept
Convert this instance into ConnectionInfo if getCmdOpcode() == GET_CONN_INFO, getStatus() == SUCCESS ...
Definition: MgmtTypes.cpp:524
jau::nsize_t getDataSize() const noexcept override
Definition: MgmtTypes.hpp:1611
MgmtStatus getStatus() const noexcept
Definition: MgmtTypes.hpp:1655
uint8_t name[MGMT_MAX_NAME_LENGTH]; uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
Definition: MgmtTypes.hpp:1718
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:924
uint16_t param_count 2 MgmtConnParam param[] 15 = 1x
Definition: MgmtTypes.hpp:1184
uint16_t key_count MgmtIdentityResolvingKey keys[key_count]
Definition: MgmtTypes.hpp:843
uint8_t debug_keys, uint16_t key_count, MgmtLinkKey keys[key_count]
Definition: MgmtTypes.hpp:640
uint16_t key_count MgmtLongTermKey keys[key_count]
Definition: MgmtTypes.hpp:699
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:978
mgmt_addr_info { EUI48, uint8_t type }, uint8_t pin_len, uint8_t pin_code[16]
Definition: MgmtTypes.hpp:937
static std::vector< MgmtDefaultParam > getParams(const uint8_t *data, const jau::nsize_t length) noexcept
Definition: MgmtTypes.cpp:327
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:1144
MgmtDefaultParamU16 param1, MgmtDefaultParamU16 param2,.
Definition: MgmtTypes.hpp:1354
uint8_t discoverable uint16_t timeout
Definition: MgmtTypes.hpp:579
uint8_t name[MGMT_MAX_NAME_LENGTH]; uint8_t short_name[MGMT_MAX_SHORT_NAME_LENGTH];
Definition: MgmtTypes.hpp:602
uint8_t privacy 0x00 disabled, 0x01 always on (discoverable + pairing), 0x02 limited (not when discov...
Definition: MgmtTypes.hpp:897
mgmt_addr_info { EUI48, uint8_t type }, bool disconnect (1 octet)
Definition: MgmtTypes.hpp:1028
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:1066
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:1055
mgmt_addr_info { EUI48, uint8_t type },
Definition: MgmtTypes.hpp:1103
mgmt_addr_info { EUI48, uint8_t type }, uint32_t passkey
Definition: MgmtTypes.hpp:1078
static SMPEnv & get() noexcept
Definition: SMPHandler.hpp:133
Transient read only and endian aware octet data, i.e.
Definition: octets.hpp:67
constexpr nsize_t size() const noexcept
Returns the used memory size for read and write operations, may be zero.
Definition: octets.hpp:162
constexpr uint8_t const * get_ptr() const noexcept
Definition: octets.hpp:272
Implementation of a Copy-On-Write (CoW) using jau::darray as the underlying storage,...
Definition: cow_darray.hpp:127
constexpr_atomic void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: cow_darray.hpp:811
constexpr_atomic void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: cow_darray.hpp:752
constexpr const_iterator cbegin() const noexcept
Returns an jau::cow_ro_iterator to the first element of this CoW storage.
Definition: cow_darray.hpp:637
bool(* equal_comparator)(const value_type &a, const value_type &b)
Generic value_type equal comparator to be user defined for e.g.
Definition: cow_darray.hpp:989
constexpr_atomic size_type size() const noexcept
Like std::vector::size().
Definition: cow_darray.hpp:717
std::make_signed< size_type >::type difference_type
Definition: cow_darray.hpp:144
constexpr_atomic bool push_back_unique(const value_type &x, equal_comparator comparator)
Like std::vector::push_back(), but only if the newly added element does not yet exist.
constexpr_atomic size_type erase_matching(const value_type &x, const bool all_matching, equal_comparator comparator)
Erase either the first matching element or all matching elements.
Implementation of a Copy-On-Write (CoW) read-onlu iterator over immutable value_type storage.
constexpr difference_type dist_begin() const noexcept
Returns the distance to_begin() using zero as first index.
constexpr cow_ro_iterator cbegin() const noexcept
Returns a new const_iterator pointing to the first element, aka begin.
constexpr bool is_end() const noexcept
Returns true, if this iterator points to cend().
Implementation of a dynamic linear array storage, aka vector.
Definition: darray.hpp:148
constexpr const_iterator cend() const noexcept
Definition: darray.hpp:711
constexpr size_type size() const noexcept
Like std::vector::size().
Definition: darray.hpp:763
constexpr void push_back(const value_type &x)
Like std::vector::push_back(), copy.
Definition: darray.hpp:1124
constexpr const_iterator cbegin() const noexcept
Definition: darray.hpp:705
constexpr void clear() noexcept
Like std::vector::clear(), but ending with zero capacity.
Definition: darray.hpp:904
Main jau environment class, supporting environment variable access and fetching elapsed time using it...
Definition: environment.hpp:74
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 void set_terminating() noexcept
Optional path to signal early termination, i.e.
Definition: environment.cpp:50
Service runner, a reusable dedicated thread performing custom user services.
bool stop() noexcept
Stops this service, if running.
bool shall_stop2(int dummy) noexcept
Helper function to easy FunctionDef usage w/o creating a lambda alike capture with same semantics as ...
static bool remove_sighandler() noexcept
Remove the sighandler.
static const ::pid_t pid_self
bool join() noexcept
Blocks the current thread until service is stopped or returns immediately if not running or called fr...
static bool singleton_sighandler() noexcept
Install the singleton SIGALRM sighandler instance.
static BTMode btMode
#define ERR_PRINT(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:109
#define PERF3_TS_TD(m)
Definition: debug.hpp:94
#define COND_PRINT(C,...)
Use for conditional plain messages, prefix '[elapsed_time] '.
Definition: debug.hpp:151
#define WORDY_PRINT(...)
Use for environment-variable environment::VERBOSE conditional verbose messages, prefix '[elapsed_time...
Definition: debug.hpp:68
#define ABORT(...)
Use for unconditional ::abort() call with given messages, prefix '[elapsed_time] ABORT @ file:line fu...
Definition: debug.hpp:101
#define PERF_TS_T0()
Definition: debug.hpp:79
#define DBG_PRINT(...)
Use for environment-variable environment::DEBUG conditional debug messages, prefix '[elapsed_time] De...
Definition: debug.hpp:52
#define PERF3_TS_T0()
Definition: debug.hpp:93
#define ERR_PRINT2(...)
Use for unconditional error messages, prefix '[elapsed_time] Error @ FILE:LINE FUNC: '.
Definition: debug.hpp:112
#define WARN_PRINT(...)
Use for unconditional warning messages, prefix '[elapsed_time] Warning @ FILE:LINE FUNC: '.
Definition: debug.hpp:123
#define PERF_TS_TD(m)
Definition: debug.hpp:80
constexpr UnaryFunction for_each_fidelity(InputIt first, InputIt last, UnaryFunction f)
Like jau::for_each(), see above.
constexpr UnaryFunction for_each_const(T &data, UnaryFunction f, std::enable_if_t< is_cow_type< T >::value, bool >=true) noexcept
constexpr uint16_t get_uint16(uint8_t const *buffer) noexcept
Returns a uint16_t value from the given byte address using packed_t to resolve a potential memory ali...
Definition: byte_util.hpp:661
lb_endian_t
Simplified reduced endian type only covering little- and big-endian.
Definition: byte_util.hpp:227
@ little
Identifier for little endian, equivalent to endian::little.
HCIStatusCode to_HCIStatusCode(const MgmtStatus mstatus) noexcept
Definition: MgmtTypes.cpp:648
constexpr const jau::fraction_i64 THREAD_SHUTDOWN_TIMEOUT_MS
Maximum time in fractions of seconds to wait for a thread shutdown.
Definition: DBTConst.hpp:73
MgmtLinkKeyType
Link Key Types compatible with Mgmt's MgmtLinkKeyInfo.
Definition: MgmtTypes.hpp:118
jau::function< void(const MgmtEvent &)> MgmtEventCallback
Definition: MgmtTypes.hpp:2918
MgmtLTKType
Long Term Key Types compatible with Mgmt's MgmtLongTermKey.
Definition: MgmtTypes.hpp:145
SMPIOCapability
Vol 3, Part H, 2.3.2 IO capabilities.
Definition: SMPTypes.hpp:209
MgmtLTKType to_MgmtLTKType(const SMPLongTermKey::Property ltk_prop_mask) noexcept
Definition: MgmtTypes.cpp:132
constexpr const bool USE_LINUX_BT_SECURITY
Definition: DBTConst.hpp:60
@ MGMT_HEADER_SIZE
Definition: MgmtTypes.hpp:78
@ MGMT_INDEX_NONE
Definition: MgmtTypes.hpp:73
@ UNSET
Denoting unset value, i.e.
void(* ChangedAdapterSetFunc)(bool added, std::shared_ptr< BTAdapter > &adapter)
Callback function to receive change events regarding the system's adapter set, e.g.
Definition: BTManager.hpp:166
BTMode
Bluetooth adapter operating mode.
Definition: BTTypes0.hpp:112
void clear() noexcept
Clears internal list.
HCIWhitelistConnectType
HCI Whitelist connection type.
Definition: BTTypes0.hpp:471
std::string to_string(const DiscoveryPolicy v) noexcept
Definition: BTAdapter.cpp:58
Entry * get(const EUI48 &addr, const std::string &name, AddressNameEntryMatchFunc m) noexcept
Returns a matching BTSecurityRegistry::Entry with the given addr and/or name.
AdapterSetting
Adapter Setting Bits.
Definition: BTTypes1.hpp:144
BDAddressType
BT Core Spec v5.2: Vol 3, Part C Generic Access Profile (GAP): 15.1.1.1 Public Bluetooth address.
Definition: BTAddress.hpp:60
jau::function< void(bool, std::shared_ptr< BTAdapter > &)> ChangedAdapterSetCallback
Callback jau::function to receive change events regarding the system's adapter set,...
Definition: BTManager.hpp:192
BTRole
Bluetooth roles from the perspective of the link layer (connection initiator).
Definition: BTTypes0.hpp:69
HCIStatusCode
BT Core Spec v5.2: Vol 1, Part F Controller Error Codes: 1.3 List of Error Codes.
Definition: HCITypes.hpp:138
constexpr uint8_t number(const DiscoveryPolicy rhs) noexcept
Definition: BTAdapter.hpp:100
@ STATIC_PUBLIC
Static public 'random' device address 0b11.
@ NONE
Zero mode, neither DUAL, BREDR nor LE.
@ DUAL
Dual Bluetooth mode, i.e.
@ LE
LE only Bluetooth mode.
@ BREDR
BREDR only Bluetooth mode.
@ BDADDR_LE_RANDOM
Bluetooth LE random address, see BLERandomAddressType.
@ BDADDR_LE_PUBLIC
Bluetooth LE public address.
@ Slave
Slave or peripheral role, advertising and waiting for connections to accept.
jau::function< R(A...)> bind_member(C1 *base, R(C0::*mfunc)(A...)) noexcept
Bind given class instance and non-void member function to an anonymous function using func_member_tar...
jau::function< R(A...)> bind_free(R(*func)(A...)) noexcept
Bind given non-void free-function to an anonymous function using func::free_target_t.
uint_fast32_t nsize_t
Natural 'size_t' alternative using uint_fast32_t as its natural sized type.
Definition: int_types.hpp:53
int_fast32_t snsize_t
Natural 'ssize_t' alternative using int_fast32_t as its natural sized type.
Definition: int_types.hpp:65
constexpr T max(const T x, const T y) noexcept
Returns the maximum of two integrals (w/ branching) in O(1)
Definition: base_math.hpp:191
std::string to_hexstring(value_type const &v) noexcept
Produce a lower-case hexadecimal string representation of the given pointer.
__pack(...): Produces MSVC, clang and gcc compatible lead-in and -out macros.
Definition: backtrace.hpp:32
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
STL namespace.
Used in MgmtLoadConnParamCmd and MgmtEvtNewConnectionParam.
Definition: MgmtTypes.hpp:1167
Used for MgmtLoadLinkKeyCmd and MgmtEvtNewLinkKey.
Definition: MgmtTypes.hpp:345
static constexpr uint8_t to_role(const BTRole adapterRole, const bool is_responder)
Definition: MgmtTypes.hpp:264
SMP Identity Resolving Key, used for platform agnostic persistence.
Definition: SMPTypes.hpp:636
Local SMP Link Key, used for platform agnostic persistence, mapping to platform specific MgmtLoadLink...
Definition: SMPTypes.hpp:824
SMP Long Term Key, used for platform agnostic persistence.
Definition: SMPTypes.hpp:552
A packed 48 bit EUI-48 identifier, formerly known as MAC-48 or simply network device MAC address (Med...
Definition: eui48.hpp:324
CXX_ALWAYS_INLINE bool compare_exchange_strong(_Tp &__e, _Tp __i) noexcept
A 128-bit packed uint8_t data array.
Definition: int_types.hpp:114
void clear() noexcept
Definition: int_types.hpp:114