Skip to content

Commit c8666d8

Browse files
committed
Add asynchronous client secure
* Adds parameter `rc` to `NimBLEDevice::startSecurity`, default value works as the original method. * * `rc`: if not nullptr, will allow caller to obtain the internal return code. * Adds parameter `async` to `NimBLEClient::secureConnection`, default value works as the original method. * * `async`; if true, will send the secure command and return immediately with a true value for successfully sending the command, else false.
1 parent a59e8ee commit c8666d8

File tree

4 files changed

+153
-146
lines changed

4 files changed

+153
-146
lines changed

src/NimBLEClient.cpp

Lines changed: 114 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,8 @@ NimBLEClient::NimBLEClient(const NimBLEAddress& peerAddress)
6363
m_pClientCallbacks{&defaultCallbacks},
6464
m_connHandle{BLE_HS_CONN_HANDLE_NONE},
6565
m_terminateFailCount{0},
66-
m_deleteCallbacks{false},
67-
m_connEstablished{false},
68-
m_asyncConnect{false},
69-
m_exchangeMTU{true},
66+
m_asyncSecureAttempt{0},
67+
m_config{},
7068
# if CONFIG_BT_NIMBLE_EXT_ADV
7169
m_phyMask{BLE_GAP_LE_PHY_1M_MASK | BLE_GAP_LE_PHY_2M_MASK | BLE_GAP_LE_PHY_CODED_MASK},
7270
# endif
@@ -89,7 +87,7 @@ NimBLEClient::~NimBLEClient() {
8987
// Before we are finished with the client, we must release resources.
9088
deleteServices();
9189

92-
if (m_deleteCallbacks) {
90+
if (m_config.deleteCallbacks) {
9391
delete m_pClientCallbacks;
9492
}
9593
} // ~NimBLEClient
@@ -122,7 +120,7 @@ size_t NimBLEClient::deleteService(const NimBLEUUID& uuid) {
122120
}
123121

124122
return m_svcVec.size();
125-
} // deleteServices
123+
} // deleteService
126124

