Skip to content

Commit

Permalink
Fix 100 LE device connection limitation
Browse files Browse the repository at this point in the history
Currently after 100 devices are added to the device security database,
any subsequent LE connection to a newly discovered LE device address
will fail as entries in the security record database are not reused.

This patch removes a device record if the device itself is removed and
also ensures that the oldest device security record is deleted if the
limit is reached to ensure a new record can be allocated.

Bug: 31625900
Change-Id: I22f6c82c64a9a9bfb2a16d79182903e5aa011355
  • Loading branch information
Andre Eisenbach committed Sep 28, 2016
1 parent f2e3bea commit 013c32b
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 82 deletions.
14 changes: 5 additions & 9 deletions stack/btm/btm_ble.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ extern void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable);
extern BOOLEAN smp_proc_ltk_request(BD_ADDR bda);
#endif
extern void gatt_notify_enc_cmpl(BD_ADDR bd_addr);

/*******************************************************************************/
/* External Function to be called by other modules */
/*******************************************************************************/
Expand All @@ -73,16 +74,11 @@ BOOLEAN BTM_SecAddBleDevice (BD_ADDR bd_addr, BD_NAME bd_name, tBT_DEVICE_TYPE d
tBLE_ADDR_TYPE addr_type)
{
BTM_TRACE_DEBUG ("%s: dev_type=0x%x", __func__, dev_type);
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr);

if (!p_dev_rec) {
if (list_length(btm_cb.sec_dev_rec) > BTM_SEC_MAX_DEVICE_RECORDS) {
BTM_TRACE_ERROR("%s: %d max devices reached!", __func__, BTM_SEC_MAX_DEVICE_RECORDS);
return FALSE;
}

p_dev_rec = osi_calloc(sizeof(tBTM_SEC_DEV_REC));
list_append(btm_cb.sec_dev_rec, p_dev_rec);
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr);
if (!p_dev_rec)
{
p_dev_rec = btm_sec_allocate_dev_rec();

memcpy(p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN);
p_dev_rec->hci_handle = BTM_GetHCIConnHandle(bd_addr, BT_TRANSPORT_BR_EDR);
Expand Down
148 changes: 75 additions & 73 deletions stack/btm/btm_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
#include "hcidefs.h"
#include "l2c_api.h"

static tBTM_SEC_DEV_REC *btm_find_oldest_dev (void);

/*******************************************************************************
**
** Function BTM_SecAddDevice
Expand All @@ -63,22 +61,12 @@ BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name,
LINK_KEY link_key, UINT8 key_type, tBTM_IO_CAP io_cap,
UINT8 pin_length)
{
tBTM_SEC_DEV_REC *p_dev_rec;
int i, j;
BOOLEAN found = FALSE;

BTM_TRACE_API("%s: link key type:%x", __func__, key_type);
p_dev_rec = btm_find_dev (bd_addr);

tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr);
if (!p_dev_rec)
{
if (list_length(btm_cb.sec_dev_rec) > BTM_SEC_MAX_DEVICE_RECORDS) {
BTM_TRACE_DEBUG("%s: Max devices reached!", __func__);
return FALSE;
}

BTM_TRACE_DEBUG ("%s: allocate a new dev rec", __func__);
p_dev_rec = osi_calloc(sizeof(tBTM_SEC_DEV_REC));
list_append(btm_cb.sec_dev_rec, p_dev_rec);
p_dev_rec = btm_sec_allocate_dev_rec();

memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN);
p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_BR_EDR);
Expand All @@ -88,11 +76,19 @@ BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name,
/* update conn params, use default value for background connection params */
memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS));
#endif
} else {
/* "Bump" timestamp for existing record */
p_dev_rec->timestamp = btm_cb.dev_rec_count++;

/* TODO(eisenbach):
* Small refactor, but leaving original logic for now.
* On the surface, this does not make any sense at all. Why change the
* bond state for an existing device here? This logic should be verified
* as part of a larger refactor.
*/
p_dev_rec->bond_type = BOND_TYPE_UNKNOWN;
}

p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; /* Default value */
p_dev_rec->timestamp = btm_cb.dev_rec_count++;

if (dev_class)
memcpy (p_dev_rec->dev_class, dev_class, DEV_CLASS_LEN);

