diff options
-rw-r--r-- | api/direct_bt/BTAdapter.hpp | 43 | ||||
-rw-r--r-- | examples/dbt_constants.hpp | 2 | ||||
-rw-r--r-- | examples/dbt_peripheral00.cpp | 25 | ||||
-rw-r--r-- | java/jau/direct_bt/DBTAdapter.java | 3 | ||||
-rw-r--r-- | java/jni/direct_bt/DBTAdapter.cxx | 11 | ||||
-rw-r--r-- | java/org/direct_bt/BTAdapter.java | 18 | ||||
-rwxr-xr-x | scripts/run-native-example.sh | 1 | ||||
-rw-r--r-- | src/direct_bt/BTAdapter.cpp | 239 |
8 files changed, 233 insertions, 109 deletions
diff --git a/api/direct_bt/BTAdapter.hpp b/api/direct_bt/BTAdapter.hpp index 06d64478..cd3ba90a 100644 --- a/api/direct_bt/BTAdapter.hpp +++ b/api/direct_bt/BTAdapter.hpp @@ -332,12 +332,19 @@ namespace direct_bt { typedef jau::cow_darray<impl::StatusListenerPair> statusListenerList_t; statusListenerList_t statusListenerList; + // Storing SMPKeyBin entries, referenced by their remote address, i.e. BTDevice address. + std::string key_path; + typedef std::shared_ptr<SMPKeyBin> SMPKeyBinRef; + typedef jau::darray<SMPKeyBinRef> key_list_t; + key_list_t key_list; + DBGattServerRef gattServerData = nullptr; mutable std::mutex mtx_discoveredDevices; mutable std::mutex mtx_connectedDevices; mutable std::mutex mtx_discovery; mutable std::mutex mtx_sharedDevices; // final mutex of all BTDevice lifecycle + mutable std::mutex mtx_keys; mutable jau::sc_atomic_bool sync_data; bool updateDataFromHCI() noexcept; @@ -405,6 +412,13 @@ namespace direct_bt { std::shared_ptr<BTDevice> getSharedDevice(const BTDevice & device) noexcept; void removeSharedDevice(const BTDevice & device) noexcept; + static SMPKeyBinRef findSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress) noexcept; + static bool removeSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress, const bool remove_file, const std::string key_path_) noexcept; + SMPKeyBinRef findSMPKeyBin(BDAddressAndType const & remoteAddress) noexcept; + /** Adding a SMPKeyBin will remove previous entry. */ + bool addSMPKeyBin(const SMPKeyBinRef& key, const bool write_file) noexcept; + bool removeSMPKeyBin(BDAddressAndType const & remoteAddress, const bool remove_file) noexcept; + bool mgmtEvNewSettingsMgmt(const MgmtEvent& e) noexcept; void updateAdapterSettings(const bool off_thread, const AdapterSetting new_settings, const bool sendEvent, const uint64_t timestamp) noexcept; bool mgmtEvDeviceDiscoveringMgmt(const MgmtEvent& e) noexcept; @@ -650,20 +664,41 @@ namespace direct_bt { bool setSecureConnections(const bool enable) noexcept; /** + * Set the adapter's persistent storage directory for SMPKeyBin files. + * - if set, all SMPKeyBin instances will be managed and persistent. + * - if not set, all SMPKeyBin instances will be transient only. + * + * When called, all keys within the path will be loaded, + * i.e. issuing uploadKeys() for all keys belonging to this BTAdapter. + * + * Persistent SMPKeyBin management is only functional when BTAdapter is in BTRole::Slave peripheral mode. + * + * For each SMPKeyBin file one shared BTDevice in BTRole::Master will be instantiated + * when uploadKeys() is called. + * + * @param path persistent storage path to SMPKeyBin files + * @see uploadKeys() + */ + void setSMPKeyPath(const std::string path) noexcept; + + /** * Associate the given SMPKeyBin with the contained remote address, i.e. SMPKeyBin::getRemoteAddrAndType(). * * Further uploads the Long Term Key (LTK) and Link Key (LK) for a potential upcoming connection, * if they are contained in the given SMPKeyBin file. * * This method is provided to support BTRole::Slave peripheral adapter mode, - * allowing user to inject all required keys before a connection occurs. + * allowing user to inject all required keys after initialize() * - * FIXME: Pass keys to BTDevice instance after connection! + * One shared BTDevice in BTRole::Master is instantiated. * - * @param keys SMPKeyBin file + * @param bin SMPKeyBin instance, might be persistent in filesystem + * @param write if true, write file to persistent storage, otherwise not * @return HCIStatusCode::SUCCESS or an error state on failure + * @see setSMPKeyPath() + * @see BTDevice::uploadKeys() */ - HCIStatusCode setSMPKeyBin(const SMPKeyBin& keys) noexcept; + HCIStatusCode uploadKeys(SMPKeyBin& bin, const bool write) noexcept; /** * Initialize the adapter with default values, including power-on. diff --git a/examples/dbt_constants.hpp b/examples/dbt_constants.hpp index 2ff4aaa4..71d6f8f1 100644 --- a/examples/dbt_constants.hpp +++ b/examples/dbt_constants.hpp @@ -38,4 +38,6 @@ */ constexpr const char KEY_PATH[] = "keys"; +constexpr const char ADAPTER_KEY_PATH[] = "dbt_keys"; + #endif /* DBT_CONSTANTS_HPP */ diff --git a/examples/dbt_peripheral00.cpp b/examples/dbt_peripheral00.cpp index 09fa7a58..8fe81ff9 100644 --- a/examples/dbt_peripheral00.cpp +++ b/examples/dbt_peripheral00.cpp @@ -70,7 +70,6 @@ static bool SHOW_UPDATE_EVENTS = false; static bool startAdvertising(BTAdapter *a, std::string msg); static bool stopAdvertising(BTAdapter *a, std::string msg); -static void processConnectedDevice(std::shared_ptr<BTDevice> device); static void processReadyDevice(std::shared_ptr<BTDevice> device); static void processDisconnectedDevice(std::shared_ptr<BTDevice> device); @@ -216,9 +215,6 @@ class MyAdapterStatusListener : public AdapterStatusListener { void deviceConnected(std::shared_ptr<BTDevice> device, const uint16_t handle, const uint64_t timestamp) override { fprintf_td(stderr, "****** CONNECTED: %s\n", device->toString(true).c_str()); - std::thread sd(::processConnectedDevice, device); // @suppress("Invalid arguments") - sd.detach(); - (void)handle; (void)timestamp; } @@ -232,9 +228,6 @@ class MyAdapterStatusListener : public AdapterStatusListener { // next: deviceReady(..) break; case SMPPairingState::FAILED: { - const bool res = SMPKeyBin::remove(KEY_PATH, *device); - fprintf_td(stderr, "****** PAIRING_STATE: state %s; Remove key file %s, res %d\n", - to_string(state).c_str(), SMPKeyBin::getFilename(KEY_PATH, *device).c_str(), res); // next: deviceReady() or deviceDisconnected(..) } break; case SMPPairingState::REQUESTED_BY_RESPONDER: @@ -479,16 +472,6 @@ static bool stopAdvertising(BTAdapter *a, std::string msg) { return HCIStatusCode::SUCCESS == status; } -static void processConnectedDevice(std::shared_ptr<BTDevice> device) { - fprintf_td(stderr, "****** Connected Device: Start %s\n", device->toString().c_str()); - - // Already Connected - Can't Do! - // HCIStatusCode res = SMPKeyBin::readAndApply(KEY_PATH, *device, BTSecurityLevel::UNSET, true /* verbose */); - // fprintf_td(stderr, "****** CONNECTED: SMPKeyBin::readAndApply(..) result %s\n", to_string(res).c_str()); - - fprintf_td(stderr, "****** Connected Device: End %s\n", device->toString().c_str()); -} - static void processDisconnectedDevice(std::shared_ptr<BTDevice> device) { fprintf_td(stderr, "****** Disconnected Device: Start %s\n", device->toString().c_str()); @@ -505,8 +488,6 @@ static void processDisconnectedDevice(std::shared_ptr<BTDevice> device) { static void processReadyDevice(std::shared_ptr<BTDevice> device) { fprintf_td(stderr, "****** Processing Ready Device: Start %s\n", device->toString().c_str()); - SMPKeyBin::createAndWrite(*device, KEY_PATH, false /* overwrite */, true /* verbose */); - fprintf_td(stderr, "****** Processing Ready Device: End %s\n", device->toString().c_str()); } @@ -562,11 +543,7 @@ static bool initAdapter(std::shared_ptr<BTAdapter>& adapter) { fprintf_td(stderr, "initAdapter: Set Default LE PHY: status %s: Tx %s, Rx %s\n", to_string(res).c_str(), to_string(Tx).c_str(), to_string(Rx).c_str()); } - { - std::vector<SMPKeyBin> keys = SMPKeyBin::readAllForLocalAdapter(adapter->getAddressAndType(), KEY_PATH, true /* verbose_ */); - jau::nsize_t count = SMPKeyBin::applyAll(keys, *adapter); - fprintf_td(stderr, "initAdapter: Set %d SMPKeyBin, successfully taken %d SMPKeyBin entries\n", keys.size(), count); - } + adapter->setSMPKeyPath(ADAPTER_KEY_PATH); std::shared_ptr<AdapterStatusListener> asl( std::make_shared<MyAdapterStatusListener>() ); adapter->addStatusListener( asl ); diff --git a/java/jau/direct_bt/DBTAdapter.java b/java/jau/direct_bt/DBTAdapter.java index a6dd1254..a4d36c85 100644 --- a/java/jau/direct_bt/DBTAdapter.java +++ b/java/jau/direct_bt/DBTAdapter.java @@ -274,6 +274,9 @@ public class DBTAdapter extends DBTObject implements BTAdapter public native boolean setSecureConnections(final boolean enable); @Override + public native void setSMPKeyPath(final String path); + + @Override public final HCIStatusCode initialize() { return initialize(BTMode.DUAL); } diff --git a/java/jni/direct_bt/DBTAdapter.cxx b/java/jni/direct_bt/DBTAdapter.cxx index 524f2064..9af80f24 100644 --- a/java/jni/direct_bt/DBTAdapter.cxx +++ b/java/jni/direct_bt/DBTAdapter.cxx @@ -910,6 +910,17 @@ jboolean Java_jau_direct_1bt_DBTAdapter_setSecureConnections(JNIEnv *env, jobjec return JNI_FALSE; } +void Java_jau_direct_1bt_DBTAdapter_setSMPKeyPath(JNIEnv *env, jobject obj, jstring jpath) { + try { + BTAdapter *adapter = jau::getJavaUplinkObject<BTAdapter>(env, obj); + jau::JavaGlobalObj::check(adapter->getJavaObject(), E_FILE_LINE); + std::string path = jau::from_jstring_to_string(env, jpath); + adapter->setSMPKeyPath(path); + } catch(...) { + rethrow_and_raise_java_exception(env); + } +} + jbyte Java_jau_direct_1bt_DBTAdapter_initializeImpl(JNIEnv *env, jobject obj, jbyte jbtMode) { try { BTAdapter *adapter = jau::getJavaUplinkObject<BTAdapter>(env, obj); diff --git a/java/org/direct_bt/BTAdapter.java b/java/org/direct_bt/BTAdapter.java index f9d222f5..1dc32f17 100644 --- a/java/org/direct_bt/BTAdapter.java +++ b/java/org/direct_bt/BTAdapter.java @@ -477,6 +477,24 @@ public interface BTAdapter extends BTObject boolean setSecureConnections(final boolean enable); /** + * Set the adapter's persistent storage directory for {@link SMPKeyBin} files. + * - if set, all {@link SMPKeyBin} instances will be managed and persistent. + * - if not set, all {@link SMPKeyBin} instances will be transient only. + * + * When called, all keys within the path will be loaded, + * i.e. issuing {@link BTDevice#uploadKeys(SMPKeyBin, BTSecurityLevel) for all keys belonging to this BTAdapter. + * + * Persistent {@link SMPKeyBin} management is only functional when {@link BTAdapter} is in {@link BTRole#Slave} peripheral mode. + * + * For each {@link SMPKeyBin} file one shared {@link BTDevice} in {@link BTRole#Master} will be instantiated + * when uploadKeys() is called. + * + * @param path persistent storage path to {@link SMPKeyBin} files + * @see BTDevice#uploadKeys(SMPKeyBin, BTSecurityLevel) + */ + void setSMPKeyPath(final String path); + + /** * Initialize the adapter with default values, including power-on. * <p> * Method shall be issued on the desired adapter found via {@link BTManager.ChangedAdapterSetListener}. diff --git a/scripts/run-native-example.sh b/scripts/run-native-example.sh index 192c74b3..e7f93e62 100755 --- a/scripts/run-native-example.sh +++ b/scripts/run-native-example.sh @@ -172,6 +172,7 @@ runit() { echo LD_LIBRARY_PATH=`pwd`/lib $EXE_WRAPPER bin/${exename} "$@" mkdir -p keys + mkdir -p dbt_keys if [ "${run_setcap}" -eq "1" ]; then runit_setcap "$@" diff --git a/src/direct_bt/BTAdapter.cpp b/src/direct_bt/BTAdapter.cpp index cec26514..a033ee1c 100644 --- a/src/direct_bt/BTAdapter.cpp +++ b/src/direct_bt/BTAdapter.cpp @@ -336,6 +336,11 @@ void BTAdapter::close() noexcept { const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor sharedDevices.clear(); } + { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + key_list.clear(); + key_path.clear(); + } valid = false; DBG_PRINT("BTAdapter::close: XXX"); } @@ -448,88 +453,59 @@ bool BTAdapter::setSecureConnections(const bool enable) noexcept { return enable == isAdapterSettingBitSet(new_settings, AdapterSetting::SECURE_CONN); } -HCIStatusCode BTAdapter::setSMPKeyBin(const SMPKeyBin& keys) noexcept { - if( keys.getLocalAddrAndType() != adapterInfo.addressAndType ) { - if( keys.getVerbose() ) { +void BTAdapter::setSMPKeyPath(const std::string path) noexcept { + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + key_path = path; + + std::vector<SMPKeyBin> keys = SMPKeyBin::readAllForLocalAdapter(getAddressAndType(), key_path, jau::environment::get().debug /* verbose_ */); + for(SMPKeyBin f : keys) { + uploadKeys(f, false /* write */); + } +} + +HCIStatusCode BTAdapter::uploadKeys(SMPKeyBin& bin, const bool write) noexcept { + if( bin.getLocalAddrAndType() != adapterInfo.addressAndType ) { + if( bin.getVerbose() ) { jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Adapter address not matching: %s, %s", - keys.toString().c_str(), toString().c_str()); + bin.toString().c_str(), toString().c_str()); } return HCIStatusCode::INVALID_PARAMS; } - /** - if( isConnected ) { - ERR_PRINT("BTDevice::setLongTermKeyInfo: Already connected: %s", toString().c_str()); - return HCIStatusCode::CONNECTION_ALREADY_EXISTS; - } */ - // FIXME: Pass keys to BTDevice instance after connection! -#if USE_LINUX_BT_SECURITY + EInfoReport ad_report; + { + ad_report.setSource( EInfoReport::Source::NA ); + ad_report.setTimestamp( jau::getCurrentMilliseconds() ); + ad_report.setAddressType( bin.getRemoteAddrAndType().type ); + ad_report.setAddress( bin.getRemoteAddrAndType().address ); + } + // Enforce BTRole::Master on new device, + // since this functionality is only for local being BTRole::Slave peripheral! + std::shared_ptr<BTDevice> device = BTDevice::make_shared(*this, ad_report); + device->btRole = BTRole::Master; + addSharedDevice(device); + HCIStatusCode res; BTManager & mngr = getManager(); - res = mngr.unpairDevice(dev_id, keys.getRemoteAddrAndType(), false /* disconnect */); + res = mngr.unpairDevice(dev_id, bin.getRemoteAddrAndType(), false /* disconnect */); if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) { ERR_PRINT("BTAdapter::setSMPKeyBin: Unpair device failed: %s, %s", - keys.getRemoteAddrAndType().toString().c_str(), toString().c_str()); - } - if( keys.hasLTKInit() ) { - res = mngr.uploadLongTermKey(dev_id, keys.getRemoteAddrAndType(), keys.getLTKInit()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LTK initiator failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; - } + bin.getRemoteAddrAndType().toString().c_str(), toString().c_str()); } - if( keys.hasLTKResp() ) { - res = mngr.uploadLongTermKey(dev_id, keys.getRemoteAddrAndType(), keys.getLTKResp()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LTK responder failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; + + res = device->uploadKeys(bin, BTSecurityLevel::NONE); + if( HCIStatusCode::SUCCESS != res ) { + WARN_PRINT("(dev_id %d): Upload SMPKeyBin failed %s, %s (removing file)", + dev_id, to_string(res).c_str(), bin.toString().c_str()); + if( key_path.size() > 0 ) { + bin.remove(key_path); } - } - if( BDAddressType::BDADDR_BREDR != keys.getRemoteAddrAndType().type ) { - // Not supported - DBG_PRINT("BTAdapter::setSMPKeyBin: Upload LK for LE address not supported -> ignored"); + return res; } else { - if( BTRole::Master == btRole ) { - // Remote device is slave (peripheral), we are master (initiator) - if( keys.hasLKInit() ) { - res = mngr.uploadLinkKey(dev_id, keys.getRemoteAddrAndType(), keys.getLKInit()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LK initiator failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; - } - } - } else { - // Remote device is master (central), we are slave (peripheral) - if( keys.hasLKResp() ) { - res = mngr.uploadLinkKey(dev_id, keys.getRemoteAddrAndType(), keys.getLKResp()); - if( HCIStatusCode::SUCCESS != res ) { - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload LK responder failed: %s, %s", - keys.toString().c_str(), toString().c_str()); - } - return res; - } - } - } - } - if( keys.getVerbose() ) { - jau::PLAIN_PRINT(true, "BTAdapter::setSMPKeyBin: Upload OK: %s, %s", - keys.toString().c_str(), toString().c_str()); + DBG_PRINT("BTAdapter::setSMPKeyBin(dev_id %d): Upload OK: %s, %s", + dev_id, bin.toString().c_str(), toString().c_str()); } + addSMPKeyBin( std::make_shared<SMPKeyBin>(bin), write); // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS return HCIStatusCode::SUCCESS; -#elif SMP_SUPPORTED_BY_OS - return HCIStatusCode::NOT_SUPPORTED; -#else - return HCIStatusCode::NOT_SUPPORTED; -#endif } HCIStatusCode BTAdapter::initialize(const BTMode btMode) noexcept { @@ -1086,7 +1062,6 @@ bool BTAdapter::removeDiscoveredDevice(const BDAddressAndType & addressAndType) return false; } - int BTAdapter::removeDiscoveredDevices() noexcept { const std::lock_guard<std::mutex> lock(mtx_discoveredDevices); // RAII-style acquire and relinquish via destructor jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc @@ -1117,6 +1092,8 @@ jau::darray<std::shared_ptr<BTDevice>> BTAdapter::getDiscoveredDevices() const n return res; } +// ************************************************* + bool BTAdapter::addSharedDevice(std::shared_ptr<BTDevice> const &device) noexcept { const std::lock_guard<std::mutex> lock(mtx_sharedDevices); // RAII-style acquire and relinquish via destructor if( nullptr != findDevice(sharedDevices, *device) ) { @@ -1149,6 +1126,8 @@ std::shared_ptr<BTDevice> BTAdapter::findSharedDevice (const EUI48 & address, co return findDevice(sharedDevices, address, addressType); } +// ************************************************* + void BTAdapter::removeDevice(BTDevice & device) noexcept { WORDY_PRINT("DBTAdapter::removeDevice: Start %s", toString().c_str()); removeAllStatusListener(device); @@ -1168,6 +1147,61 @@ void BTAdapter::removeDevice(BTDevice & device) noexcept { // ************************************************* +BTAdapter::SMPKeyBinRef BTAdapter::findSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress) noexcept { + const jau::nsize_t size = keys.size(); + for (jau::nsize_t i = 0; i < size; ++i) { + SMPKeyBinRef& k = keys[i]; + if ( nullptr != k && remoteAddress == k->getRemoteAddrAndType() ) { + return k; + } + } + return nullptr; +} +bool BTAdapter::removeSMPKeyBin(key_list_t & keys, BDAddressAndType const & remoteAddress, const bool remove_file, const std::string key_path_) noexcept { + for (auto it = keys.begin(); it != keys.end(); ++it) { + const SMPKeyBinRef& k = *it; + if ( nullptr != k && remoteAddress == k->getRemoteAddrAndType() ) { + DBG_PRINT("BTAdapter::removeSMPKeyBin(file %d): %s", remove_file, k->toString().c_str()); + if( remove_file && key_path_.size() > 0 ) { + if( !k->remove(key_path_) ) { + WARN_PRINT("Failed removal of SMPKeyBin file: %s", k->getFilename(key_path_).c_str()); + } + } + keys.erase(it); + return true; + } + } + return false; +} +BTAdapter::SMPKeyBinRef BTAdapter::findSMPKeyBin(BDAddressAndType const & remoteAddress) noexcept { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + return findSMPKeyBin(key_list, remoteAddress); +} +bool BTAdapter::addSMPKeyBin(const SMPKeyBinRef& key, const bool write_file) noexcept { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + removeSMPKeyBin(key_list, key->getRemoteAddrAndType(), write_file /* remove_file */, key_path); + if( jau::environment::get().debug ) { + key->setVerbose(true); + DBG_PRINT("BTAdapter::addSMPKeyBin(file %d): %s", write_file, key->toString().c_str()); + } + key_list.push_back( key ); + if( write_file && key_path.size() > 0 ) { + if( !key->write(key_path, true /* overwrite */) ) { + WARN_PRINT("Failed write of SMPKeyBin file: %s", key->getFilename(key_path).c_str()); + } + } + return true; +} +bool BTAdapter::removeSMPKeyBin(BDAddressAndType const & remoteAddress, const bool remove_file) noexcept { + const std::lock_guard<std::mutex> lock(mtx_keys); // RAII-style acquire and relinquish via destructor + jau::sc_atomic_critical sync(sync_data); // redundant due to mutex-lock cache-load operation, leaving it for doc + return removeSMPKeyBin(key_list, remoteAddress, remove_file, key_path); +} + +// ************************************************* + HCIStatusCode BTAdapter::startAdvertising(DBGattServerRef gattServerData_, const uint16_t adv_interval_min, const uint16_t adv_interval_max, const AD_PDU_Type adv_type, @@ -1492,6 +1526,7 @@ bool BTAdapter::mgmtEvDeviceConnectedHCI(const MgmtEvent& e) noexcept { ad_report.read_data(event.getData(), event.getDataSize()); } int new_connect = 0; + bool slave_unpair = false; std::shared_ptr<BTDevice> device = findConnectedDevice(event.getAddress(), event.getAddressType()); if( nullptr == device ) { device = findDiscoveredDevice(event.getAddress(), event.getAddressType()); @@ -1505,25 +1540,30 @@ bool BTAdapter::mgmtEvDeviceConnectedHCI(const MgmtEvent& e) noexcept { if( nullptr != device ) { addDiscoveredDevice(device); // connected devices must be in shared + discovered list new_connect = 2; + slave_unpair = BTRole::Slave == getRole(); } } if( nullptr == device ) { + // (new_connect = 3) a whitelist auto-connect w/o previous discovery, or + // (new_connect = 4) we are a peripheral being connected by a remote client device = BTDevice::make_shared(*this, ad_report); addDiscoveredDevice(device); addSharedDevice(device); - if( BTRole::Slave == getRole() ) { - new_connect = 4; // slave adapter (peripheral), got connected by remote client - /** - * Without unpair in SC mode, the peripheral fails the DHKey Check. - * TODO: Provide a persistent pre-pairing mechanism via SMPKeyBin. - */ - HCIStatusCode res = mgmt.unpairDevice(dev_id, device->getAddressAndType(), false /* disconnect */); + new_connect = BTRole::Master == getRole() ? 3 : 4; + slave_unpair = BTRole::Slave == getRole(); + } + if( slave_unpair ) { + /** + * Without unpair in SC mode (or key pre-pairing), the peripheral fails the DHKey Check. + */ + const SMPKeyBinRef key = findSMPKeyBin( device->getAddressAndType() ); // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS + if( nullptr == key ) { + // No pre-pairing -> unpair + HCIStatusCode res = mgmt.unpairDevice(dev_id, device->getAddressAndType(), false /* disconnect */); if( HCIStatusCode::SUCCESS != res && HCIStatusCode::NOT_PAIRED != res ) { - WARN_PRINT("BTAdapter::EventHCI:DeviceConnected(dev_id %d, new_connect %d): Unpair device failed: %s, %s", + WARN_PRINT("(dev_id %d, new_connect %d): Unpair device failed: %s, %s", dev_id, new_connect, to_string(res).c_str(), device->getAddressAndType().toString().c_str()); } - } else { - new_connect = 3; // master adapter, whitelist auto-connect w/o previous discovery } } @@ -1672,7 +1712,7 @@ bool BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept { device->toString().c_str()); unlockConnect(*device); - device->notifyDisconnected(); + device->notifyDisconnected(); // -> unpair() removeConnectedDevice(*device); if( !device->isConnSecurityAutoEnabled() ) { @@ -1691,6 +1731,18 @@ bool BTAdapter::mgmtEvDeviceDisconnectedHCI(const MgmtEvent& e) noexcept { }); removeDiscoveredDevice(device->addressAndType); // ensure device will cause a deviceFound event after disconnect } + if( BTRole::Slave == getRole() ) { + // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS + SMPKeyBinRef key = findSMPKeyBin(device->getAddressAndType()); + if( nullptr != key ) { + const HCIStatusCode res = device->uploadKeys(*key, BTSecurityLevel::NONE); + if( HCIStatusCode::SUCCESS != res ) { + WARN_PRINT("(dev_id %d): Upload SMPKeyBin failed %s, %s (removing file)", + dev_id, to_string(res).c_str(), key->toString().c_str()); + removeSMPKeyBin(device->getAddressAndType(), true /* remove_file */); + } + } + } } else { WORDY_PRINT("BTAdapter::EventHCI:DeviceDisconnected(dev_id %d): Device not tracked: %s", dev_id, event.toString().c_str()); @@ -2080,6 +2132,31 @@ bool BTAdapter::hciSMPMsgCallback(const BDAddressAndType & addressAndType, void BTAdapter::sendDevicePairingState(std::shared_ptr<BTDevice> device, const SMPPairingState state, const PairingMode mode, uint64_t timestamp) noexcept { + if( BTRole::Slave == getRole() ) { + // PERIPHERAL_ADAPTER_MANAGES_SMP_KEYS + if( SMPPairingState::COMPLETED == state ) { + // Pairing completed + if( PairingMode::PRE_PAIRED != mode ) { + // newly paired -> store keys + SMPKeyBin key = SMPKeyBin::create(*device); + if( key.isValid() ) { + addSMPKeyBin( std::make_shared<SMPKeyBin>(key), true /* write_file */ ); + } + } else { + // pre-paired, refresh PairingData of BTDevice (perhaps a new instance) + const SMPKeyBinRef key = findSMPKeyBin( device->getAddressAndType() ); + if( nullptr != key ) { + bool res = device->setSMPKeyBin(*key); + if( !res ) { + WARN_PRINT("(dev_id %d): device::setSMPKeyBin() failed %d, %s", res, key->toString().c_str()); + } + } + } + } else if( SMPPairingState::FAILED == state ) { + // Pairing failed + removeSMPKeyBin(device->getAddressAndType(), true /* remove_file */); + } + } int i=0; jau::for_each_fidelity(statusListenerList, [&](impl::StatusListenerPair &p) { try { |