diff --git a/components/bt/host/bluedroid/api/esp_gap_ble_api.c b/components/bt/host/bluedroid/api/esp_gap_ble_api.c index 762cf2cb02d..90e80b0db56 100644 --- a/components/bt/host/bluedroid/api/esp_gap_ble_api.c +++ b/components/bt/host/bluedroid/api/esp_gap_ble_api.c @@ -937,6 +937,39 @@ esp_err_t esp_ble_get_bond_device_list(int *dev_num, esp_ble_bond_dev_t *dev_lis return (ret == BT_STATUS_SUCCESS ? ESP_OK : ESP_FAIL); } +esp_err_t esp_ble_gap_set_bond_device_except(esp_bd_addr_t bd_addr, bool except) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (bd_addr == NULL) { + return ESP_ERR_INVALID_ARG; + } + +#if (SMP_INCLUDED == TRUE) + return (btc_storage_set_bond_except((bt_bdaddr_t *)bd_addr, except) == BT_STATUS_SUCCESS) + ? ESP_OK : ESP_FAIL; +#else + (void)except; + return ESP_FAIL; +#endif +} + +esp_err_t esp_ble_gap_is_bond_device_excepted(esp_bd_addr_t bd_addr, bool *excepted) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (bd_addr == NULL || excepted == NULL) { + return ESP_ERR_INVALID_ARG; + } + +#if (SMP_INCLUDED == TRUE) + return (btc_storage_get_bond_except((bt_bdaddr_t *)bd_addr, excepted) == BT_STATUS_SUCCESS) + ? ESP_OK : ESP_FAIL; +#else + return ESP_FAIL; +#endif +} + esp_err_t esp_ble_oob_req_reply(esp_bd_addr_t bd_addr, uint8_t *TK, uint8_t len) { if(len != ESP_BT_OCTET16_LEN) { diff --git a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h index 5035f5f441e..50e02385fc1 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h @@ -3936,6 +3936,52 @@ int esp_ble_get_bond_device_num(void); */ esp_err_t esp_ble_get_bond_device_list(int *dev_num, esp_ble_bond_dev_t *dev_list); +/** +* @brief Protect or unprotect a bonded device from automatic eviction +* when the bond list is full. +* +* `bd_addr` must already be in the BLE bond list (the same +* address reported by `ESP_GAP_BLE_AUTH_CMPL_EVT` or +* `esp_ble_get_bond_device_list()`). Do not pass an unbound +* address: this API will not create a new bond section and +* returns `ESP_FAIL`. +* +* Excepted devices are never selected by the overflow cleanup +* in NVS (they stay bonded even if they are idle/disconnected). +* `esp_ble_remove_bond_device()` still removes them explicitly. +* +* The flag is stored on that device's NVS bond record and +* survives reboot. Keep the number of excepted devices +* strictly less than the configured max bond count, otherwise +* a new pairing may have no evictable candidate. +* +* @param[in] bd_addr : BD address of an already bonded peer +* @param[in] except : true to protect, false to clear the protection +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_ARG : bd_addr is NULL +* - ESP_FAIL : device is not in the BLE bond list, the excepted bond +* count would exceed (max bond count - 1), or storage write failed +* +*/ +esp_err_t esp_ble_gap_set_bond_device_except(esp_bd_addr_t bd_addr, bool except); + +/** +* @brief Query whether a device's bond is protected from overflow eviction. +* +* This reads a per-device flag on that bond, not a separate except list. +* If the address is not bonded, `*excepted` is set to false. +* +* @param[in] bd_addr : BD address of the peer +* @param[out] excepted : set to true only if this device is bonded and excepted +* +* @return - ESP_OK : success +* - ESP_ERR_INVALID_ARG : bd_addr or excepted is NULL +* - ESP_FAIL : Bluedroid is not enabled +* +*/ +esp_err_t esp_ble_gap_is_bond_device_excepted(esp_bd_addr_t bd_addr, bool *excepted); + /** * @brief This function is called to provide the OOB data for * SMP in response to ESP_GAP_BLE_OOB_REQ_EVT diff --git a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c index 9b314c89478..32207c26463 100644 --- a/components/bt/host/bluedroid/btc/core/btc_ble_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_ble_storage.c @@ -12,6 +12,7 @@ #include "btc/btc_ble_storage.h" #include "bta/bta_gatts_co.h" #include "btc/btc_util.h" +#include "stack/btm_api.h" #if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE && BLE_PERIPH_PSEUDO_ADDR_BOND == TRUE) #include "stack/btm_ble_api.h" #endif @@ -21,12 +22,192 @@ //the maximum number of bonded devices #define BONED_DEVICES_MAX_COUNT (BTM_SEC_MAX_BONDS) +static bool btc_storage_bond_is_excepted(const char *section) +{ + int except = 0; + + return btc_config_get_int(section, BTC_BLE_STORAGE_EXCEPT_STR, &except) && (except != 0); +} + +#if (BLE_INCLUDED == TRUE) +/* Caller must hold btc_config_lock(). Count BLE bonds with ExceptBond set. */ +static uint16_t btc_storage_count_excepted_ble_bonds(void) +{ + uint16_t count = 0; + + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); + iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *section = btc_config_section_name(iter); + int device_type = 0; + + if (!string_is_bdaddr(section)) { + continue; + } + if (!btc_config_get_int(section, BTC_BLE_STORAGE_DEV_TYPE_STR, &device_type) || + !(device_type & BT_DEVICE_TYPE_BLE)) { + continue; + } + if (btc_storage_bond_is_excepted(section)) { + count++; + } + } + return count; +} + +/* Caller must hold btc_config_lock(). Warn if NVS has too many excepted bonds. */ +void btc_storage_check_excepted_bond_limit(void) +{ + if (BONED_DEVICES_MAX_COUNT == 0) { + return; + } + + uint16_t except_count = btc_storage_count_excepted_ble_bonds(); + uint16_t except_max = (uint16_t)(BONED_DEVICES_MAX_COUNT - 1); + + if (except_count > except_max) { + BTC_TRACE_WARNING("ExceptBond count %u exceeds limit %u (max bonds %u)", + except_count, except_max, BONED_DEVICES_MAX_COUNT); + } +} +#endif /* BLE_INCLUDED == TRUE */ + +static bool btc_storage_bdaddr_is_connected(BD_ADDR addr) +{ + /* Use BTM ACL only. BTA_DmGetConnectionState() pulls bta_dm_find_peer_device + * from bta_dm_pm.c, which is not linked on BLE-only / no-PM builds. */ + if (BTM_IsAclConnectionUp(addr, BT_TRANSPORT_LE)) { + return true; + } + if (BTM_IsAclConnectionUp(addr, BT_TRANSPORT_BR_EDR)) { + return true; + } + return false; +} + +static bool btc_storage_bond_is_connected(const char *section) +{ + bt_bdaddr_t bd_addr; + tBTM_LE_PID_KEYS pid; + size_t pid_len = sizeof(pid); + + if (!string_to_bdaddr(section, &bd_addr)) { + return false; + } + if (btc_storage_bdaddr_is_connected(bd_addr.address)) { + return true; + } + + /* Section may be keyed by RPA while the live ACL uses the identity. */ + memset(&pid, 0, sizeof(pid)); + if (btc_config_get_bin(section, BTC_BLE_STORAGE_LE_KEY_PID_STR, (uint8_t *)&pid, &pid_len) && + pid_len == sizeof(pid) && + memcmp(pid.static_addr, bd_addr.address, sizeof(bd_addr.address)) != 0) { + if (btc_storage_bdaddr_is_connected(pid.static_addr)) { + return true; + } + } + return false; +} + +static uint16_t btc_storage_count_bdaddr_sections(void) +{ + uint16_t count = 0; + + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); + iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + if (string_is_bdaddr(btc_config_section_name(iter))) { + count++; + } + } + return count; +} + +/* NVS sections are newest-first. Keep updating the match so the last one is oldest. + * The newest bdaddr section is never evicted (it is the bond just written / most recent). */ +static const char *btc_storage_find_evict_candidate(bool connected_ok) +{ + const char *candidate = NULL; + const char *newest = NULL; + + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); + iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *section = btc_config_section_name(iter); + + if (!string_is_bdaddr(section)) { + continue; + } + if (newest == NULL) { + newest = section; + continue; + } + if (btc_storage_bond_is_excepted(section)) { + continue; + } + if (!connected_ok && btc_storage_bond_is_connected(section)) { + continue; + } + candidate = section; + } + return candidate; +} + +static bool btc_storage_evict_bond_section(const char *section) +{ + bt_bdaddr_t bd_addr; + bdstr_t section_copy; + + if (!section || !string_to_bdaddr(section, &bd_addr)) { + return false; + } + + /* Section name is freed by remove_section; copy before mutating the list. */ + { + size_t name_len = strlen(section); + if (name_len >= sizeof(section_copy)) { + return false; + } + memcpy(section_copy, section, name_len + 1); + } + + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_LE); + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_BR_EDR); + if (btc_config_remove_section(section_copy)) { + BTIF_TRACE_WARNING("Exceeded the maximum number of bonded devices. Deleting device info: %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], + bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); + return true; + } + return false; +} + +void btc_storage_evict_overflow_bonded_devices(uint16_t max_keep) +{ + uint16_t count = btc_storage_count_bdaddr_sections(); + + while (count > max_keep) { + const char *victim = btc_storage_find_evict_candidate(false); + + if (!victim) { + /* No disconnected, non-excepted bond; fall back to the oldest connected one. */ + victim = btc_storage_find_evict_candidate(true); + } + if (!victim) { + BTIF_TRACE_WARNING("Cannot evict bonded devices: remaining bonds are excepted (count=%u max=%u)", + count, max_keep); + break; + } + if (!btc_storage_evict_bond_section(victim)) { + break; + } + count--; + } +} + static void _btc_storage_save(void) { - uint16_t addr_section_count = 0; - bt_bdaddr_t bd_addr; - - const btc_config_section_iter_t *need_remove_iter = NULL; const btc_config_section_iter_t *iter = btc_config_section_begin(); while (iter != btc_config_section_end()) { @@ -55,33 +236,12 @@ static void _btc_storage_save(void) continue; } - if(addr_section_count == BONED_DEVICES_MAX_COUNT) { - need_remove_iter = iter; - } - addr_section_count ++; iter = btc_config_section_next(iter); } - /*exceeded the maximum number of bonded devices, delete them */ - if (need_remove_iter) { - while(need_remove_iter != btc_config_section_end()) { - const char *need_remove_section = btc_config_section_name(need_remove_iter); - if (!string_is_bdaddr(need_remove_section)) { - need_remove_iter = btc_config_section_next(need_remove_iter); - continue; - } - need_remove_iter = btc_config_section_next(need_remove_iter); - //delete device info - string_to_bdaddr(need_remove_section, &bd_addr); - BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_LE); - BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_BR_EDR); - //delete config info - if (btc_config_remove_section(need_remove_section)) { - // The need_remove_section has been freed - BTIF_TRACE_WARNING("Exceeded the maximum number of bonded devices. Deleting the last device info: %02x:%02x:%02x:%02x:%02x:%02x", - bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); - } - } - } + + /* Bond list is newest-first in NVS. Evict oldest disconnected (then oldest + * connected) non-excepted devices until the count fits. */ + btc_storage_evict_overflow_bonded_devices(BONED_DEVICES_MAX_COUNT); btc_config_flush(); } @@ -282,6 +442,9 @@ static bt_status_t _btc_storage_remove_all_ble_keys(const char *name) ret |= btc_config_remove(name, BTC_BLE_STORAGE_PSEUDO_BOND_STR); } #endif + if (btc_config_exist(name, BTC_BLE_STORAGE_EXCEPT_STR)) { + ret |= btc_config_remove(name, BTC_BLE_STORAGE_EXCEPT_STR); + } return ret; } @@ -1172,6 +1335,87 @@ int btc_storage_get_num_ble_bond_devices(void) return num_dev; } +bt_status_t btc_storage_set_bond_except(bt_bdaddr_t *remote_bd_addr, bool except) +{ + bdstr_t bdstr; + int device_type = 0; + bool ret; + + if (remote_bd_addr == NULL) { + return BT_STATUS_FAIL; + } + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + btc_config_lock(); + /* Require an existing BLE bond section so we do not create an empty + * bdaddr section that would consume a bond slot. */ + if (!btc_config_has_section(bdstr)) { + btc_config_unlock(); + BTC_TRACE_WARNING("%s: %s is not bonded, cannot set except=%d", __func__, bdstr, except); + return BT_STATUS_FAIL; + } + if (!btc_config_get_int(bdstr, BTC_BLE_STORAGE_DEV_TYPE_STR, &device_type)) { + btc_config_unlock(); + BTC_TRACE_WARNING("%s: %s has no bond record, cannot set except=%d", __func__, bdstr, except); + return BT_STATUS_FAIL; + } + if (!(device_type & BT_DEVICE_TYPE_BLE)) { + btc_config_unlock(); + BTC_TRACE_WARNING("%s: %s is not a BLE bonded device (device_type=0x%x), cannot set except=%d", + __func__, bdstr, device_type, except); + return BT_STATUS_FAIL; + } + + if (except) { + if (!btc_storage_bond_is_excepted(bdstr)) { + uint16_t except_count = btc_storage_count_excepted_ble_bonds(); + uint16_t except_max = (BONED_DEVICES_MAX_COUNT > 0) + ? (uint16_t)(BONED_DEVICES_MAX_COUNT - 1) : 0; + + if (except_count >= except_max) { + btc_config_unlock(); + BTC_TRACE_WARNING("%s: cannot set except for %s: excepted bond count %u reached limit %u (max bonds %u)", + __func__, bdstr, except_count, except_max, BONED_DEVICES_MAX_COUNT); + return BT_STATUS_FAIL; + } + } + ret = btc_config_set_int(bdstr, BTC_BLE_STORAGE_EXCEPT_STR, 1); + } else if (btc_config_exist(bdstr, BTC_BLE_STORAGE_EXCEPT_STR)) { + ret = btc_config_remove(bdstr, BTC_BLE_STORAGE_EXCEPT_STR); + } else { + ret = true; + } + + if (ret) { + btc_config_flush(); + } else { + BTC_TRACE_ERROR("%s: failed to %s ExceptBond for %s", __func__, + except ? "set" : "clear", bdstr); + } + btc_config_unlock(); + + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +bt_status_t btc_storage_get_bond_except(bt_bdaddr_t *remote_bd_addr, bool *except) +{ + bdstr_t bdstr; + int except_val = 0; + + if (remote_bd_addr == NULL || except == NULL) { + return BT_STATUS_FAIL; + } + + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + + btc_config_lock(); + *except = btc_config_get_int(bdstr, BTC_BLE_STORAGE_EXCEPT_STR, &except_val) && (except_val != 0); + btc_config_unlock(); + + return BT_STATUS_SUCCESS; +} + bt_status_t btc_storage_get_gatt_cl_supp_feat(bt_bdaddr_t *remote_bd_addr, uint8_t *value, int len) { bdstr_t bdstr; diff --git a/components/bt/host/bluedroid/btc/core/btc_storage.c b/components/bt/host/bluedroid/btc/core/btc_storage.c index 9b197e3b060..91b53da7bb5 100644 --- a/components/bt/host/bluedroid/btc/core/btc_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_storage.c @@ -36,42 +36,8 @@ bt_status_t btc_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr, BOOLEAN sc_support) { bdstr_t bdstr; - bt_bdaddr_t bd_addr; bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); - /* device not in bond list and exceed the maximum number of bonded devices, delete the inactive bonded device */ - if (btc_storage_get_num_all_bond_devices() >= BTM_SEC_MAX_BONDS && !btc_config_has_section(bdstr)) { - const btc_config_section_iter_t *iter = btc_config_section_begin(); - const btc_config_section_iter_t *remove_iter = NULL; - /* find the last bdaddr-formatted device section */ - while (iter != btc_config_section_end()) { - const char *name = btc_config_section_name(iter); - if (name && string_is_bdaddr(name)) { - remove_iter = iter; - } - iter = btc_config_section_next(iter); - } - - if (remove_iter != NULL) { - const char *remove_section = btc_config_section_name(remove_iter); - if (string_to_bdaddr(remove_section, &bd_addr)) { - // delete device info - BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_BR_EDR); - BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_LE); - - // delete config info - if (btc_config_remove_section(remove_section)) { - BTC_TRACE_WARNING("exceeded the maximum number of bonded devices, delete the first device info : %02x:%02x:%02x:%02x:%02x:%02x", - bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); - } - } else { - BTC_TRACE_ERROR("Failed to convert section name to bdaddr: %s", remove_section); - } - } else { - BTC_TRACE_WARNING("No bdaddr-formatted section found to remove"); - } - } - BTC_TRACE_DEBUG("add to storage: Remote device:%s\n", bdstr); btc_config_lock(); @@ -79,6 +45,33 @@ bt_status_t btc_storage_add_bonded_device(bt_bdaddr_t *remote_bd_addr, ret &= btc_config_set_int(bdstr, BTC_STORAGE_PIN_LENGTH_STR, (int)pin_length); ret &= btc_config_set_bin(bdstr, BTC_STORAGE_LINK_KEY_STR, link_key, sizeof(LINK_KEY)); ret &= btc_config_set_bin(bdstr, BTC_STORAGE_SC_SUPPORT, (uint8_t *)&sc_support, sizeof(sc_support)); +#if (SMP_INCLUDED == TRUE) + /* Evict after the write so the new device is newest and is not selected. */ + btc_storage_evict_overflow_bonded_devices(BTM_SEC_MAX_BONDS); +#else + { + uint16_t count = 0; + const btc_config_section_iter_t *iter = btc_config_section_begin(); + const btc_config_section_iter_t *remove_iter = NULL; + while (iter != btc_config_section_end()) { + const char *name = btc_config_section_name(iter); + if (name && string_is_bdaddr(name)) { + count++; + remove_iter = iter; + } + iter = btc_config_section_next(iter); + } + if (count > BTM_SEC_MAX_BONDS && remove_iter != NULL) { + const char *remove_section = btc_config_section_name(remove_iter); + bt_bdaddr_t bd_addr; + if (string_to_bdaddr(remove_section, &bd_addr)) { + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_BR_EDR); + BTA_DmRemoveDevice(bd_addr.address, BT_TRANSPORT_LE); + btc_config_remove_section(remove_section); + } + } + } +#endif /* write bonded info immediately */ btc_config_flush(); btc_config_unlock(); @@ -141,10 +134,13 @@ static bt_status_t btc_in_fetch_bonded_devices(int add) { bt_status_t status = BT_STATUS_FAIL; uint16_t dev_cnt = 0; - const btc_config_section_iter_t *remove_iter = NULL; - bt_bdaddr_t bd_addr; btc_config_lock(); + /* Drop oldest non-excepted overflow first so excepted bonds are not trimmed. */ + btc_storage_evict_overflow_bonded_devices(BTM_SEC_MAX_BONDS); +#if (BLE_INCLUDED == TRUE) + btc_storage_check_excepted_bond_limit(); +#endif for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); iter = btc_config_section_next(iter)) { const char *name = btc_config_section_name(iter); if (!string_is_bdaddr(name)) { @@ -165,25 +161,10 @@ static bt_status_t btc_in_fetch_bonded_devices(int add) #endif ///BLE_INCLUDED == TRUE } } else { - /* delete the exceeded device info from nvs */ - remove_iter = iter; - while (remove_iter != btc_config_section_end()) { - const char *remove_section = btc_config_section_name(remove_iter); - string_to_bdaddr(remove_section, &bd_addr); - if (!string_is_bdaddr(remove_section)) { - remove_iter = btc_config_section_next(remove_iter); - continue; - } - remove_iter = btc_config_section_next(remove_iter); - /* delete config info */ - if (btc_config_remove_section(remove_section)) { - BTC_TRACE_WARNING("exceeded the maximum number of bonded devices, delete the exceed device info : %02x:%02x:%02x:%02x:%02x:%02x", - bd_addr.address[0], bd_addr.address[1], bd_addr.address[2], bd_addr.address[3], bd_addr.address[4], bd_addr.address[5]); - } - } - /* write into nvs */ - btc_config_flush(); - break; + /* Leftover overflow is excepted (eviction refuses to delete them). + * Keep the NVS entries but do not load extra BTM records. */ + BTC_TRACE_WARNING("skip loading overflow bonded device %s (excepted or no evictable candidate)", name); + continue; } } btc_config_unlock(); diff --git a/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h b/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h index 24649a5088f..a3c0976d72e 100644 --- a/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h +++ b/components/bt/host/bluedroid/btc/include/btc/btc_ble_storage.h @@ -38,6 +38,9 @@ * identity feature). Such sections legitimately share the peer Identity with * another (local,peer) bond and must be exempt from identity-based de-dup. */ #define BTC_BLE_STORAGE_PSEUDO_BOND_STR "PseudoBond" +/* Marks a bond that must not be auto-evicted when the bond list is full. + * Explicit removal via esp_ble_remove_bond_device() is still allowed. */ +#define BTC_BLE_STORAGE_EXCEPT_STR "ExceptBond" #define BTC_BLE_STORAGE_LOCAL_ADAPTER_STR "Adapter" #define BTC_BLE_STORAGE_LE_LOCAL_KEY_IR_STR "LE_LOCAL_KEY_IR" @@ -98,6 +101,20 @@ void btc_storage_delete_duplicate_ble_devices(void); void btc_storage_remove_unused_sections(uint8_t *cur_addr, tBTM_LE_PID_KEYS *del_pid_key); +/* Caller must hold btc_config_lock(). Evict bonds until bdaddr section count + * is <= max_keep. Excepted bonds are never evicted. Disconnected bonds are + * preferred over connected ones; NVS list order (oldest last) is the age key. */ +void btc_storage_evict_overflow_bonded_devices(uint16_t max_keep); + +#if (BLE_INCLUDED == TRUE) +/* Caller must hold btc_config_lock(). Check except count against bond limit. */ +void btc_storage_check_excepted_bond_limit(void); +#endif /* BLE_INCLUDED == TRUE */ + +bt_status_t btc_storage_set_bond_except(bt_bdaddr_t *remote_bd_addr, bool except); + +bt_status_t btc_storage_get_bond_except(bt_bdaddr_t *remote_bd_addr, bool *except); + #endif ///SMP_INCLUDED == TRUE #define BTC_BLE_STORAGE_GATT_CL_SUPP_FEAT_STR "GATT_CL_SUPP_FEAT"