Expand All @@ -108,26 +104,23 @@ BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name,
p_dev_rec->num_read_pages = 0;
if (features)
{
BOOLEAN found = FALSE;
memcpy (p_dev_rec->features, features, sizeof (p_dev_rec->features));
for (i = HCI_EXT_FEATURES_PAGE_MAX; i >= 0; i--)
for (int i = HCI_EXT_FEATURES_PAGE_MAX; !found && i >= 0; i--)
{
for (j = 0; j < HCI_FEATURE_BYTES_PER_PAGE; j++)
for (int j = 0; j < HCI_FEATURE_BYTES_PER_PAGE; j++)
{
if (p_dev_rec->features[i][j] != 0)
{
found = TRUE;
p_dev_rec->num_read_pages = i + 1;
break;
}
}
if (found)
{
p_dev_rec->num_read_pages = i + 1;
break;
}
}
}
else
} else {
memset (p_dev_rec->features, 0, sizeof (p_dev_rec->features));
}

BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask);

Expand Down Expand Up @@ -177,16 +170,15 @@ BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name,
*******************************************************************************/
BOOLEAN BTM_SecDeleteDevice (BD_ADDR bd_addr)
{
tBTM_SEC_DEV_REC *p_dev_rec;

if (BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_LE) ||
BTM_IsAclConnectionUp(bd_addr, BT_TRANSPORT_BR_EDR))
{
BTM_TRACE_WARNING("%s FAILED: Cannot Delete when connection is active", __func__);
return FALSE;
}

if ((p_dev_rec = btm_find_dev(bd_addr)) != NULL)
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr);
if (p_dev_rec != NULL)
{
btm_sec_free_dev(p_dev_rec);
/* Tell controller to get rid of the link key, if it has one stored */
Expand Down Expand Up @@ -240,20 +232,10 @@ bool is_bd_addr_equal(void *data, void *context)
*******************************************************************************/
tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr)
{
tBTM_SEC_DEV_REC *p_dev_rec = NULL;
tBTM_INQ_INFO *p_inq_info;
BTM_TRACE_EVENT ("btm_sec_alloc_dev");

if (list_length(btm_cb.sec_dev_rec) > BTM_SEC_MAX_DEVICE_RECORDS) {
p_dev_rec = btm_find_oldest_dev();
} else {
BTM_TRACE_DEBUG ("allocate a new dev rec");
p_dev_rec = osi_calloc(sizeof(tBTM_SEC_DEV_REC));
list_append(btm_cb.sec_dev_rec, p_dev_rec);
}

p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; /* Default value */
p_dev_rec->sec_flags = BTM_SEC_IN_USE;
tBTM_SEC_DEV_REC *p_dev_rec = btm_sec_allocate_dev_rec();

/* Check with the BT manager if details about remote device are known */
/* outgoing connection */
Expand All @@ -280,7 +262,6 @@ tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr)
p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_LE);
#endif
p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_BR_EDR);
p_dev_rec->timestamp = btm_cb.dev_rec_count++;

return(p_dev_rec);
}
Expand All @@ -295,17 +276,11 @@ tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr)
*******************************************************************************/
void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec)
{
p_dev_rec->bond_type = BOND_TYPE_UNKNOWN;
p_dev_rec->sec_flags = 0;

#if BLE_INCLUDED == TRUE
/* Clear out any saved BLE keys */
btm_sec_clear_ble_keys (p_dev_rec);
/* clear the ble block */
memset(&p_dev_rec->ble, 0, sizeof(tBTM_SEC_BLE));
#endif


list_remove(btm_cb.sec_dev_rec, p_dev_rec);
}

/*******************************************************************************
Expand Down Expand Up @@ -475,8 +450,6 @@ void btm_consolidate_dev(tBTM_SEC_DEV_REC *p_target_rec)

/* remove the combined record */
list_remove(btm_cb.sec_dev_rec, p_dev_rec);

p_dev_rec->bond_type = BOND_TYPE_UNKNOWN;
break;
}

Expand All @@ -490,8 +463,6 @@ void btm_consolidate_dev(tBTM_SEC_DEV_REC *p_target_rec)

/* remove the combined record */
list_remove(btm_cb.sec_dev_rec, p_dev_rec);

