summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2020-11-23 16:45:16 +0100
committerSven Gothel <[email protected]>2020-11-23 16:45:16 +0100
commit652e3fc523ac62abddc1afba8468ac601a153464 (patch)
tree577d68c4284a2431720acf7d9f1177e4d581acb7 /src
parentbb51c620dac82f4b0fb7769a7cdeafc2dc6f77cd (diff)
Enc/Auth: Allow full PairingMode modulation via BTSecurityLevel and SMPIOCapability: Like force JUST_WORKS by BTSecurityLevel::ENC_ONLY or SMPIOCapability::NO_INPUT_NO_OUTPUT
To allow non-auth encryption mode, user _must_ set BTSecurityLevel, SMPIOCapability or both appropriately. Otherwise BlueZ/Kernel will chose authenticated SMP negotiation. - DBTDevice::setConnSecurityLevel(): Will adjust SMPIOCapability automatically, if not yet set - DBTDevice::setConnIOCapability() and DBTDevice::setConnSecurity(): Perform plain setting - DBTDevice::processL2CAPSetup(): Sets BTSecurityLevel appropriately either if no-auth SMPIOCapability::NO_INPUT_NO_OUTPUT is chosen, or based on LE_Enc feature bit and/or SC capability. Using new HCI ENCRYPT_CHANGE and ENCRYPT_KEY_REFRESH_COMPLETE, for non-auth BTSecurityLevel::ENC_ONLY endpoint to set SMPPairingState::PROCESS_COMPLETED + PairingMode::JUST_WORKS. Note: In the non-auth legacy mode, the SMP (ACL.SMP) is not being used. ....
Diffstat (limited to 'src')
-rw-r--r--src/direct_bt/DBTAdapter.cpp138
-rw-r--r--src/direct_bt/DBTDevice.cpp141
2 files changed, 218 insertions, 61 deletions
diff --git a/src/direct_bt/DBTAdapter.cpp b/src/direct_bt/DBTAdapter.cpp
index bcc23d17..8d8da919 100644
--- a/src/direct_bt/DBTAdapter.cpp
+++ b/src/direct_bt/DBTAdapter.cpp
@@ -201,7 +201,9 @@ bool DBTAdapter::validateDevInfo() noexcept {
ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::CONNECT_FAILED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvConnectFailedHCI)) && ok;
ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_DISCONNECTED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceDisconnectedHCI)) && ok;
ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::DEVICE_FOUND, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvDeviceFoundHCI)) && ok;
- ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::LE_REMOTE_USER_FEATURES, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvLERemoteUserFeaturesHCI)) && ok;
+ ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_LE_REMOTE_USR_FEATURES, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvHCILERemoteUserFeaturesHCI)) && ok;
+ ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_CHANGED, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvHCIEncryptionChangedHCI)) && ok;
+ ok = hci.addMgmtEventCallback(MgmtEvent::Opcode::HCI_ENC_KEY_REFRESH_COMPLETE, jau::bindMemberFunc(this, &DBTAdapter::mgmtEvHCIEncryptionKeyRefreshCompleteHCI)) && ok;
if( !ok ) {
ERR_PRINT("Could not add all required MgmtEventCallbacks to HCIHandler: %s of %s", hci.toString().c_str(), toString().c_str());
@@ -309,6 +311,12 @@ void DBTAdapter::poweredOff() noexcept {
hci.setCurrentScanType(ScanType::NONE);
currentMetaScanType = ScanType::NONE;
+ // ensure all hci states are reset.
+ hci.clearAllStates();
+
+ io_capability_defaultval = SMPIOCapability::UNSET;
+ io_capability_device_ptr = nullptr;
+
DBG_PRINT("DBTAdapter::poweredOff: XXX");
}
@@ -332,20 +340,74 @@ std::shared_ptr<NameAndShortName> DBTAdapter::setLocalName(const std::string &na
}
bool DBTAdapter::setDiscoverable(bool value) noexcept {
- AdapterSetting current_settings;
+ AdapterSetting current_settings { AdapterSetting::NONE } ;
return MgmtStatus::SUCCESS == mgmt.setDiscoverable(dev_id, value ? 0x01 : 0x00, 10 /* timeout seconds */, current_settings);
}
bool DBTAdapter::setBondable(bool value) noexcept {
- AdapterSetting current_settings;
+ AdapterSetting current_settings { AdapterSetting::NONE } ;
return mgmt.setMode(dev_id, MgmtCommand::Opcode::SET_BONDABLE, value ? 1 : 0, current_settings);
}
bool DBTAdapter::setPowered(bool value) noexcept {
- AdapterSetting current_settings;
+ AdapterSetting current_settings { AdapterSetting::NONE } ;
return mgmt.setMode(dev_id, MgmtCommand::Opcode::SET_POWERED, value ? 1 : 0, current_settings);
}
+bool DBTAdapter::setConnIOCapability(const DBTDevice & device, const SMPIOCapability io_cap, const bool blocking) noexcept {
+ const DBTDevice * exp_null_device = nullptr; // C++11, exp as value since C++20
+ const uint32_t timeout_ms = 10000; // FIXME: Configurable?
+ const uint32_t poll_period_ms = 125;
+ uint32_t td = 0;
+ while( !io_capability_device_ptr.compare_exchange_strong(exp_null_device, &device) ) {
+ if( blocking ) {
+ if( timeout_ms <= td ) {
+ return false;
+ }
+ std::this_thread::sleep_for(std::chrono::milliseconds(poll_period_ms));
+ td += poll_period_ms;
+ } else {
+ return false;
+ }
+ }
+ SMPIOCapability o;
+ const bool res = mgmt.setIOCapability(dev_id, io_cap, o);
+ DBG_PRINT("DBTAdapter::setConnIOCapability: result %d: %s -> %s, %s",
+ res, getSMPIOCapabilityString(o).c_str(), getSMPIOCapabilityString(io_cap).c_str(),
+ device.toString(false).c_str());
+ if( res ) {
+ io_capability_defaultval = o;
+ return true;
+ } else {
+ io_capability_device_ptr = nullptr;
+ return false;
+ }
+}
+
+bool DBTAdapter::resetConnIOCapability(const DBTDevice & device) noexcept {
+ SMPIOCapability pre_io_cap { SMPIOCapability::UNSET };
+ return resetConnIOCapability(device, pre_io_cap);
+}
+
+bool DBTAdapter::resetConnIOCapability(const DBTDevice & device, SMPIOCapability& pre_io_cap) noexcept {
+ if( nullptr != io_capability_device_ptr && device == *io_capability_device_ptr ) {
+ const SMPIOCapability v = io_capability_defaultval;
+ io_capability_defaultval = SMPIOCapability::UNSET;
+ io_capability_device_ptr = nullptr;
+ SMPIOCapability o;
+ const bool res = mgmt.setIOCapability(dev_id, v, o);
+ DBG_PRINT("DBTAdapter::resetConnIOCapability: result %d: %s -> %s, %s",
+ res, getSMPIOCapabilityString(o).c_str(), getSMPIOCapabilityString(v).c_str(),
+ device.toString(false).c_str());
+ if( res ) {
+ pre_io_cap = o;
+ }
+ return res;
+ } else {
+ return false;
+ }
+}
+
HCIStatusCode DBTAdapter::reset() noexcept {
if( !isValid() ) {
ERR_PRINT("DBTAdapter::reset(): Adapter invalid: %s, %s", aptrHexString(this).c_str(), toString().c_str());
@@ -704,6 +766,7 @@ void DBTAdapter::removeDevice(DBTDevice & device) noexcept {
WORDY_PRINT("DBTAdapter::removeDevice: Start %s", toString(false).c_str());
const HCIStatusCode status = device.disconnect(HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION);
WORDY_PRINT("DBTAdapter::removeDevice: disconnect %s, %s", getHCIStatusCodeString(status).c_str(), toString(false).c_str());
+ resetConnIOCapability(device);
removeConnectedDevice(device); // usually done in DBTAdapter::mgmtEvDeviceDisconnectedHCI
removeDiscoveredDevice(device); // usually done in DBTAdapter::mgmtEvDeviceDisconnectedHCI
WORDY_PRINT("DBTAdapter::removeDevice: End %s", toString(false).c_str());
@@ -947,6 +1010,8 @@ bool DBTAdapter::mgmtEvDeviceConnectedHCI(std::shared_ptr<MgmtEvent> e) noexcept
new_connect = 3;
}
+ const SMPIOCapability io_cap_conn = mgmt.getIOCapability(dev_id);
+
EIRDataType updateMask = device->update(ad_report);
if( addConnectedDevice(device) ) { // track device, if not done yet
if( 0 == new_connect ) {
@@ -970,7 +1035,7 @@ bool DBTAdapter::mgmtEvDeviceConnectedHCI(std::shared_ptr<MgmtEvent> e) noexcept
device->toString().c_str());
}
- device->notifyConnected(device, event.getHCIHandle());
+ device->notifyConnected(device, event.getHCIHandle(), io_cap_conn);
int i=0;
jau::for_each_cow(statusListenerList, [&](std::shared_ptr<AdapterStatusListener> &l) {
@@ -1004,6 +1069,7 @@ bool DBTAdapter::mgmtEvConnectFailedHCI(std::shared_ptr<MgmtEvent> e) noexcept {
dev_id, event.toString().c_str(), jau::uint16HexString(handle).c_str(),
device->toString().c_str());
+ resetConnIOCapability(*device);
device->notifyDisconnected();
removeConnectedDevice(*device);
@@ -1028,8 +1094,8 @@ bool DBTAdapter::mgmtEvConnectFailedHCI(std::shared_ptr<MgmtEvent> e) noexcept {
return true;
}
-bool DBTAdapter::mgmtEvLERemoteUserFeaturesHCI(std::shared_ptr<MgmtEvent> e) noexcept {
- const MgmtEvtLERemoteUserFeatures &event = *static_cast<const MgmtEvtLERemoteUserFeatures *>(e.get());
+bool DBTAdapter::mgmtEvHCILERemoteUserFeaturesHCI(std::shared_ptr<MgmtEvent> e) noexcept {
+ const MgmtEvtHCILERemoteUserFeatures &event = *static_cast<const MgmtEvtHCILERemoteUserFeatures *>(e.get());
std::shared_ptr<DBTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
if( nullptr != device ) {
@@ -1045,6 +1111,31 @@ bool DBTAdapter::mgmtEvLERemoteUserFeaturesHCI(std::shared_ptr<MgmtEvent> e) noe
return true;
}
+bool DBTAdapter::mgmtEvHCIEncryptionChangedHCI(std::shared_ptr<MgmtEvent> e) noexcept {
+ const MgmtEvtHCIEncryptionChanged &event = *static_cast<const MgmtEvtHCIEncryptionChanged *>(e.get());
+
+ std::shared_ptr<DBTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
+ if( nullptr != device ) {
+ device->updatePairingState(device, SMPPairingState::PROCESS_COMPLETED, e);
+ } else {
+ WORDY_PRINT("DBTAdapter::EventHCI:EncryptionChanged(dev_id %d): Device not tracked: %s",
+ dev_id, event.toString().c_str());
+ }
+ return true;
+}
+bool DBTAdapter::mgmtEvHCIEncryptionKeyRefreshCompleteHCI(std::shared_ptr<MgmtEvent> e) noexcept {
+ const MgmtEvtHCIEncryptionKeyRefreshComplete &event = *static_cast<const MgmtEvtHCIEncryptionKeyRefreshComplete *>(e.get());
+
+ std::shared_ptr<DBTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType());
+ if( nullptr != device ) {
+ device->updatePairingState(device, SMPPairingState::PROCESS_COMPLETED, e);
+ } else {
+ WORDY_PRINT("DBTAdapter::EventHCI:EncryptionKeyRefreshComplete(dev_id %d): Device not tracked: %s",
+ dev_id, event.toString().c_str());
+ }
+ return true;
+}
+
bool DBTAdapter::mgmtEvDeviceDisconnectedHCI(std::shared_ptr<MgmtEvent> e) noexcept {
const MgmtEvtDeviceDisconnected &event = *static_cast<const MgmtEvtDeviceDisconnected *>(e.get());
@@ -1059,6 +1150,7 @@ bool DBTAdapter::mgmtEvDeviceDisconnectedHCI(std::shared_ptr<MgmtEvent> e) noexc
dev_id, event.toString().c_str(), jau::uint16HexString(event.getHCIHandle()).c_str(),
device->toString().c_str());
+ resetConnIOCapability(*device);
device->notifyDisconnected();
removeConnectedDevice(*device);
@@ -1215,15 +1307,8 @@ bool DBTAdapter::mgmtEvUserConfirmRequestMgmt(std::shared_ptr<MgmtEvent> e) noex
event.toString().c_str());
return true;
}
- // FIXME: Pass confirm_hint and value
- const SMPPairingState pstate = SMPPairingState::NUMERIC_COMPARE_EXPECTED;
-
- DBG_PRINT("DBTAdapter:mgmt:SMP: dev_id %d: address[%s, %s]: state %s, %s",
- dev_id, event.getAddress().toString().c_str(), getBDAddressTypeString(event.getAddressType()).c_str(),
- getSMPPairingStateString(pstate).c_str(),
- event.toString().c_str());
-
- updatePairingState(device, pstate, e->getTimestamp());
+ // FIXME: Pass confirm_hint and value?
+ device->updatePairingState(device, SMPPairingState::NUMERIC_COMPARE_EXPECTED, e);
return true;
}
bool DBTAdapter::mgmtEvUserPasskeyRequestMgmt(std::shared_ptr<MgmtEvent> e) noexcept {
@@ -1236,29 +1321,10 @@ bool DBTAdapter::mgmtEvUserPasskeyRequestMgmt(std::shared_ptr<MgmtEvent> e) noex
event.toString().c_str());
return true;
}
- const SMPPairingState pstate = SMPPairingState::PASSKEY_EXPECTED;
-
- DBG_PRINT("DBTAdapter:mgmt:SMP: dev_id %d: address[%s, %s]: state %s, %s",
- dev_id, event.getAddress().toString().c_str(), getBDAddressTypeString(event.getAddressType()).c_str(),
- getSMPPairingStateString(pstate).c_str(),
- event.toString().c_str());
-
- updatePairingState(device, pstate, e->getTimestamp());
+ device->updatePairingState(device, SMPPairingState::PASSKEY_EXPECTED, e);
return true;
}
-void DBTAdapter::updatePairingState(std::shared_ptr<DBTDevice> device, const SMPPairingState pstate, uint64_t timestamp) noexcept
-{
- PairingMode pmode;
- if( device->updatePairingState_locked(pstate, pmode) ) {
- sendDevicePairingState(device, pstate, pmode, timestamp);
- } else {
- WORDY_PRINT("DBTAdapter::updatePairingState: dev_id %d: Unchanged: state %s, %s", dev_id,
- getSMPPairingStateString(pstate).c_str(),
- device->toString().c_str());
- }
-}
-
bool DBTAdapter::hciSMPMsgCallback(const EUI48& address, BDAddressType addressType,
std::shared_ptr<const SMPPDUMsg> msg, const HCIACLData::l2cap_frame& source) noexcept {
std::shared_ptr<DBTDevice> device = findConnectedDevice(address, addressType);
diff --git a/src/direct_bt/DBTDevice.cpp b/src/direct_bt/DBTDevice.cpp
index 8c5b42a8..c52a9bf3 100644
--- a/src/direct_bt/DBTDevice.cpp
+++ b/src/direct_bt/DBTDevice.cpp
@@ -142,7 +142,8 @@ std::string DBTDevice::toString(bool includeDiscoveredServices) const noexcept {
std::string out("Device[address["+getAddressString()+", "+getBDAddressTypeString(getAddressType())+leaddrtype+"], name['"+name+
"'], age[total "+std::to_string(t0-ts_creation)+", ldisc "+std::to_string(t0-ts_last_discovery)+", lup "+std::to_string(t0-ts_last_update)+
"]ms, connected["+std::to_string(allowDisconnect)+"/"+std::to_string(isConnected)+", handle "+jau::uint16HexString(hciConnHandle)+
- "], security "+getBTSecurityLevelString(pairing_data.sec_level).c_str()+", pairing "+getPairingModeString(pairing_data.mode).c_str()+", rssi "+std::to_string(getRSSI())+
+ ", sec[lvl "+getBTSecurityLevelString(pairing_data.sec_level_conn).c_str()+", io "+getSMPIOCapabilityString(pairing_data.ioCap_conn).c_str()+
+ ", pairing "+getPairingModeString(pairing_data.mode).c_str()+"]], rssi "+std::to_string(getRSSI())+
", tx-power "+std::to_string(tx_power)+
", appearance "+jau::uint16HexString(static_cast<uint16_t>(appearance))+" ("+getAppearanceCatString(appearance)+
"), "+msdstr+", "+javaObjectToString()+"]");
@@ -388,14 +389,17 @@ HCIStatusCode DBTDevice::connectDefault()
}
}
-void DBTDevice::notifyConnected(std::shared_ptr<DBTDevice> sthis, const uint16_t handle) noexcept {
+void DBTDevice::notifyConnected(std::shared_ptr<DBTDevice> sthis, const uint16_t handle, const SMPIOCapability io_cap) noexcept {
// coming from connected callback, update state and spawn-off connectGATT in background if appropriate (LE)
- DBG_PRINT("DBTDevice::notifyConnected: handle %s -> %s, %s",
- jau::uint16HexString(hciConnHandle).c_str(), jau::uint16HexString(handle).c_str(), toString().c_str());
+ DBG_PRINT("DBTDevice::notifyConnected: handle %s -> %s, io %s -> %s, %s",
+ jau::uint16HexString(hciConnHandle).c_str(), jau::uint16HexString(handle).c_str(),
+ getSMPIOCapabilityString(pairing_data.ioCap_conn).c_str(), getSMPIOCapabilityString(io_cap).c_str(),
+ toString(false).c_str());
clearSMPStates();
allowDisconnect = true;
isConnected = true;
hciConnHandle = handle;
+ pairing_data.ioCap_conn = io_cap;
(void)sthis; // not used yet
}
@@ -411,13 +415,20 @@ void DBTDevice::notifyLEFeatures(std::shared_ptr<DBTDevice> sthis, const LEFeatu
}
void DBTDevice::processL2CAPSetup(std::shared_ptr<DBTDevice> sthis) {
- DBG_PRINT("DBTDevice::processL2CAPSetup: %s", toString().c_str());
+ // FIXME: Deadlocks SMP process while @ l2cap_att.setBTSecurityLevel() -> SMP + **MGMT::PASSKEY_REQ** -> l2cap_att.connect()-completion,
+ // i.e. l2cap_att.setBTSecurityLevel() (or its connect() process security callbacks.
+ // const std::lock_guard<std::mutex> lock(mtx_pairing); // RAII-style acquire and relinquish via destructor
+ (void)sthis;
+ DBG_PRINT("DBTDevice::processL2CAPSetup: Start %s", toString(false).c_str());
if( isLEAddressType() && !l2cap_att.isOpen() ) {
const bool responderLikesEncryption = pairing_data.res_requested_sec || isLEFeaturesBitSet(le_features, LEFeatures::LE_Encryption);
const BTSecurityLevel sec_level_user = pairing_data.sec_level_user;
- BTSecurityLevel sec_level = pairing_data.sec_level;
+ const SMPIOCapability io_cap = pairing_data.ioCap_conn;
+ BTSecurityLevel sec_level { BTSecurityLevel::UNSET };
if( BTSecurityLevel::UNSET != sec_level_user ) {
sec_level = sec_level_user;
+ } else if( SMPIOCapability::NO_INPUT_NO_OUTPUT == io_cap ) {
+ sec_level = BTSecurityLevel::ENC_ONLY; // no auth w/o I/O
} else {
if( responderLikesEncryption && adapter.hasSecureConnections() ) {
sec_level = BTSecurityLevel::ENC_AUTH_FIPS;
@@ -427,25 +438,26 @@ void DBTDevice::processL2CAPSetup(std::shared_ptr<DBTDevice> sthis) {
sec_level = BTSecurityLevel::NONE;
}
}
- const bool l2cap_open = l2cap_att.open(*this);
- const bool l2cap_sec = l2cap_open && l2cap_att.setBTSecurityLevel(sec_level); // initiates hciSMPMsgCallback() if sec_level > BT_SECURITY_LOW
+ pairing_data.sec_level_conn = sec_level;
+ const bool l2cap_open = l2cap_att.open(*this, sec_level); // initiates hciSMPMsgCallback() if sec_level > BT_SECURITY_LOW
+ const bool l2cap_auth = l2cap_open && BTSecurityLevel::ENC_ONLY < sec_level;
#if SMP_SUPPORTED_BY_OS
- const bool smp_sec = connectSMP(sthis, sec_level);
+ const bool smp_auth = connectSMP(sthis, sec_level) && BTSecurityLevel::ENC_ONLY < sec_level;
#else
- const bool smp_sec = false;
+ const bool smp_auth = false;
#endif
- DBG_PRINT("DBTDevice::processL2CAPSetup: sec_level %s, connect[smp %d, l2cap[open %d, sec %d]], %s",
- getBTSecurityLevelString(sec_level).c_str(), smp_sec, l2cap_open, l2cap_sec, toString().c_str());
+ DBG_PRINT("DBTDevice::processL2CAPSetup: lvl %s, io %s, connect[smp auth %d, l2cap[open %d, auth %d]]",
+ getBTSecurityLevelString(sec_level).c_str(), getSMPIOCapabilityString(io_cap).c_str(),
+ smp_auth, l2cap_open, l2cap_auth);
+
+ adapter.resetConnIOCapability(*this);
+
if( !l2cap_open ) {
+ pairing_data.sec_level_conn = BTSecurityLevel::NONE;
disconnect(HCIStatusCode::INTERNAL_FAILURE);
- } else if( !l2cap_sec && !smp_sec ) {
- // No security and hence no SMP dialogue, i.e. hciSMPMsgCallback():
- pairing_data.sec_level = BTSecurityLevel::NONE;
- processDeviceReady(sthis, jau::getCurrentMilliseconds());
- } else {
- pairing_data.sec_level = sec_level;
}
}
+ DBG_PRINT("DBTDevice::processL2CAPSetup: End %s", toString(false).c_str());
}
void DBTDevice::processDeviceReady(std::shared_ptr<DBTDevice> sthis, const uint64_t timestamp) {
@@ -457,9 +469,15 @@ void DBTDevice::processDeviceReady(std::shared_ptr<DBTDevice> sthis, const uint6
}
}
-bool DBTDevice::updatePairingState_locked(SMPPairingState state, PairingMode& current_mode) noexcept {
+bool DBTDevice::updatePairingState(std::shared_ptr<DBTDevice> sthis, SMPPairingState state, std::shared_ptr<MgmtEvent> evt) noexcept {
const std::lock_guard<std::mutex> lock(mtx_pairing); // RAII-style acquire and relinquish via destructor
PairingMode mode = pairing_data.mode;
+ bool is_device_ready = false;
+
+ DBG_PRINT("DBTDevice::updatePairingState.0: state %s -> %s, %s, %s",
+ getSMPPairingStateString(pairing_data.state).c_str(), getSMPPairingStateString(state).c_str(),
+ evt->toString().c_str(), toString(false).c_str());
+
if( pairing_data.state != state ) {
// Potentially force update PairingMode by forced state change, assuming being the initiator.
// FIXME: Initiator and responder role might need more specific determination and documentation.
@@ -473,15 +491,39 @@ bool DBTDevice::updatePairingState_locked(SMPPairingState state, PairingMode& cu
case SMPPairingState::OOB_EXPECTED:
mode = PairingMode::OUT_OF_BAND;
break;
+ case SMPPairingState::PROCESS_COMPLETED:
+ if( BTSecurityLevel::ENC_ONLY >= pairing_data.sec_level_conn ) {
+ // no auth, done
+ mode = PairingMode::JUST_WORKS;
+ is_device_ready = true;
+ } else {
+ // requires SMP auth: reset, no change
+ state = pairing_data.state;
+ }
+ break;
default: // nop
break;
}
+ DBG_PRINT("DBTDevice::updatePairingState.1: state %s -> %s, mode %s -> %s, ready %d",
+ getSMPPairingStateString(pairing_data.state).c_str(), getSMPPairingStateString(state).c_str(),
+ getPairingModeString(pairing_data.mode).c_str(), getPairingModeString(mode).c_str(), is_device_ready);
+ }
+
+ if( pairing_data.state != state ) {
pairing_data.mode = mode;
pairing_data.state = state;
- current_mode = mode;
+
+ adapter.sendDevicePairingState(sthis, state, mode, evt->getTimestamp());
+
+ if( is_device_ready ) {
+ std::thread dc(&DBTDevice::processDeviceReady, this, sthis, evt->getTimestamp()); // @suppress("Invalid arguments")
+ dc.detach();
+ }
+ DBG_PRINT("DBTDevice::updatePairingState.2: Complete: state %s", getSMPPairingStateString(state).c_str());
return true;
+ } else {
+ DBG_PRINT("DBTDevice::updatePairingState.3: Unchanged: state %s", getSMPPairingStateString(state).c_str());
}
- current_mode = mode;
return false;
}
@@ -499,6 +541,7 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_
switch( opc ) {
case SMPPDUMsg::Opcode::PAIRING_FAILED: {
+#if 0
pmode = PairingMode::NONE;
pstate = SMPPairingState::FAILED;
pairing_data.res_requested_sec = false;
@@ -510,11 +553,14 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_
pairing_data.sec_level = sec_level;
}
const bool l2cap_close = l2cap_att.close();
- const bool l2cap_open = l2cap_att.open(*this);
+ const bool l2cap_open = l2cap_att.open(*this, sec_level);
is_device_ready = l2cap_open;
DBG_PRINT("DBTDevice:hci:SMP: l2cap ATT reopen: ready %d, sec_level %s, l2cap[close %d, open %d], %s",
is_device_ready, getBTSecurityLevelString(sec_level).c_str(), l2cap_close, l2cap_open, toString(false).c_str());
+#else
+ pstate = SMPPairingState::FAILED;
+#endif
} break;
@@ -600,10 +646,11 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_
return;
}
- DBG_PRINT("DBTDevice:hci:SMP: address[%s, %s]: state %s -> %s, mode %s -> %s, %s, %s",
+ DBG_PRINT("DBTDevice:hci:SMP: address[%s, %s]: state %s -> %s, mode %s -> %s, ready %d, %s, %s",
address.toString().c_str(), getBDAddressTypeString(addressType).c_str(),
getSMPPairingStateString(old_pstate).c_str(), getSMPPairingStateString(pstate).c_str(),
getPairingModeString(old_pmode).c_str(), getPairingModeString(pmode).c_str(),
+ is_device_ready,
msg->toString().c_str(), source.toString().c_str());
pairing_data.mode = pmode;
@@ -617,6 +664,46 @@ void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_
}
}
+bool DBTDevice::setConnSecurityLevel(const BTSecurityLevel sec_level, const bool blocking) noexcept {
+ DBG_PRINT("DBTDevice::setSecurityLevel: %s -> %s, %s",
+ getBTSecurityLevelString(pairing_data.sec_level_user).c_str(),
+ getBTSecurityLevelString(sec_level).c_str(), toString(false).c_str());
+
+ if( !isValid() || isConnected || allowDisconnect ) {
+ return false;
+ }
+ pairing_data.sec_level_user = sec_level;
+
+ if( BTSecurityLevel::ENC_ONLY >= sec_level && !adapter.isConnIOCapabilitySet() )
+ {
+ // Adjust SMPIOCapability to match the no_auth BTSecurityLevel <= BTSecurityLevel::ENC_ONLY,
+ // if connect hasn't been issued and setIOCapability() not used yet.
+ return setConnIOCapability( SMPIOCapability::NO_INPUT_NO_OUTPUT, blocking);
+ } else {
+ return true;
+ }
+}
+
+bool DBTDevice::setConnIOCapability(const SMPIOCapability io_cap, const bool blocking) noexcept {
+ if( !isValid() || isConnected || allowDisconnect ) {
+ return false;
+ }
+ return adapter.setConnIOCapability(*this, io_cap, blocking);
+}
+
+bool DBTDevice::setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap, const bool blocking) noexcept {
+ DBG_PRINT("DBTDevice::setConnSecurity: %s -> %s, %s, %s",
+ getBTSecurityLevelString(pairing_data.sec_level_user).c_str(),
+ getBTSecurityLevelString(sec_level).c_str(),
+ getSMPIOCapabilityString(io_cap).c_str(), toString(false).c_str());
+
+ if( !isValid() || isConnected || allowDisconnect ) {
+ return false;
+ }
+ pairing_data.sec_level_user = sec_level;
+ return adapter.setConnIOCapability(*this, io_cap, blocking);
+}
+
HCIStatusCode DBTDevice::setPairingPasskey(const uint32_t passkey) noexcept {
const std::lock_guard<std::mutex> lock(mtx_pairing); // RAII-style acquire and relinquish via destructor
@@ -668,9 +755,12 @@ HCIStatusCode DBTDevice::setPairingNumericComparison(const bool positive) noexce
void DBTDevice::clearSMPStates() noexcept {
const std::lock_guard<std::mutex> lock(mtx_pairing); // RAII-style acquire and relinquish via destructor
- pairing_data.sec_level = BTSecurityLevel::UNSET;
- pairing_data.mode = PairingMode::NONE;
+ pairing_data.ioCap_conn=SMPIOCapability::UNSET;
+ pairing_data.sec_level_conn = BTSecurityLevel::UNSET;
+ pairing_data.sec_level_user = BTSecurityLevel::UNSET;
+
pairing_data.state = SMPPairingState::NONE;
+ pairing_data.mode = PairingMode::NONE;
pairing_data.res_requested_sec = false;
pairing_data.authReqs_resp = SMPAuthReqs::NONE;
@@ -981,5 +1071,6 @@ exit:
}
void DBTDevice::remove() noexcept {
+ clearSMPStates();
adapter.removeDevice(*this);
}