127125
/**
128126
* @brief Connect to the BLE Server using the address of the last connected device, or the address\n
@@ -174,16 +172,11 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
174172
return false;
175173
}
176174

177-
if (isConnected() || m_connEstablished) {
175+
if (isConnected()) {
178176
NIMBLE_LOGE(LOG_TAG, "Client already connected");
179177
return false;
180178
}
181179

182-
if (NimBLEDevice::isConnectionInProgress()) {
183-
NIMBLE_LOGE(LOG_TAG, "Connection already in progress");
184-
return false;
185-
}
186-
187180
const ble_addr_t* peerAddr = address.getBase();
188181
if (ble_gap_conn_find_by_addr(peerAddr, NULL) == 0) {
189182
NIMBLE_LOGE(LOG_TAG, "A connection to %s already exists", address.toString().c_str());
@@ -201,12 +194,9 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
201194
deleteServices();
202195
}
203196

204-
int rc = 0;
205-
m_asyncConnect = asyncConnect;
206-
m_exchangeMTU = exchangeMTU;
207-
208-
// Set the connection in progress flag to prevent a scan from starting while connecting.
209-
NimBLEDevice::setConnectionInProgress(true);
197+
int rc = 0;
198+
m_config.asyncConnect = asyncConnect;
199+
m_config.exchangeMTU = exchangeMTU;
210200

211201
do {
212202
# if CONFIG_BT_NIMBLE_EXT_ADV
@@ -261,11 +251,10 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
261251

262252
if (rc != 0) {
263253
m_lastErr = rc;
264-
NimBLEDevice::setConnectionInProgress(false);
265254
return false;
266255
}
267256

268-
if (m_asyncConnect) {
257+
if (m_config.asyncConnect) {
269258
return true;
270259
}
271260

@@ -289,19 +278,14 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
289278
rc = taskData.m_flags;
290279
if (rc != 0) {
291280
NIMBLE_LOGE(LOG_TAG, "Connection failed; status=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
292-
// If the failure was not a result of a disconnection, make sure we disconnect now to avoid dangling connections
293-
if (isConnected()) {
294-
disconnect();
295-
}
296281
m_lastErr = rc;
282+
if (m_config.deleteOnConnectFail) {
283+
NimBLEDevice::deleteClient(this);
284+
}
297285
return false;
298-
} else {
299-
NIMBLE_LOGI(LOG_TAG, "Connection established");
300286
}
301287

302-
m_connEstablished = true;
303288
m_pClientCallbacks->onConnect(this);
304-
305289
NIMBLE_LOGD(LOG_TAG, "<< connect()");
306290
// Check if still connected before returning
307291
return isConnected();
@@ -313,9 +297,21 @@ bool NimBLEClient::connect(const NimBLEAddress& address, bool deleteAttributes,
313297
* @return True on success.
314298
* @details This is a blocking function and should not be used in a callback.
315299
*/
316-
bool NimBLEClient::secureConnection() const {
300+
bool NimBLEClient::secureConnection(bool async) const {
317301
NIMBLE_LOGD(LOG_TAG, ">> secureConnection()");
318302

303+
int rc = 0;
304+
if (async && !NimBLEDevice::startSecurity(m_connHandle, &rc)) {
305+
m_lastErr = rc;
306+
m_asyncSecureAttempt = 0;
307+
return false;
308+
}
309+
310+
if (async) {
311+
m_asyncSecureAttempt++;
312+
return true;
313+
}
314+
319315
NimBLETaskData taskData(const_cast<NimBLEClient*>(this), BLE_HS_ENOTCONN);
320316
m_pTaskData = &taskData;
321317
int retryCount = 1;
@@ -357,7 +353,7 @@ bool NimBLEClient::disconnect(uint8_t reason) {
357353
* @brief Cancel an ongoing connection attempt.
358354
* @return True if the command was successfully sent.
359355
*/
360-
bool NimBLEClient::cancelConnect() {
356+
bool NimBLEClient::cancelConnect() const {
361357
int rc = ble_gap_conn_cancel();
362358
if (rc != 0 && rc != BLE_HS_EALREADY) {
363359
NIMBLE_LOGE(LOG_TAG, "ble_gap_conn_cancel failed: rc=%d %s", rc, NimBLEUtils::returnCodeToString(rc));
@@ -368,6 +364,32 @@ bool NimBLEClient::cancelConnect() {
368364
return true;
369365
} // cancelConnect
370366

367+
/**
368+
* @brief Set or unset a flag to delete this client when disconnected or connection failed.
369+
* @param [in] deleteOnDisconnect Set the client to self delete when disconnected.
370+
* @param [in] deleteOnConnectFail Set the client to self delete when a connection attempt fails.
371+
*/
372+
void NimBLEClient::setSelfDelete(bool deleteOnDisconnect, bool deleteOnConnectFail) {
373+
m_config.deleteOnDisconnect = deleteOnDisconnect;
374+
m_config.deleteOnConnectFail = deleteOnConnectFail;
375+
} // setSelfDelete
376+
377+
/**
378+
* @brief Get a copy of the clients configuration.
379+
* @return A copy of the clients configuration.
380+
*/
381+
NimBLEClient::Config NimBLEClient::getConfig() const {
382+
return m_config;
383+
} // getConfig
384+
385+
/**
386+
* @brief Set the client configuration options.
387+
* @param [in] config The config options instance to set the client configuration to.
388+
*/
389+
void NimBLEClient::setConfig(NimBLEClient::Config config) {
390+
m_config = config;
391+
} // setConfig
392+
371393
# if CONFIG_BT_NIMBLE_EXT_ADV
372394
/**
373395
* @brief Set the PHY types to use when connecting to a server.
@@ -492,9 +514,8 @@ uint16_t NimBLEClient::getConnHandle() const {
492514
* To disconnect from a peer, use disconnect().
493515
*/
494516
void NimBLEClient::clearConnection() {
495-
m_connHandle = BLE_HS_CONN_HANDLE_NONE;
496-
m_connEstablished = false;
497-
m_peerAddress = NimBLEAddress{};
517+
m_connHandle = BLE_HS_CONN_HANDLE_NONE;
518+
m_peerAddress = NimBLEAddress{};
498519
} // clearConnection
499520

500521
/**
@@ -509,15 +530,13 @@ void NimBLEClient::clearConnection() {
509530
* This enables the GATT Server to read the attributes of the client connected to it.
510531
*/
511532
bool NimBLEClient::setConnection(const NimBLEConnInfo& connInfo) {
512-
if (isConnected() || m_connEstablished) {
533+
if (isConnected()) {
513534
NIMBLE_LOGE(LOG_TAG, "Already connected");
514535
return false;
515536
}
516537

517-
m_peerAddress = connInfo.getAddress();
518-
m_connHandle = connInfo.getConnHandle();
519-
m_connEstablished = true;
520-
538+
m_peerAddress = connInfo.getAddress();
539+
m_connHandle = connInfo.getConnHandle();
521540
return true;
522541
} // setConnection
523542

@@ -763,6 +782,12 @@ int NimBLEClient::serviceDiscoveredCB(uint16_t connHandle,
763782
NimBLETaskData* pTaskData = (NimBLETaskData*)arg;
764783
NimBLEClient* pClient = (NimBLEClient*)pTaskData->m_pInstance;
765784

785+
if (error->status == BLE_HS_ENOTCONN) {
786+
NIMBLE_LOGE(LOG_TAG, "<< Service Discovered; Not connected");
787+
NimBLEUtils::taskRelease(*pTaskData, error->status);
788+
return error->status;
789+
}
790+
766791
// Make sure the service discovery is for this device
767792
if (pClient->getConnHandle() != connHandle) {
768793
return 0;
@@ -902,16 +927,23 @@ bool NimBLEClient::exchangeMTU() {
902927
* @param [in] arg A pointer to the client instance that registered for this callback.
903928
*/
904929
int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
905-
NimBLEClient* pClient = (NimBLEClient*)arg;
906-
int rc = 0;
930+
NimBLEClient* pClient = (NimBLEClient*)arg;
931+
int rc = 0;
932+
NimBLETaskData* pTaskData = pClient->m_pTaskData; // save a copy in case client is deleted
907933

908934
NIMBLE_LOGD(LOG_TAG, "Got Client event %s", NimBLEUtils::gapEventToString(event->type));
909935

910936
switch (event->type) {
911937
case BLE_GAP_EVENT_DISCONNECT: {
938+
// workaround for bug in NimBLE stack where disconnect event argument is not passed correctly
939+
pClient = NimBLEDevice::getClientByHandle(event->disconnect.conn.conn_handle);
940+
if (pClient == nullptr) {
941+
return 0;
942+
}
943+
912944
rc = event->disconnect.reason;
913945
// If Host reset tell the device now before returning to prevent
914-
// any errors caused by calling host functions before resyncing.
946+
// any errors caused by calling host functions before re-syncing.
915947
switch (rc) {
916948
case BLE_HS_ECONTROLLER:
917949
case BLE_HS_ETIMEOUT_HCI:
@@ -921,49 +953,49 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
921953
NimBLEDevice::onReset(rc);
922954
break;
923955
default:
924-
// Check that the event is for this client.
925-
if (pClient->m_connHandle != event->disconnect.conn.conn_handle) {
926-
return 0;
927-
}
928-
929956
break;
930957
}
931958

959+
NIMBLE_LOGD(LOG_TAG, "disconnect; reason=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
960+
932961
pClient->m_terminateFailCount = 0;
962+
pClient->m_asyncSecureAttempt = 0;
933963
NimBLEDevice::removeIgnored(pClient->m_peerAddress);
934-
pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
935964

936-
// If we received a connected event but did not get established
937-
// then a disconnect event will be sent but we should not send it to the
938-
// app for processing. Instead we will ensure the task is released
939-
// and report the error.
940-
if (!pClient->m_connEstablished) {
941-
break;
965+
// Don't call the disconnect callback if we are waiting for a connection to complete and it fails
966+
if (rc != (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT) || pClient->m_config.asyncConnect) {
967+
pClient->m_pClientCallbacks->onDisconnect(pClient, rc);
942968
}
943969

944-
NIMBLE_LOGI(LOG_TAG, "disconnect; reason=%d, %s", rc, NimBLEUtils::returnCodeToString(rc));
970+
pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
971+
972+
if (pClient->m_config.deleteOnDisconnect) {
973+
// If we are set to self delete on disconnect but we have a task waiting on the connection
974+
// completion we will set the flag to delete on connect fail instead of deleting here
975+
// to prevent segmentation faults or double deleting
976+
if (pTaskData != nullptr && rc == (BLE_HS_ERR_HCI_BASE + BLE_ERR_CONN_ESTABLISHMENT)) {
977+
pClient->m_config.deleteOnConnectFail = true;
978+
break;
979+
}
980+
NimBLEDevice::deleteClient(pClient);
981+
}
945982

946-
pClient->m_connEstablished = false;
947-
pClient->m_pClientCallbacks->onDisconnect(pClient, rc);
948983
break;
949984
} // BLE_GAP_EVENT_DISCONNECT
950985

951986
case BLE_GAP_EVENT_CONNECT: {
952987
// If we aren't waiting for this connection response we should drop the connection immediately.
953-
if (pClient->isConnected() || (!pClient->m_asyncConnect && pClient->m_pTaskData == nullptr)) {
988+
if (pClient->isConnected() || (!pClient->m_config.asyncConnect && pClient->m_pTaskData == nullptr)) {
954989
ble_gap_terminate(event->connect.conn_handle, BLE_ERR_REM_USER_CONN_TERM);
955990
return 0;
956991
}
957992

958-
NimBLEDevice::setConnectionInProgress(false);
959993
rc = event->connect.status;
960994
if (rc == 0) {
961-
NIMBLE_LOGI(LOG_TAG, "Connected event");
962-
963995
pClient->m_connHandle = event->connect.conn_handle;
964-
if (pClient->m_exchangeMTU) {
965-
if (!pClient->exchangeMTU() && !pClient->m_asyncConnect) {
966-
rc = pClient->m_lastErr;
996+
if (pClient->m_config.exchangeMTU) {
997+
if (!pClient->exchangeMTU() && !pClient->m_config.asyncConnect) {
998+
rc = pClient->m_lastErr; // sets the error in the task data
967999
break;
9681000
}
9691001
}
@@ -973,15 +1005,19 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
9731005
NimBLEDevice::addIgnored(pClient->m_peerAddress);
9741006
} else {
9751007
pClient->m_connHandle = BLE_HS_CONN_HANDLE_NONE;
976-
if (!pClient->m_asyncConnect) {
1008+
if (!pClient->m_config.asyncConnect) {
9771009
break;
9781010
}
1011+
1012+
if (pClient->m_config.deleteOnConnectFail) { // async connect
1013+
delete pClient;
1014+
return 0;
1015+
}
9791016
}
9801017

981-
if (pClient->m_asyncConnect) {
982-
pClient->m_connEstablished = rc == 0;
1018+
if (pClient->m_config.asyncConnect) {
9831019
pClient->m_pClientCallbacks->onConnect(pClient);
984-
} else if (!pClient->m_exchangeMTU) {
1020+
} else if (!pClient->m_config.exchangeMTU) {
9851021
break; // not waiting for MTU exchange so release the task now.
9861022
}
9871023

@@ -1004,13 +1040,6 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
10041040

10051041
case BLE_GAP_EVENT_NOTIFY_RX: {
10061042
if (pClient->m_connHandle != event->notify_rx.conn_handle) return 0;
1007-
1008-
// If a notification comes before this flag is set we might
1009-
// access a vector while it is being cleared in connect()
1010-
if (!pClient->m_connEstablished) {
1011-
return 0;
1012-
}
1013-
10141043
NIMBLE_LOGD(LOG_TAG, "Notify Received for handle: %d", event->notify_rx.attr_handle);
10151044

10161045
for (const auto& svc : pClient->m_svcVec) {
@@ -1099,7 +1128,12 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
10991128
if (event->enc_change.status == (BLE_HS_ERR_HCI_BASE + BLE_ERR_PINKEY_MISSING)) {
11001129
// Key is missing, try deleting.
11011130
ble_store_util_delete_peer(&peerInfo.m_desc.peer_id_addr);
1131+
// Attempt a retry if async secure failed.
1132+
if (pClient->m_asyncSecureAttempt == 1) {
1133+
pClient->secureConnection(true);
1134+
}
11021135
} else {
1136+
pClient->m_asyncSecureAttempt = 0;
11031137
pClient->m_pClientCallbacks->onAuthenticationComplete(peerInfo);
11041138
}
11051139
}
@@ -1169,8 +1203,8 @@ int NimBLEClient::handleGapEvent(struct ble_gap_event* event, void* arg) {
11691203
}
11701204
} // Switch
11711205

1172-
if (pClient->m_pTaskData != nullptr) {
1173-
NimBLEUtils::taskRelease(*pClient->m_pTaskData, rc);
1206+
if (pTaskData != nullptr) {
1207+
NimBLEUtils::taskRelease(*pTaskData, rc);
11741208
}
11751209

11761210
return 0;
@@ -1191,11 +1225,11 @@ bool NimBLEClient::isConnected() const {
11911225
*/
11921226
void NimBLEClient::setClientCallbacks(NimBLEClientCallbacks* pClientCallbacks, bool deleteCallbacks) {
11931227
if (pClientCallbacks != nullptr) {
1194-
m_pClientCallbacks = pClientCallbacks;
1195-
m_deleteCallbacks = deleteCallbacks;
1228+
m_pClientCallbacks = pClientCallbacks;
1229+
m_config.deleteCallbacks = deleteCallbacks;
11961230
} else {
1197-
m_pClientCallbacks = &defaultCallbacks;
1198-
m_deleteCallbacks = false;
1231+
m_pClientCallbacks = &defaultCallbacks;
1232+
m_config.deleteCallbacks = false;
11991233
}
12001234
} // setClientCallbacks
12011235

0 commit comments

Comments
 (0)