aboutsummaryrefslogtreecommitdiffstats
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
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. ....
-rw-r--r--api/direct_bt/DBTAdapter.hpp29
-rw-r--r--api/direct_bt/DBTDevice.hpp71
-rw-r--r--examples/direct_bt_scanner10/dbt_scanner10.cpp15
-rw-r--r--examples/java/DBTScanner10.java16
-rw-r--r--java/direct_bt/tinyb/DBTDevice.java31
-rw-r--r--java/jni/direct_bt/DBTDevice.cxx47
-rw-r--r--java/org/tinyb/BluetoothDevice.java66
-rw-r--r--java/tinyb/dbus/DBusDevice.java14
-rw-r--r--src/direct_bt/DBTAdapter.cpp138
-rw-r--r--src/direct_bt/DBTDevice.cpp141
10 files changed, 476 insertions, 92 deletions
diff --git a/api/direct_bt/DBTAdapter.hpp b/api/direct_bt/DBTAdapter.hpp
index 85d1a618..f6f159a5 100644
--- a/api/direct_bt/DBTAdapter.hpp
+++ b/api/direct_bt/DBTAdapter.hpp
@@ -214,6 +214,9 @@ namespace direct_bt {
std::atomic<ScanType> currentMetaScanType; // = ScanType::NONE
std::atomic<bool> keep_le_scan_alive; // = false;
+ jau::ordered_atomic<SMPIOCapability, std::memory_order_relaxed> io_capability_defaultval = SMPIOCapability::UNSET;
+ std::atomic<const DBTDevice *> io_capability_device_ptr = nullptr;
+
std::vector<std::shared_ptr<DBTDevice>> connectedDevices;
std::vector<std::shared_ptr<DBTDevice>> discoveredDevices; // all discovered devices
std::vector<std::shared_ptr<DBTDevice>> sharedDevices; // All active shared devices. Final holder of DBTDevice lifecycle!
@@ -246,10 +249,30 @@ namespace direct_bt {
uint16_t min_interval, uint16_t max_interval,
uint16_t latency, uint16_t supervision_timeout);
friend HCIStatusCode DBTDevice::connectBREDR(const uint16_t pkt_type, const uint16_t clock_offset, const uint8_t role_switch);
+ friend bool DBTDevice::setConnSecurityLevel(const BTSecurityLevel sec_level, const bool blocking) noexcept;
+ friend bool DBTDevice::setConnIOCapability(const SMPIOCapability io_cap, const bool blocking) noexcept;
+ friend bool DBTDevice::setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap, const bool blocking) noexcept;
+ friend void DBTDevice::processL2CAPSetup(std::shared_ptr<DBTDevice> sthis);
+ friend bool DBTDevice::updatePairingState(std::shared_ptr<DBTDevice> sthis, SMPPairingState state, std::shared_ptr<MgmtEvent> evt) noexcept;
friend void DBTDevice::hciSMPMsgCallback(std::shared_ptr<DBTDevice> sthis, std::shared_ptr<const SMPPDUMsg> msg, const HCIACLData::l2cap_frame& source) noexcept;
friend void DBTDevice::processDeviceReady(std::shared_ptr<DBTDevice> sthis, const uint64_t timestamp);
friend std::vector<std::shared_ptr<GATTService>> DBTDevice::getGATTServices() noexcept;
+ /**
+ * Sets the given SMPIOCapability for the next DBTDevice::connectLE() or DBTDevice::connectBREDR().
+ * <p>
+ * The SMPIOCapability value will be reset to its previous value when connection is completed or failed.
+ * </p>
+ * @param io_cap SMPIOCapability to be applied
+ * @param blocking if true, blocks until previous setting is completed,
+ * i.e. until connection has been completed or failed.
+ * Otherwise returns immediately with false if previous connection result is still pending.
+ */
+ bool setConnIOCapability(const DBTDevice & device, const SMPIOCapability io_cap, const bool blocking) noexcept;
+ bool resetConnIOCapability(const DBTDevice & device) noexcept;
+ bool resetConnIOCapability(const DBTDevice & device, SMPIOCapability& pre_io_cap) noexcept;
+ bool isConnIOCapabilitySet() const noexcept { return nullptr != io_capability_device_ptr; }
+
bool addConnectedDevice(const std::shared_ptr<DBTDevice> & device) noexcept;
bool removeConnectedDevice(const DBTDevice & device) noexcept;
int disconnectAllDevices(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION ) noexcept;
@@ -274,9 +297,12 @@ namespace direct_bt {
bool mgmtEvDeviceDiscoveringHCI(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvDeviceConnectedHCI(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvConnectFailedHCI(std::shared_ptr<MgmtEvent> e) noexcept;
- bool mgmtEvLERemoteUserFeaturesHCI(std::shared_ptr<MgmtEvent> e) noexcept;
+ bool mgmtEvHCILERemoteUserFeaturesHCI(std::shared_ptr<MgmtEvent> e) noexcept;
+ bool mgmtEvHCIEncryptionChangedHCI(std::shared_ptr<MgmtEvent> e) noexcept;
+ bool mgmtEvHCIEncryptionKeyRefreshCompleteHCI(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvDeviceDisconnectedHCI(std::shared_ptr<MgmtEvent> e) noexcept;
+
bool mgmtEvDeviceDiscoveringAny(std::shared_ptr<MgmtEvent> e, const bool hciSourced) noexcept;
bool mgmtEvPinCodeRequestMgmt(std::shared_ptr<MgmtEvent> e) noexcept;
@@ -285,7 +311,6 @@ namespace direct_bt {
bool mgmtEvAuthFailedMgmt(std::shared_ptr<MgmtEvent> e) noexcept;
bool mgmtEvDeviceUnpairedMgmt(std::shared_ptr<MgmtEvent> e) noexcept;
- void updatePairingState(std::shared_ptr<DBTDevice> device, const SMPPairingState pstate, uint64_t timestamp) noexcept;
bool hciSMPMsgCallback(const EUI48& address, BDAddressType addressType,
std::shared_ptr<const SMPPDUMsg> msg, const HCIACLData::l2cap_frame& source) noexcept;
void sendDevicePairingState(std::shared_ptr<DBTDevice> device, const SMPPairingState state, const PairingMode mode, uint64_t timestamp) noexcept;
diff --git a/api/direct_bt/DBTDevice.hpp b/api/direct_bt/DBTDevice.hpp
index 6e0f322f..759add9d 100644
--- a/api/direct_bt/DBTDevice.hpp
+++ b/api/direct_bt/DBTDevice.hpp
@@ -39,6 +39,7 @@
#include "HCIIoctl.hpp"
#include "HCIComm.hpp"
+#include "MgmtTypes.hpp"
#include "SMPHandler.hpp"
#include "GATTHandler.hpp"
@@ -80,10 +81,14 @@ namespace direct_bt {
std::atomic<bool> allowDisconnect; // allowDisconnect = isConnected || 'isConnectIssued'
struct PairingData {
- jau::ordered_atomic<BTSecurityLevel, std::memory_order_relaxed> sec_level=BTSecurityLevel::UNSET, sec_level_user=BTSecurityLevel::UNSET;
+ jau::ordered_atomic<SMPIOCapability, std::memory_order_relaxed> ioCap_conn=SMPIOCapability::UNSET;
+ jau::ordered_atomic<BTSecurityLevel, std::memory_order_relaxed> sec_level_conn=BTSecurityLevel::UNSET;
+ jau::ordered_atomic<BTSecurityLevel, std::memory_order_relaxed> sec_level_user=BTSecurityLevel::UNSET;
+
jau::ordered_atomic<SMPPairingState, std::memory_order_relaxed> state;
jau::ordered_atomic<PairingMode, std::memory_order_relaxed> mode;
jau::relaxed_atomic_bool res_requested_sec;
+
SMPAuthReqs authReqs_init, authReqs_resp;
SMPIOCapability ioCap_init, ioCap_resp;
SMPOOBDataFlag oobFlag_init, oobFlag_resp;
@@ -109,7 +114,7 @@ namespace direct_bt {
EIRDataType update(GattGenericAccessSvc const &data, const uint64_t timestamp) noexcept;
void notifyDisconnected() noexcept;
- void notifyConnected(std::shared_ptr<DBTDevice> sthis, const uint16_t handle) noexcept;
+ void notifyConnected(std::shared_ptr<DBTDevice> sthis, const uint16_t handle, const SMPIOCapability io_cap) noexcept;
void notifyLEFeatures(std::shared_ptr<DBTDevice> sthis, const LEFeatures features) noexcept;
/**
@@ -168,7 +173,7 @@ namespace direct_bt {
*/
bool connectGATT(std::shared_ptr<DBTDevice> sthis) noexcept;
- bool updatePairingState_locked(const SMPPairingState state, PairingMode& current_mode) noexcept;
+ bool updatePairingState(std::shared_ptr<DBTDevice> sthis, SMPPairingState state, std::shared_ptr<MgmtEvent> evt) noexcept;
/**
* Will be performed within disconnect() and notifyDisconnected().
@@ -425,14 +430,66 @@ namespace direct_bt {
HCIStatusCode disconnect(const HCIStatusCode reason=HCIStatusCode::REMOTE_USER_TERMINATED_CONNECTION ) noexcept;
/**
- * Set the overriding security level used at device connection.
+ * Set the BTSecurityLevel used to connect to this device.
+ * <p>
+ * Method returns false if this device has already being connected,
+ * or DBTDevice::connectLE() or DBTDevice::connectBREDR() has been issued already.
+ * </p>
+ * <p>
+ * To ensure consistent no authentication setup,<br>
+ * implementation will set SMPIOCapability::NO_INPUT_NO_OUTPUT if sec_level <= BTSecurityLevel::ENC_ONLY<br>
+ * and DBTDevice::setConnIOCapability() not used.
+ * </p>
+ * @param sec_level BTSecurityLevel to be applied
+ * @param blocking if true, blocks until previous SMPIOCapability setting is completed,
+ * i.e. until connection has been completed or failed.
+ * Otherwise returns immediately with false if previous connection result is still pending.
+ * @return
+ */
+ bool setConnSecurityLevel(const BTSecurityLevel sec_level, const bool blocking) noexcept;
+
+ /**
+ * Return the BTSecurityLevel, determined when connection is established.
+ */
+ BTSecurityLevel getConnSecurityLevel() const noexcept { return pairing_data.sec_level_conn; }
+
+ /**
+ * Sets the given SMPIOCapability used to connect to this device.
+ * <p>
+ * Method returns false if operation fails, this device has already being connected,
+ * or DBTDevice::connectLE() or DBTDevice::connectBREDR() has been issued already.
+ * </p>
+ * <p>
+ * The SMPIOCapability value will be reset to its previous value when connection is completed or failed.
+ * </p>
+ * @param io_cap SMPIOCapability to be applied
+ * @param blocking if true, blocks until previous SMPIOCapability setting is completed,
+ * i.e. until connection has been completed or failed.
+ * Otherwise returns immediately with false if previous connection result is still pending.
+ */
+ bool setConnIOCapability(const SMPIOCapability io_cap, const bool blocking=true) noexcept;
+
+ /**
+ * Sets the given BTSecurityLevel and SMPIOCapability used to connect to this device.
+ * <p>
+ * Method returns false if operation fails, this device has already being connected,
+ * or DBTDevice::connectLE() or DBTDevice::connectBREDR() has been issued already.
+ * </p>
+ * <p>
+ * The SMPIOCapability value will be reset to its previous value when connection is completed or failed.
+ * </p>
+ * @param sec_level BTSecurityLevel to be applied
+ * @param io_cap SMPIOCapability to be applied
+ * @param blocking if true, blocks until previous SMPIOCapability setting is completed,
+ * i.e. until connection has been completed or failed.
+ * Otherwise returns immediately with false if previous connection result is still pending.
*/
- void setSecurityLevel(const BTSecurityLevel sec_level) noexcept { pairing_data.sec_level_user = sec_level; }
+ bool setConnSecurity(const BTSecurityLevel sec_level, const SMPIOCapability io_cap, const bool blocking=true) noexcept;
/**
- * Return the currently set security level.
+ * Return the set SMPIOCapability value, determined when connection is established.
*/
- BTSecurityLevel getCurrentSecurityLevel() const noexcept { return pairing_data.sec_level; }
+ SMPIOCapability getConnIOCapability() const noexcept { return pairing_data.ioCap_conn; }
/**
* Method sets the given passkey entry, see PairingMode::PASSKEY_ENTRY_ini.
diff --git a/examples/direct_bt_scanner10/dbt_scanner10.cpp b/examples/direct_bt_scanner10/dbt_scanner10.cpp
index f757d422..c2773cf4 100644
--- a/examples/direct_bt_scanner10/dbt_scanner10.cpp
+++ b/examples/direct_bt_scanner10/dbt_scanner10.cpp
@@ -79,6 +79,7 @@ static std::vector<EUI48> waitForDevices;
const static uint32_t NO_PASSKEY = 0xffffffffU;
static uint32_t pairing_passkey = NO_PASSKEY;
static BTSecurityLevel sec_level = BTSecurityLevel::UNSET;
+static SMPIOCapability io_capabilities = SMPIOCapability::UNSET;
static void connectDiscoveredDevice(std::shared_ptr<DBTDevice> device);
@@ -362,9 +363,15 @@ class MyGATTEventListener : public AssociatedGATTCharacteristicListener {
static void connectDiscoveredDevice(std::shared_ptr<DBTDevice> device) {
fprintf(stderr, "****** Connecting Device: Start %s\n", device->toString().c_str());
device->getAdapter().stopDiscovery();
- if( BTSecurityLevel::UNSET < sec_level ) {
- device->setSecurityLevel(sec_level);
+
+ if( BTSecurityLevel::UNSET < sec_level && SMPIOCapability::UNSET != io_capabilities ) {
+ device->setConnSecurity(sec_level, io_capabilities, true /* blocking */);
+ } else if( BTSecurityLevel::UNSET < sec_level ) {
+ device->setConnSecurityLevel(sec_level, true /* blocking */);
+ } else if( SMPIOCapability::UNSET != io_capabilities ) {
+ device->setConnIOCapability(io_capabilities, true /* blocking */);
}
+
HCIStatusCode res;
if( !USE_WHITELIST ) {
res = device->connectDefault();
@@ -755,6 +762,8 @@ int main(int argc, char *argv[])
pairing_passkey = atoi(argv[++i]);
} else if( !strcmp("-seclevel", argv[i]) && argc > (i+1) ) {
sec_level = getBTSecurityLevel(atoi(argv[++i]));
+ } else if( !strcmp("-iocap", argv[i]) && argc > (i+1) ) {
+ io_capabilities = getSMPIOCapability(atoi(argv[++i]));
} else if( !strcmp("-charid", argv[i]) && argc > (i+1) ) {
charIdentifier = std::string(argv[++i]);
} else if( !strcmp("-charval", argv[i]) && argc > (i+1) ) {
@@ -779,7 +788,7 @@ int main(int argc, char *argv[])
"[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] "
"[-resetEachCon connectionCount] "
"(-mac <device_address>)* (-wl <device_address>)* "
- "[-passkey <digits>] [-seclevel <int>]"
+ "[-seclevel <int>] [-iocap <int>] [-passkey <digits>]"
"[-charid <uuid>] [-charval <byte-val>]"
"[-dbt_verbose true|false] "
"[-dbt_debug true|false|adapter.event,gatt.data,hci.event,mgmt.event] "
diff --git a/examples/java/DBTScanner10.java b/examples/java/DBTScanner10.java
index e1696bcd..44d7f9d6 100644
--- a/examples/java/DBTScanner10.java
+++ b/examples/java/DBTScanner10.java
@@ -57,6 +57,7 @@ import org.tinyb.GATTCharacteristicListener;
import org.tinyb.HCIStatusCode;
import org.tinyb.HCIWhitelistConnectType;
import org.tinyb.PairingMode;
+import org.tinyb.SMPIOCapability;
import org.tinyb.SMPPairingState;
import org.tinyb.ScanType;
import org.tinyb.BluetoothManager.ChangedAdapterSetListener;
@@ -79,6 +80,7 @@ public class DBTScanner10 {
static final int NO_PASSKEY = 0xffffffff;
int pairing_passkey = NO_PASSKEY;
BTSecurityLevel sec_level = BTSecurityLevel.UNSET;
+ SMPIOCapability io_capabilities = SMPIOCapability.UNSET;
long timestamp_t0;
@@ -289,9 +291,15 @@ public class DBTScanner10 {
final HCIStatusCode r = device.getAdapter().stopDiscovery();
println("****** Connecting Device: stopDiscovery result "+r);
}
- if( BTSecurityLevel.UNSET.value < sec_level.value ) {
- device.setSecurityLevel(sec_level);
+
+ if( BTSecurityLevel.UNSET.value < sec_level.value && SMPIOCapability.UNSET.value != io_capabilities.value ) {
+ device.setConnSecurity(sec_level, io_capabilities, true /* blocking */);
+ } else if( BTSecurityLevel.UNSET.value < sec_level.value ) {
+ device.setConnSecurityLevel(sec_level, true /* blocking */);
+ } else if( SMPIOCapability.UNSET.value != io_capabilities.value ) {
+ device.setConnIOCapability(io_capabilities, true /* blocking */);
}
+
HCIStatusCode res;
if( !USE_WHITELIST ) {
res = device.connect();
@@ -732,6 +740,8 @@ public class DBTScanner10 {
test.pairing_passkey = Integer.valueOf(args[++i]).intValue();
} else if( arg.equals("-seclevel") && args.length > (i+1) ) {
test.sec_level = BTSecurityLevel.get( (byte)Integer.valueOf(args[++i]).intValue() );
+ } else if( arg.equals("-iocap") && args.length > (i+1) ) {
+ test.io_capabilities = SMPIOCapability.get( (byte)Integer.valueOf(args[++i]).intValue() );
} else if( arg.equals("-charid") && args.length > (i+1) ) {
test.charIdentifier = args[++i];
} else if( arg.equals("-charval") && args.length > (i+1) ) {
@@ -755,7 +765,7 @@ public class DBTScanner10 {
"[-disconnect] [-enableGATTPing] [-count <number>] [-single] [-show_update_events] [-quiet] "+
"[-resetEachCon connectionCount] "+
"(-mac <device_address>)* (-wl <device_address>)* "+
- "[-passkey <digits>] [-seclevel <int>]" +
+ "[-seclevel <int>] [-iocap <int>] [-passkey <digits>]" +
"[-charid <uuid>] [-charval <byte-val>]"+
"[-verbose] [-debug] "+
"[-dbt_verbose true|false] "+
diff --git a/java/direct_bt/tinyb/DBTDevice.java b/java/direct_bt/tinyb/DBTDevice.java
index 425472ac..2895f123 100644
--- a/java/direct_bt/tinyb/DBTDevice.java
+++ b/java/direct_bt/tinyb/DBTDevice.java
@@ -47,6 +47,7 @@ import org.tinyb.EIRDataTypeSet;
import org.tinyb.GATTCharacteristicListener;
import org.tinyb.HCIStatusCode;
import org.tinyb.PairingMode;
+import org.tinyb.SMPIOCapability;
import org.tinyb.SMPPairingState;
public class DBTDevice extends DBTObject implements BluetoothDevice
@@ -334,16 +335,34 @@ public class DBTDevice extends DBTObject implements BluetoothDevice
}
@Override
- public final void setSecurityLevel(final BTSecurityLevel sec_level) {
- setSecurityLevelImpl(sec_level.value);
+ public final boolean setConnSecurityLevel(final BTSecurityLevel sec_level, final boolean blocking) {
+ return setConnSecurityLevelImpl(sec_level.value, blocking);
}
- private final native void setSecurityLevelImpl(final byte sec_level);
+ private final native boolean setConnSecurityLevelImpl(final byte sec_level, final boolean blocking);
@Override
- public final BTSecurityLevel getCurrentSecurityLevel() {
- return BTSecurityLevel.get( getCurrentSecurityLevelImpl() );
+ public final BTSecurityLevel getConnSecurityLevel() {
+ return BTSecurityLevel.get( getConnSecurityLevelImpl() );
}
- private final native byte getCurrentSecurityLevelImpl();
+ private final native byte getConnSecurityLevelImpl();
+
+ @Override
+ public final boolean setConnIOCapability(final SMPIOCapability io_cap, final boolean blocking) {
+ return setConnIOCapabilityImpl(io_cap.value, blocking);
+ }
+ private final native boolean setConnIOCapabilityImpl(final byte io_cap, final boolean blocking);
+
+ @Override
+ public final boolean setConnSecurity(final BTSecurityLevel sec_level, final SMPIOCapability io_cap, final boolean blocking) {
+ return setConnSecurityImpl(sec_level.value, io_cap.value, blocking);
+ }
+ private final native boolean setConnSecurityImpl(final byte sec_level, final byte io_cap, final boolean blocking);
+
+ @Override
+ public final SMPIOCapability getConnIOCapability() {
+ return SMPIOCapability.get( getConnIOCapabilityImpl() );
+ }
+ private final native byte getConnIOCapabilityImpl();
@Override
public HCIStatusCode setPairingPasskey(final int passkey) {
diff --git a/java/jni/direct_bt/DBTDevice.cxx b/java/jni/direct_bt/DBTDevice.cxx
index d691f4bb..ec4e6676 100644
--- a/java/jni/direct_bt/DBTDevice.cxx
+++ b/java/jni/direct_bt/DBTDevice.cxx
@@ -379,29 +379,68 @@ jbyte Java_direct_1bt_tinyb_DBTDevice_connectLEImpl1(JNIEnv *env, jobject obj,
}
-void Java_direct_1bt_tinyb_DBTDevice_setSecurityLevelImpl(JNIEnv *env, jobject obj, jbyte jsec_level) {
+jboolean Java_direct_1bt_tinyb_DBTDevice_setConnSecurityLevelImpl(JNIEnv *env, jobject obj, jbyte jsec_level, jboolean jblocking) {
try {
DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj);
JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
- device->setSecurityLevel( getBTSecurityLevel( static_cast<uint8_t>(jsec_level) ) );
+ return device->setConnSecurityLevel( getBTSecurityLevel( static_cast<uint8_t>(jsec_level) ), JNI_TRUE == jblocking );
} catch(...) {
rethrow_and_raise_java_exception(env);
}
+ return JNI_FALSE;
}
-jbyte Java_direct_1bt_tinyb_DBTDevice_getCurrentSecurityLevelImpl(JNIEnv *env, jobject obj) {
+jbyte Java_direct_1bt_tinyb_DBTDevice_getConnSecurityLevelImpl(JNIEnv *env, jobject obj) {
try {
DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj);
JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
- return number( device->getCurrentSecurityLevel() );
+ return number( device->getConnSecurityLevel() );
} catch(...) {
rethrow_and_raise_java_exception(env);
}
return number( BTSecurityLevel::UNSET );
}
+jboolean Java_direct_1bt_tinyb_DBTDevice_setConnIOCapabilityImpl(JNIEnv *env, jobject obj, jbyte jio_cap, jboolean jblocking) {
+ try {
+ DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj);
+ JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
+
+ return device->setConnIOCapability( getSMPIOCapability( static_cast<uint8_t>(jio_cap) ), JNI_TRUE == jblocking );
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return JNI_FALSE;
+}
+
+jboolean Java_direct_1bt_tinyb_DBTDevice_setConnSecurityImpl(JNIEnv *env, jobject obj, jbyte jsec_level, jbyte jio_cap, jboolean jblocking) {
+ try {
+ DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj);
+ JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
+
+ return device->setConnSecurity( getBTSecurityLevel( static_cast<uint8_t>(jsec_level) ),
+ getSMPIOCapability( static_cast<uint8_t>(jio_cap) ),
+ JNI_TRUE == jblocking );
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return JNI_FALSE;
+}
+
+jbyte Java_direct_1bt_tinyb_DBTDevice_getConnIOCapabilityImpl(JNIEnv *env, jobject obj) {
+ try {
+ DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj);
+ JavaGlobalObj::check(device->getJavaObject(), E_FILE_LINE);
+
+ return number( device->getConnIOCapability() );
+ } catch(...) {
+ rethrow_and_raise_java_exception(env);
+ }
+ return number( SMPIOCapability::UNSET );
+}
+
jbyte Java_direct_1bt_tinyb_DBTDevice_getPairingModeImpl(JNIEnv *env, jobject obj) {
try {
DBTDevice *device = getJavaUplinkObject<DBTDevice>(env, obj);
diff --git a/java/org/tinyb/BluetoothDevice.java b/java/org/tinyb/BluetoothDevice.java
index 34b3ea93..53355a51 100644
--- a/java/org/tinyb/BluetoothDevice.java
+++ b/java/org/tinyb/BluetoothDevice.java
@@ -194,18 +194,76 @@ public interface BluetoothDevice extends BluetoothObject
boolean pair() throws BluetoothException;
/**
- * Set the overriding security level used at device connection.
+ * Set the {@link BTSecurityLevel} used to connect to this device.
+ * <p>
+ * Method returns false if this device has already being connected,
+ * or {@link #connectLE(short, short, short, short, short, short) connectLE} or {@link #connect()} has been issued already.
+ * </p>
+ * <p>
+ * To ensure consistent no authentication setup,<br>
+ * implementation will set {@link SMPIOCapability#NO_INPUT_NO_OUTPUT} if sec_level <= {@link BTSecurityLevel#ENC_ONLY}<br>
+ * and {@link #setConnIOCapability(SMPIOCapability, boolean)} not used.
+ * </p>
+ * @param sec_level {@link BTSecurityLevel} to be applied
+ * @param blocking if true, blocks until previous {@link SMPIOCapability} setting is completed,
+ * i.e. until connection has been completed or failed.
+ * Otherwise returns immediately with false if previous connection result is still pending.
+ * @return
+ * @since 2.1.0
+ * @implNote not implemented in tinyb.dbus
+ */
+ boolean setConnSecurityLevel(final BTSecurityLevel sec_level, boolean blocking);
+
+ /**
+ * Return the {@link BTSecurityLevel}, determined when connection is established.
+ * @since 2.1.0
+ * @implNote not implemented in tinyb.dbus
+ */
+ BTSecurityLevel getConnSecurityLevel();
+
+ /**
+ * Sets the given {@link SMPIOCapability} used to connect to this device.
+ * <p>
+ * Method returns false if operation fails, this device has already being connected,
+ * or {@link #connectLE(short, short, short, short, short, short) connectLE} or {@link #connect()} has been issued already.
+ * </p>
+ * <p>
+ * The {@link SMPIOCapability} value will be reset to its previous value when connection is completed or failed.
+ * </p>
+ * @param io_cap {@link SMPIOCapability} to be applied
+ * @param blocking if true, blocks until previous {@link SMPIOCapability} setting is completed,
+ * i.e. until connection has been completed or failed.
+ * Otherwise returns immediately with false if previous connection result is still pending.
+ * @since 2.1.0
+ * @implNote not implemented in tinyb.dbus
+ */
+ boolean setConnIOCapability(final SMPIOCapability io_cap, final boolean blocking);
+
+ /**
+ * Sets the given {@link BTSecurityLevel} and {@link SMPIOCapability} used to connect to this device.
+ * <p>
+ * Method returns false if operation fails, this device has already being connected,
+ * or {@link #connectLE(short, short, short, short, short, short) connectLE} or {@link #connect()} has been issued already.
+ * </p>
+ * <p>
+ * The {@link SMPIOCapability} value will be reset to its previous value when connection is completed or failed.
+ * </p>
+ * @param sec_level {@link BTSecurityLevel} to be applied
+ * @param io_cap {@link SMPIOCapability} to be applied
+ * @param blocking if true, blocks until previous {@link SMPIOCapability} setting is completed,
+ * i.e. until connection has been completed or failed.
+ * Otherwise returns immediately with false if previous connection result is still pending.
* @since 2.1.0
* @implNote not implemented in tinyb.dbus
*/
- void setSecurityLevel(final BTSecurityLevel sec_level);
+ boolean setConnSecurity(final BTSecurityLevel sec_level, final SMPIOCapability io_cap, final boolean blocking);
/**
- * Return the currently set security level.
+ * Return the {@link SMPIOCapability} value, determined when connection is established.
* @since 2.1.0
* @implNote not implemented in tinyb.dbus
*/
- BTSecurityLevel getCurrentSecurityLevel();
+ SMPIOCapability getConnIOCapability();
/**
* Method sets the given passkey entry, see {@link PairingMode#PASSKEY_ENTRY_ini}.
diff --git a/java/tinyb/dbus/DBusDevice.java b/java/tinyb/dbus/DBusDevice.java
index e3ade052..526abe62 100644
--- a/java/tinyb/dbus/DBusDevice.java
+++ b/java/tinyb/dbus/DBusDevice.java
@@ -45,6 +45,7 @@ import org.tinyb.BluetoothUtils;
import org.tinyb.GATTCharacteristicListener;
import org.tinyb.HCIStatusCode;
import org.tinyb.PairingMode;
+import org.tinyb.SMPIOCapability;
import org.tinyb.SMPPairingState;
public class DBusDevice extends DBusObject implements BluetoothDevice
@@ -109,10 +110,19 @@ public class DBusDevice extends DBusObject implements BluetoothDevice
public native boolean pair() throws BluetoothException;
@Override
- public final void setSecurityLevel(final BTSecurityLevel sec_level) { } // FIXME
+ public final boolean setConnSecurityLevel(final BTSecurityLevel sec_level, final boolean blocking) { return false; } // FIXME
@Override
- public final BTSecurityLevel getCurrentSecurityLevel() { return BTSecurityLevel.UNSET; } // FIXME
+ public final BTSecurityLevel getConnSecurityLevel() { return BTSecurityLevel.UNSET; } // FIXME
+
+ @Override
+ public final boolean setConnIOCapability(final SMPIOCapability io_cap, final boolean blocking) { return false; } // FIXME
+
+ @Override
+ public final boolean setConnSecurity(final BTSecurityLevel sec_level, final SMPIOCapability io_cap, final boolean blocking) { return false; } // FIXME
+
+ @Override
+ public final SMPIOCapability getConnIOCapability() { return SMPIOCapability.UNSET; } // FIXME
@Override
public HCIStatusCode setPairingPasskey(final int passkey) { return HCIStatusCode.INTERNAL_FAILURE; }
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);
}