p_dev_rec->bond_type = BOND_TYPE_UNKNOWN;
}
break;
}
Expand Down Expand Up @@ -524,48 +495,79 @@ tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr)

/*******************************************************************************
**
** Function btm_find_oldest_dev
** Function btm_find_oldest_dev_rec
**
** Description Locates the oldest device in use. It first looks for
** the oldest non-paired device. If all devices are paired it
** deletes the oldest paired device.
** returns the oldest paired device.
**
** Returns Pointer to the record or NULL
**
*******************************************************************************/
tBTM_SEC_DEV_REC *btm_find_oldest_dev (void)
static tBTM_SEC_DEV_REC* btm_find_oldest_dev_rec (void)
{
tBTM_SEC_DEV_REC *p_oldest = NULL;
UINT32 ot = 0xFFFFFFFF;
UINT32 ts_oldest = 0xFFFFFFFF;
tBTM_SEC_DEV_REC *p_oldest_paired = NULL;
UINT32 ot_paired = 0xFFFFFFFF;
UINT32 ts_oldest_paired = 0xFFFFFFFF;

/* First look for the non-paired devices for the oldest entry */
list_node_t *end = list_end(btm_cb.sec_dev_rec);
for (list_node_t *node = list_begin(btm_cb.sec_dev_rec); node != end; node = list_next(node)) {
tBTM_SEC_DEV_REC *p_dev_rec = list_node(node);
/* Device is not paired */
if ((p_dev_rec->sec_flags & (BTM_SEC_LINK_KEY_KNOWN |BTM_SEC_LE_LINK_KEY_KNOWN)) == 0) {
if (p_dev_rec->timestamp < ot) {

if ((p_dev_rec->sec_flags & (BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LE_LINK_KEY_KNOWN)) == 0) {
// Device is not paired
if (p_dev_rec->timestamp < ts_oldest) {
p_oldest = p_dev_rec;
ot = p_dev_rec->timestamp;
ts_oldest = p_dev_rec->timestamp;
}
} else {
// Paired device
if (p_dev_rec->timestamp < ts_oldest_paired) {
p_oldest_paired = p_dev_rec;
ts_oldest_paired = p_dev_rec->timestamp;
}
continue;
}
}

if (p_dev_rec->timestamp < ot_paired) {
p_oldest_paired = p_dev_rec;
ot_paired = p_dev_rec->timestamp;
}
// If we did not find any non-paired devices, use the oldest paired one...
if (ts_oldest == 0xFFFFFFFF)
p_oldest = p_oldest_paired;

return p_oldest;
}

/*******************************************************************************
**
** Function btm_sec_allocate_dev_rec
**
** Description Attempts to allocate a new device record. If we have
** exceeded the maximum number of allowable records to
** allocate, the oldest record will be deleted to make room
** for the new record.
**
** Returns Pointer to the newly allocated record
**
*******************************************************************************/
tBTM_SEC_DEV_REC* btm_sec_allocate_dev_rec(void)
{
tBTM_SEC_DEV_REC *p_dev_rec = NULL;

if (list_length(btm_cb.sec_dev_rec) > BTM_SEC_MAX_DEVICE_RECORDS)
{
p_dev_rec = btm_find_oldest_dev_rec();
list_remove(btm_cb.sec_dev_rec, p_dev_rec);
}

/* if non-paired device return oldest */
if (ot != 0xFFFFFFFF)
return(p_oldest);
p_dev_rec = osi_calloc(sizeof(tBTM_SEC_DEV_REC));
list_append(btm_cb.sec_dev_rec, p_dev_rec);

// Initialize defaults
p_dev_rec->sec_flags = BTM_SEC_IN_USE;
p_dev_rec->bond_type = BOND_TYPE_UNKNOWN;
p_dev_rec->timestamp = btm_cb.dev_rec_count++;

/* only paired devices present, return oldest */
return p_oldest_paired;
return p_dev_rec;
}

/*******************************************************************************
Expand Down
1 change: 1 addition & 0 deletions stack/btm/btm_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,7 @@ extern void btm_report_device_status (tBTM_DEV_STATUS status);
*/
extern BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr);

extern tBTM_SEC_DEV_REC *btm_sec_allocate_dev_rec(void);
extern tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr);
extern void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec);
extern tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr);
Expand Down

0 comments on commit 013c32b

Please sign in to comment.