这篇文章简单分析一下 蓝牙音箱配对流程.现在的音箱基本都支持security simple pairing.所以这里的流程基本上就是ssp的代码流程.

源码参考的是 Android 6.0 上面的bluedroid.这里先介绍一些bluedroid定义的概率.

首先介绍一下 配对的几个状态:pairing_cb.state  ,这个定义在bluetooth.h里面.

 /** Bluetooth Bond state */
typedef enum {
BT_BOND_STATE_NONE,
BT_BOND_STATE_BONDING,
BT_BOND_STATE_BONDED
} bt_bond_state_t;

每次有配对状态发生改变的时候,通过bond_state_changed 来向上层汇报状态.

bluetooth.c

static int create_bond(const bt_bdaddr_t *bd_addr, int transport)
{
/* sanity check */
if (interface_ready() == FALSE)
return BT_STATUS_NOT_READY; return btif_dm_create_bond(bd_addr, transport);
}

btif_dm.c

/*******************************************************************************
**
** Function btif_dm_create_bond
**
** Description Initiate bonding with the specified device
**
** Returns bt_status_t
**
*******************************************************************************/
bt_status_t btif_dm_create_bond(const bt_bdaddr_t *bd_addr, int transport)
{
btif_dm_create_bond_cb_t create_bond_cb;
create_bond_cb.transport = transport;
bdcpy(create_bond_cb.bdaddr.address, bd_addr->address); bdstr_t bdstr;
BTIF_TRACE_EVENT("%s: bd_addr=%s, transport=%d", __FUNCTION__, bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr)), transport);
if (pairing_cb.state != BT_BOND_STATE_NONE)
return BT_STATUS_BUSY; btif_transfer_context(btif_dm_generic_evt, BTIF_DM_CB_CREATE_BOND,
(char *)&create_bond_cb, sizeof(btif_dm_create_bond_cb_t), NULL); return BT_STATUS_SUCCESS;
}

当前的配对状态是  BT_BOND_STATE_NONE,

btif_dm.c:

/*******************************************************************************
**
** Function btif_dm_generic_evt
**
** Description Executes non-BTA upstream events in BTIF context
**
** Returns void
**
*******************************************************************************/
static void btif_dm_generic_evt(UINT16 event, char* p_param)
{
BTIF_TRACE_EVENT("%s: event=%d", __FUNCTION__, event);
switch(event)
{
case BTIF_DM_CB_DISCOVERY_STARTED:
{
HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb, BT_DISCOVERY_STARTED);
}
break; case BTIF_DM_CB_CREATE_BOND:
{
pairing_cb.timeout_retries = NUM_TIMEOUT_RETRIES;
btif_dm_create_bond_cb_t *create_bond_cb = (btif_dm_create_bond_cb_t*)p_param;
btif_dm_cb_create_bond(&create_bond_cb->bdaddr, create_bond_cb->transport);
}
break;

继续看:

/*******************************************************************************
**
** Function btif_dm_cb_create_bond
**
** Description Create bond initiated from the BTIF thread context
** Special handling for HID devices
**
** Returns void
**
*******************************************************************************/
static void btif_dm_cb_create_bond(bt_bdaddr_t *bd_addr, tBTA_TRANSPORT transport)
{
BOOLEAN is_hid = check_cod(bd_addr, COD_HID_POINTING);
bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING); #if BLE_INCLUDED == TRUE
int device_type;
int addr_type;
bdstr_t bdstr;
bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr));
if (transport == BT_TRANSPORT_LE)
{
...
}
if((btif_config_get_int((char const *)&bdstr,"DevType", &device_type) &&
(btif_storage_get_remote_addr_type(bd_addr, &addr_type) == BT_STATUS_SUCCESS) &&
(device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) || (transport == BT_TRANSPORT_LE))
{
BTA_DmAddBleDevice(bd_addr->address, addr_type, device_type);
}
#endif
#if BLE_INCLUDED == TRUE
if(is_hid && (device_type & BT_DEVICE_TYPE_BLE) == )
#else
if(is_hid)
#endif
{
int status;
status = btif_hh_connect(bd_addr);
if(status != BT_STATUS_SUCCESS)
bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE);
}
else
{
BTA_DmBondByTransport((UINT8 *)bd_addr->address, transport); //非HID,继续进入到BTA
}
/* Track originator of bond creation */
pairing_cb.is_local_initiated = TRUE; }

bta_dm_api.c

/*******************************************************************************
**
** Function BTA_DmBondByTransports
**
** Description This function initiates a bonding procedure with a peer
** device
**
**
** Returns void
**
*******************************************************************************/
void BTA_DmBondByTransport(BD_ADDR bd_addr, tBTA_TRANSPORT transport)
{
tBTA_DM_API_BOND *p_msg; if ((p_msg = (tBTA_DM_API_BOND *) GKI_getbuf(sizeof(tBTA_DM_API_BOND))) != NULL)
{
p_msg->hdr.event = BTA_DM_API_BOND_EVT;
bdcpy(p_msg->bd_addr, bd_addr);
p_msg->transport = transport;
bta_sys_sendmsg(p_msg);
} }

关于消息的发送流程,这里就不讲了,直接分析所执行的函数:

BTA_DM_API_BOND_EVT    // BTA got event 0x107

bta_dm_main.c

BOOLEAN bta_dm_sm_execute(BT_HDR *p_msg)
{
UINT16 event = p_msg->event & 0x00ff; APPL_TRACE_EVENT("bta_dm_sm_execute event:0x%x", event); /* execute action functions */
if(event < BTA_DM_NUM_ACTIONS)
{
(*bta_dm_action[event])( (tBTA_DM_MSG*) p_msg);
} return TRUE;
}
    bta_dm_bond,              /* 11  BTA_DM_API_BOND_EVT */

到目前为止,transport = BTA_TRANSPORT_UNKNOWN  = 0

bta_dm_act.c

/*******************************************************************************
**
** Function bta_dm_bond
**
** Description Bonds with peer device
**
**
** Returns void
**
*******************************************************************************/
void bta_dm_bond (tBTA_DM_MSG *p_data)
{
tBTM_STATUS status;
tBTA_DM_SEC sec_event;
char *p_name; if (p_data->bond.transport == BTA_TRANSPORT_UNKNOWN)
status = BTM_SecBond ( p_data->bond.bd_addr, , NULL, );//0
else
status = BTM_SecBondByTransport ( p_data->bond.bd_addr, p_data->bond.transport, , NULL, ); if (bta_dm_cb.p_sec_cback && (status != BTM_CMD_STARTED))
{
...
} }

btm_sec.c  进入到stack/btm 了.

/*******************************************************************************
**
** Function BTM_SecBond
**
** Description This function is called to perform bonding with peer device.
** If the connection is already up, but not secure, pairing
** is attempted. If already paired BTM_SUCCESS is returned.
**
** Parameters: bd_addr - Address of the device to bond
** pin_len - length in bytes of the PIN Code
** p_pin - pointer to array with the PIN Code
** trusted_mask - bitwise OR of trusted services (array of UINT32)
**
** Note: After 2.1 parameters are not used and preserved here not to change API
*******************************************************************************/
tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
{
tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
#if BLE_INCLUDED == TRUE
if (BTM_UseLeLink(bd_addr))
transport = BT_TRANSPORT_LE;
#endif
return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask);
}

在btm 里面同样对于配对有相应的状态转换 btm_cb.pairing_state:

定义在btm_int.h里面:

/* Pairing State */
enum
{
BTM_PAIR_STATE_IDLE, /* Idle */
BTM_PAIR_STATE_GET_REM_NAME, /* Getting the remote name (to check for SM4) */
BTM_PAIR_STATE_WAIT_PIN_REQ, /* Started authentication, waiting for PIN req (PIN is pre-fetched) */
BTM_PAIR_STATE_WAIT_LOCAL_PIN, /* Waiting for local PIN code */
BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM, /* Waiting user 'yes' to numeric confirmation */
BTM_PAIR_STATE_KEY_ENTRY, /* Key entry state (we are a keyboard) */
BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP, /* Waiting for local response to peer OOB data */
BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS, /* Waiting for local IO capabilities and OOB data */
BTM_PAIR_STATE_INCOMING_SSP, /* Incoming SSP (got peer IO caps when idle) */
BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, /* All done, waiting authentication cpmplete */
BTM_PAIR_STATE_WAIT_DISCONNECT /* Waiting to disconnect the ACL */
};

这里先 说一下一般的 btm_cb.pairing_state 里面的配对状态转换的流程:

BTM_PAIR_STATE_IDLE -->BTM_PAIR_STATE_GET_REM_NAME -->BTM_PAIR_STATE_WAIT_PIN_REQ-->BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS(SSP)--->BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM(depends on IO)-->BTM_PAIR_STATE_WAIT_AUTH_COMPLETE-->BTM_PAIR_STATE_IDLE

关于security 的flag 定义在btm_int.h:

#define BTM_SEC_AUTHORIZED      BTM_SEC_FLAG_AUTHORIZED     /* 0x01 */
#define BTM_SEC_AUTHENTICATED BTM_SEC_FLAG_AUTHENTICATED /* 0x02 */
#define BTM_SEC_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED /* 0x04 */
#define BTM_SEC_NAME_KNOWN 0x08
#define BTM_SEC_LINK_KEY_KNOWN BTM_SEC_FLAG_LKEY_KNOWN /* 0x10 */
#define BTM_SEC_LINK_KEY_AUTHED BTM_SEC_FLAG_LKEY_AUTHED /* 0x20 */
#define BTM_SEC_ROLE_SWITCHED 0x40
#define BTM_SEC_IN_USE 0x80

那么最终启动security的时候,就初始化为BTM_SEC_IN_USE    = 0x80

/*******************************************************************************
**
** Function btm_sec_bond_by_transport
**
** Description this is the bond function that will start either SSP or SMP.
**
** Parameters: bd_addr - Address of the device to bond
** pin_len - length in bytes of the PIN Code
** p_pin - pointer to array with the PIN Code
** trusted_mask - bitwise OR of trusted services (array of UINT32)
**
** Note: After 2.1 parameters are not used and preserved here not to change API
*******************************************************************************/
tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport,
UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
{
tBTM_SEC_DEV_REC *p_dev_rec;
tBTM_STATUS status;
UINT8 *p_features;
UINT8 ii;
tACL_CONN *p= btm_bda_to_acl(bd_addr, transport);
BTM_TRACE_API ("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x",
bd_addr[], bd_addr[], bd_addr[], bd_addr[], bd_addr[], bd_addr[]); /* Other security process is in progress */
if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
{
BTM_TRACE_ERROR ("BTM_SecBond: already busy in state: %s", btm_pair_state_descr(btm_cb.pairing_state));
return(BTM_WRONG_MODE);
} if ((p_dev_rec = btm_find_or_alloc_dev (bd_addr)) == NULL)//find from btm_cb.sec_dev_rec
{
return(BTM_NO_RESOURCES);
} BTM_TRACE_DEBUG ("before update sec_flags=0x%x", p_dev_rec->sec_flags);//first time 0x80 /* Finished if connection is active and already paired */
if ( ((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_BR_EDR
&& (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))
#if (BLE_INCLUDED == TRUE)
||((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_LE
&& (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))
#endif )
{
BTM_TRACE_WARNING("BTM_SecBond -> Already Paired");
return(BTM_SUCCESS);
} /* Tell controller to get rid of the link key if it has one stored */
if ((BTM_DeleteStoredLinkKey (bd_addr, NULL)) != BTM_SUCCESS)//delete link key
return(BTM_NO_RESOURCES); /* Save the PIN code if we got a valid one */
if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != )) //we have not got one valid
{
...
} memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD; p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE;///* Outbound call requires authentication */ 0x10
p_dev_rec->is_originator = TRUE;
if (trusted_mask)
BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask);
... p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED
| BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED);/*clear the all the flags*/ BTM_TRACE_DEBUG ("after update sec_flags=0x%x", p_dev_rec->sec_flags); //still in use 0x80
if (!controller_get_interface()->supports_simple_pairing()) //local HCI_Read_Local_Extended_Features
    {
/* The special case when we authenticate keyboard. Set pin type to fixed */
/* It would be probably better to do it from the application, but it is */
/* complicated */
if (((p_dev_rec->dev_class[] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL)
&& (p_dev_rec->dev_class[] & BTM_COD_MINOR_KEYBOARD)
&& (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED))
{
btm_cb.pin_type_changed = TRUE;
btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED);
}
} for (ii = ; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++)
{
p_features = p_dev_rec->features[ii];
BTM_TRACE_EVENT(" remote_features page[%1d] = %02x-%02x-%02x-%02x",
ii, p_features[], p_features[], p_features[], p_features[]);
BTM_TRACE_EVENT(" %02x-%02x-%02x-%02x",
p_features[], p_features[], p_features[], p_features[]);
} BTM_TRACE_EVENT ("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x", p_dev_rec->sm4, p_dev_rec->hci_handle);//have not got remote feature /* If connection already exists... */
if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE)/* then start authentication*/
{
if (!btm_sec_start_authentication (p_dev_rec))
return(BTM_NO_RESOURCES); btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); /* Mark lcb as bonding */
l2cu_update_lcb_4_bonding (bd_addr, TRUE);
return(BTM_CMD_STARTED);
} BTM_TRACE_DEBUG ("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4);//local mode = 4.
if (!controller_get_interface()->supports_simple_pairing()
|| (p_dev_rec->sm4 == BTM_SM4_KNOWN)) // sm4 of remote is not known now
{
if ( btm_sec_check_prefetch_pin (p_dev_rec) )
return (BTM_CMD_STARTED);
}
if ((btm_cb.security_mode == BTM_SEC_MODE_SP ||
btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
btm_cb.security_mode == BTM_SEC_MODE_SC) &&
BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4))
{
/* local is 2.1 and peer is unknown */
if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == )
{
/* we are not accepting connection request from peer
* -> RNR (to learn if peer is 2.1)
* RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */
btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME);
BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);//begin to get rmt name
}
else
{
/* We are accepting connection request from peer */
btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);
}
BTM_TRACE_DEBUG ("State:%s sm4: 0x%x sec_state:%d",
btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state);
return BTM_CMD_STARTED;
} /* both local and peer are 2.1 */
status = btm_sec_dd_create_conn(p_dev_rec); if (status != BTM_CMD_STARTED)
{
btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE);
} return status;
}

从这里的逻辑看出,如果我们的2.1 并且对方的feature还有获取到,那么我们就先进行remote name的获取.并且此刻btm_cb.pairing_state状态变为:BTM_PAIR_STATE_GET_REM_NAME

下面我们看看RNR的流程:

/*******************************************************************************
**
** Function BTM_ReadRemoteDeviceName
**
** Description This function initiates a remote device HCI command to the
** controller and calls the callback when the process has completed.
**
** Input Params: remote_bda - device address of name to retrieve
** p_cb - callback function called when BTM_CMD_STARTED
** is returned.
** A pointer to tBTM_REMOTE_DEV_NAME is passed to the
** callback.
**
** Returns
** BTM_CMD_STARTED is returned if the request was successfully sent
** to HCI.
** BTM_BUSY if already in progress
** BTM_UNKNOWN_ADDR if device address is bad
** BTM_NO_RESOURCES if could not allocate resources to start the command
** BTM_WRONG_MODE if the device is not up.
**
*******************************************************************************/
tBTM_STATUS BTM_ReadRemoteDeviceName (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb
,tBT_TRANSPORT transport)
{
tBTM_INQ_INFO *p_cur = NULL;
tINQ_DB_ENT *p_i; BTM_TRACE_API ("BTM_ReadRemoteDeviceName: bd addr [%02x%02x%02x%02x%02x%02x]",
remote_bda[], remote_bda[], remote_bda[],
remote_bda[], remote_bda[], remote_bda[]); /* Use the remote device's clock offset if it is in the local inquiry database */
if ((p_i = btm_inq_db_find (remote_bda)) != NULL)
{
p_cur = &p_i->inq_info;
}
BTM_TRACE_API ("no device found in inquiry db");
if (transport == BT_TRANSPORT_LE)
{
return btm_ble_read_remote_name(remote_bda, p_cur, p_cb);
}
else
return (btm_initiate_rem_name (remote_bda, p_cur, BTM_RMT_NAME_EXT,
BTM_EXT_RMT_NAME_TIMEOUT, p_cb));
}

从上面看 这个p_cb的回调是NULL.

btm_inq.c

/*******************************************************************************
**
** Function btm_initiate_rem_name
**
** Description This function looks initiates a remote name request. It is called
** either by GAP or by the API call BTM_ReadRemoteDeviceName.
**
** Input Params: p_cur - pointer to an inquiry result structure (NULL if nonexistent)
** p_cb - callback function called when BTM_CMD_STARTED
** is returned.
** A pointer to tBTM_REMOTE_DEV_NAME is passed to the
** callback.
**
** Returns
** BTM_CMD_STARTED is returned if the request was sent to HCI.
** BTM_BUSY if already in progress
** BTM_NO_RESOURCES if could not allocate resources to start the command
** BTM_WRONG_MODE if the device is not up.
**
*******************************************************************************/
tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur,
UINT8 origin, UINT32 timeout, tBTM_CMPL_CB *p_cb)
{
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
BOOLEAN cmd_ok;
... if (origin == BTM_RMT_NAME_SEC)
{
...
}
/* Make sure there are no two remote name requests from external API in progress */
else if (origin == BTM_RMT_NAME_EXT)
{
if (p_inq->remname_active)
{
return (BTM_BUSY);
}
else
{
/* If there is no remote name request running,call the callback function and start timer */
p_inq->p_remname_cmpl_cb = p_cb;//NULL
memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN);
btu_start_timer (&p_inq->rmt_name_timer_ent,
BTU_TTYPE_BTM_RMT_NAME,
timeout); /* If the database entry exists for the device, use its clock offset */
if (p_cur)
{
cmd_ok = btsnd_hcic_rmt_name_req (remote_bda,
p_cur->results.page_scan_rep_mode,
p_cur->results.page_scan_mode,
(UINT16)(p_cur->results.clock_offset |
BTM_CLOCK_OFFSET_VALID));//start hci command
}
else /* Otherwise use defaults and mark the clock offset as invalid */
{
cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1,
HCI_MANDATARY_PAGE_SCAN_MODE, );
}
if (cmd_ok)
{
p_inq->remname_active = TRUE;
return BTM_CMD_STARTED;
}
else
return BTM_NO_RESOURCES;
}
}
else
{
return BTM_ILLEGAL_VALUE;
}
}

 从这里我们发现,其就已经开始了RNR流程了.

这个cmd 其实不仅仅有获取名字的功能,当底层获取了remote 的feature,那么这个时候也会通过

Event: HCI Remote Host Supported Features Notification

上报.

我们接下来看看这个事件的处理过程:

btu_hcif.c

        case HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT:
btu_hcif_host_support_evt (p);
break;

btm_Sec.c

/*******************************************************************************
**
** Function btm_sec_rmt_host_support_feat_evt
**
** Description This function is called when the
** HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is received
**
** Returns void
**
*******************************************************************************/
void btm_sec_rmt_host_support_feat_evt (UINT8 *p)
{
tBTM_SEC_DEV_REC *p_dev_rec;
BD_ADDR bd_addr; /* peer address */
BD_FEATURES features; STREAM_TO_BDADDR (bd_addr, p);
p_dev_rec = btm_find_or_alloc_dev (bd_addr); if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4))
{
p_dev_rec->sm4 = BTM_SM4_KNOWN;//now sm4 is known
STREAM_TO_ARRAY(features, p, HCI_FEATURE_BYTES_PER_PAGE);
int xx = ;
for(xx = ;xx<HCI_FEATURE_BYTES_PER_PAGE;xx++)
BTM_TRACE_EVENT("features[%d] = %d libs_liu",xx,features[xx]);
if (HCI_SSP_HOST_SUPPORTED(features))
{
p_dev_rec->sm4 = BTM_SM4_TRUE;//0x11 }
}
}

现在就已经知道remote devices 的sm了.支持ssp的话,sm4 = 4

下面我们看看controller 获取到名字之后host端的处理:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_rmt_name_request_comp_evt
**
** Description Process event HCI_RMT_NAME_REQUEST_COMP_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_rmt_name_request_comp_evt (UINT8 *p, UINT16 evt_len)
{
UINT8 status;
BD_ADDR bd_addr; STREAM_TO_UINT8 (status, p);
STREAM_TO_BDADDR (bd_addr, p); evt_len -= ( + BD_ADDR_LEN); btm_process_remote_name (bd_addr, p, evt_len, status);//获取名字,但是发现并没有去保存 btm_sec_rmt_name_request_complete (bd_addr, p, status);//这里保存名字
}

我们先看看 上面那个处理名字的函数:

btm_inq.c

/*******************************************************************************
**
** Function btm_process_remote_name
**
** Description This function is called when a remote name is received from
** the device. If remote names are cached, it updates the inquiry
** database.
**
** Returns void
**
*******************************************************************************/
void btm_process_remote_name (BD_ADDR bda, BD_NAME bdn, UINT16 evt_len, UINT8 hci_status)
{
tBTM_REMOTE_DEV_NAME rem_name;
tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars;
tBTM_CMPL_CB *p_cb = p_inq->p_remname_cmpl_cb;//NULL
UINT8 *p_n1; UINT16 temp_evt_len; if (bda != NULL)
{
BTM_TRACE_EVENT("BDA %02x:%02x:%02x:%02x:%02x:%02x",bda[], bda[],
bda[], bda[],
bda[], bda[]);
} ... /* If the inquire BDA and remote DBA are the same, then stop the timer and set the active to false */
if ((p_inq->remname_active ==TRUE)&&
(((bda != NULL) &&
(memcmp(bda, p_inq->remname_bda,BD_ADDR_LEN)==)) || bda == NULL)) {
#if BLE_INCLUDED == TRUE
if (BTM_UseLeLink(p_inq->remname_bda))
{
if (hci_status == HCI_ERR_UNSPECIFIED)
btm_ble_cancel_remote_name(p_inq->remname_bda);
}
#endif
btu_stop_timer (&p_inq->rmt_name_timer_ent);//停 timer
p_inq->remname_active = FALSE;
/* Clean up and return the status if the command was not successful */
/* Note: If part of the inquiry, the name is not stored, and the */
/* inquiry complete callback is called. */ if (hci_status == HCI_SUCCESS)
{
/* Copy the name from the data stream into the return structure */
/* Note that even if it is not being returned, it is used as a */
/* temporary buffer. */
p_n1 = (UINT8 *)rem_name.remote_bd_name;
rem_name.length = (evt_len < BD_NAME_LEN) ? evt_len : BD_NAME_LEN;
rem_name.remote_bd_name[rem_name.length] = ;
rem_name.status = BTM_SUCCESS;
temp_evt_len = rem_name.length; while (temp_evt_len > )
{
*p_n1++ = *bdn++;
temp_evt_len--;
}
rem_name.remote_bd_name[rem_name.length] = ;//temp struction 未返回
} /* If processing a stand alone remote name then report the error in the callback */
else
{
rem_name.status = BTM_BAD_VALUE_RET;
rem_name.length = ;
rem_name.remote_bd_name[] = ;
}
/* Reset the remote BAD to zero and call callback if possible */
memset(p_inq->remname_bda, , BD_ADDR_LEN); p_inq->p_remname_cmpl_cb = NULL;
if (p_cb)//null
(p_cb)((tBTM_REMOTE_DEV_NAME *)&rem_name);
}
}

我们再看看btm_sec_rmt_name_request_complete的实现:

btm_Sec.c

看名字,应该还是和配对流程相关.

这里发现remote device的安全转换状态也是有一个状态转换的.其变量名是p_dev_rec->sec_state

其可以取的值定义在btm_int.h里面:

#define BTM_SEC_STATE_IDLE               0
#define BTM_SEC_STATE_AUTHENTICATING 1
#define BTM_SEC_STATE_ENCRYPTING 2
#define BTM_SEC_STATE_GETTING_NAME 3
#define BTM_SEC_STATE_AUTHORIZING 4
#define BTM_SEC_STATE_SWITCHING_ROLE 5
#define BTM_SEC_STATE_DISCONNECTING 6 /* disconnecting BR/EDR */
#define BTM_SEC_STATE_DELAY_FOR_ENC 7 /* delay to check for encryption to work around */
/* controller problems */
#define BTM_SEC_STATE_DISCONNECTING_BLE 8 /* disconnecting BLE */
#define BTM_SEC_STATE_DISCONNECTING_BOTH 9 /* disconnecting BR/EDR and BLE */

btm_Sec.c

/*******************************************************************************
**
** Function btm_sec_rmt_name_request_complete
**
** Description This function is called when remote name was obtained from
** the peer device
**
** Returns void
**
*******************************************************************************/
void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT8 status)
{
tBTM_SEC_DEV_REC *p_dev_rec;
int i;
DEV_CLASS dev_class;
UINT8 old_sec_state; BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete");
if (((p_bd_addr == NULL) && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda))
|| ((p_bd_addr != NULL) && !BTM_ACL_IS_CONNECTED(p_bd_addr)))
{
btm_acl_resubmit_page();
} /* If remote name request failed, p_bd_addr is null and we need to search */
/* based on state assuming that we are doing 1 at a time */
if (p_bd_addr)
p_dev_rec = btm_find_dev (p_bd_addr);
else
{
...
}
... if (p_dev_rec)
{
old_sec_state = p_dev_rec->sec_state;
if (status == HCI_SUCCESS)
{
BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), (char *)p_bd_name, BTM_MAX_REM_BD_NAME_LEN);//这里是保存名字的地方,名字从函数的第二个参数中来
p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN;// ++0x8 = 0x88
BTM_TRACE_EVENT ("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x", p_dev_rec->sec_flags);
}
else
{
/* Notify all clients waiting for name to be resolved even if it failed so clients can continue */
p_dev_rec->sec_bd_name[] = ;
} if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME)//这里remote sec_state 是idle
p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; /* Notify all clients waiting for name to be resolved */
for (i = ;i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++)
{
if (btm_cb.p_rmt_name_callback[i] && p_bd_addr)
(*btm_cb.p_rmt_name_callback[i])(p_bd_addr, p_dev_rec->dev_class,
p_dev_rec->sec_bd_name);
}
}
else
{
...
return;
} /* If we were delaying asking UI for a PIN because name was not resolved, ask now */
if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) && p_bd_addr
&& (memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == ) )
{
...
return;
} /* Check if we were delaying bonding because name was not resolved */
if ( btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME)/*这里check 是否有配对流程需要继续*/
{
if (p_bd_addr && memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == )
{
BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete() continue bonding sm4: 0x%04x, status:0x%x", p_dev_rec->sm4, status);
if(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_CANCEL_DD)//
{
btm_sec_bond_cancel_complete();
return;
} if (status != HCI_SUCCESS)
{
btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); if (btm_cb.api.p_auth_complete_callback)
(*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class,
p_dev_rec->sec_bd_name, status);
return;
} /* if peer is very old legacy devices, HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is not reported */
if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4))
{
/* set the KNOWN flag only if BTM_PAIR_FLAGS_REJECTED_CONNECT is not set.*/
/* If it is set, there may be a race condition */
BTM_TRACE_DEBUG ("btm_sec_rmt_name_request_complete IS_SM4_UNKNOWN Flags:0x%04x",
btm_cb.pairing_flags);
if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == )
{
p_dev_rec->sm4 |= BTM_SM4_KNOWN;
}
} BTM_TRACE_DEBUG("%s, SM4 Value: %x, Legacy:%d,IS SM4:%d, Unknown:%d",__FUNCTION__,
p_dev_rec->sm4, BTM_SEC_IS_SM4_LEGACY(p_dev_rec->sm4),//sm4 = known这个变量就认为是legacy
BTM_SEC_IS_SM4(p_dev_rec->sm4),BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)); /* BT 2.1 or carkit, bring up the connection to force the peer to request PIN.
** Else prefetch (btm_sec_check_prefetch_pin will do the prefetching if needed)
*/
if ((p_dev_rec->sm4 != BTM_SM4_KNOWN) || !btm_sec_check_prefetch_pin(p_dev_rec)) //一般走这里的流程,继续create connection
{
/* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */
/* before originating */
if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT)
{
BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: waiting HCI_Connection_Complete after rejecting connection");
}
/* Both we and the peer are 2.1 - continue to create connection */
else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED)//创建dd connection
{
BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: failed to start connection"); btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); if (btm_cb.api.p_auth_complete_callback)
(*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class,
p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL);
}
}
return;
}
....

下面我们 在简单看下 btm_sec_dd_create_conn 的实现:

btm_Sec.c

/*******************************************************************************
**
** Function btm_sec_dd_create_conn
**
** Description This function is called to create the ACL connection for
** the dedicated boding process
**
** Returns void
**
*******************************************************************************/
static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec)
{
tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR);
if (p_lcb && (p_lcb->link_state == LST_CONNECTED || p_lcb->link_state == LST_CONNECTING))/*Connection already exists */
{
...
} /* Make sure an L2cap link control block is available */
if (!p_lcb && (p_lcb = l2cu_allocate_lcb (p_dev_rec->bd_addr, TRUE, BT_TRANSPORT_BR_EDR)) == NULL)
{
...
return(BTM_NO_RESOURCES);
} /* set up the control block to indicated dedicated bonding */
btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE;//0x01 | 0x04 = 0x05 if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE)
{
BTM_TRACE_WARNING ("Security Manager: failed create [%02x%02x%02x%02x%02x%02x]",
p_dev_rec->bd_addr[], p_dev_rec->bd_addr[], p_dev_rec->bd_addr[],
p_dev_rec->bd_addr[], p_dev_rec->bd_addr[], p_dev_rec->bd_addr[]); l2cu_release_lcb(p_lcb);
return(BTM_NO_RESOURCES);
} btm_acl_update_busy_level (BTM_BLI_PAGE_EVT);//update acl BTM_TRACE_DEBUG ("Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]",
p_dev_rec->bd_addr[], p_dev_rec->bd_addr[], p_dev_rec->bd_addr[],
p_dev_rec->bd_addr[], p_dev_rec->bd_addr[], p_dev_rec->bd_addr[]); btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);//更新 btm_cb.pairing_state = BTM_PAIR_STATE_WAIT_PIN_REQ return(BTM_CMD_STARTED);
}

接下来,当controller完成了物理link的建立,配对流程继续:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_connection_comp_evt
**
** Description Process event HCI_CONNECTION_COMP_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_connection_comp_evt (UINT8 *p)
{
UINT8 status;
UINT16 handle;
BD_ADDR bda;
UINT8 link_type;
UINT8 enc_mode; STREAM_TO_UINT8 (status, p);
STREAM_TO_UINT16 (handle, p);
STREAM_TO_BDADDR (bda, p);
STREAM_TO_UINT8 (link_type, p);
STREAM_TO_UINT8 (enc_mode, p); handle = HCID_GET_HANDLE (handle); if (link_type == HCI_LINK_TYPE_ACL)
{
btm_sec_connected (bda, handle, status, enc_mode); l2c_link_hci_conn_comp (status, handle, bda);
}...
}

这里还是两个函数,我们依次看一下:

首先分析一下btm_sec_connected

/*******************************************************************************
**
** Function btm_sec_connected
**
** Description This function is when a connection to the peer device is
** establsihed
**
** Returns void
**
*******************************************************************************/
void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode)
{
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda);
UINT8 res;
BOOLEAN is_pairing_device = FALSE;
tACL_CONN *p_acl_cb;
UINT8 bit_shift = ; btm_acl_resubmit_page(); if (!p_dev_rec)
{
...
}
else /* Update the timestamp for this device */
{ bit_shift = (handle == p_dev_rec->ble_hci_handle) ? :;
p_dev_rec->timestamp = btm_cb.dev_rec_count++; if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND)
{
/* tell L2CAP it's a bonding connection. */
if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
&& (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == )
&& (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ){
...
}
/* always clear the pending flag */
p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND;
}
} #if BLE_INCLUDED == TRUE
p_dev_rec->device_type |= BT_DEVICE_TYPE_BREDR;
#endif p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
&& (memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN) == ) )
{
/* if we rejected incoming connection from bonding device */
if ((status == HCI_ERR_HOST_REJECT_DEVICE)
&&(btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT))
{
... return;
}
/* wait for incoming connection without resetting pairing state */
else if (status == HCI_ERR_CONNECTION_EXISTS)
{
BTM_TRACE_WARNING ("Security Manager: btm_sec_connected: Wait for incoming connection");
return;
} is_pairing_device = TRUE;
} /* If connection was made to do bonding restore link security if changed */
btm_restore_mode(); /* if connection fails during pin request, notify application */
if (status != HCI_SUCCESS)
{
... return;
} /* If initiated dedicated bonding, return the link key now, and initiate disconnect */
/* If dedicated bonding, and we now have a link key, we are all done */
if ( is_pairing_device
&& (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) )
{
...
btm_send_link_key_notif(p_dev_rec);
...
p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; ... if (btm_cb.api.p_auth_complete_callback)
(*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr,
p_dev_rec->dev_class,
p_dev_rec->sec_bd_name, HCI_SUCCESS); btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE);
...
return;
} p_dev_rec->hci_handle = handle; /* role may not be correct here, it will be updated by l2cap, but we need to */
/* notify btm_acl that link is up, so starting of rmt name request will not */
/* set paging flag up */
p_acl_cb = btm_bda_to_acl(bda, BT_TRANSPORT_BR_EDR);
if (p_acl_cb)
{
/* whatever is in btm_establish_continue() without reporting the BTM_BL_CONN_EVT event */
#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE)
/* For now there are a some devices that do not like sending */
/* commands events and data at the same time. */
/* Set the packet types to the default allowed by the device */
btm_set_packet_types (p_acl_cb, btm_cb.btm_acl_pkt_types_supported); if (btm_cb.btm_def_link_policy)
BTM_SetLinkPolicy (p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy);
#endif
}
btm_acl_created (bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle, HCI_ROLE_SLAVE, BT_TRANSPORT_BR_EDR);
/* Initialize security flags. We need to do that because some */
/* authorization complete could have come after the connection is dropped */
/* and that would set wrong flag that link has been authorized already */
p_dev_rec->sec_flags &= ~((BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED |
BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED) << bit_shift);//first clear it if (enc_mode != HCI_ENCRYPT_MODE_DISABLED)
p_dev_rec->sec_flags |= ((BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED) << bit_shift); if (btm_cb.security_mode == BTM_SEC_MODE_LINK)
p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED << bit_shift);
... p_dev_rec->link_key_changed = FALSE; /* After connection is established we perform security if we do not know */
/* the name, or if we are originator because some procedure can have */
/* been scheduled while connection was down */
BTM_TRACE_DEBUG ("is_originator:%d ", p_dev_rec->is_originator);
if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) || p_dev_rec->is_originator)
{
if ((res = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED)//这里报包含 Start get name,Start authentication,Start encryption,一直到最后完成access granted
btm_sec_dev_rec_cback_event (p_dev_rec, res, FALSE);
}
return;
}

简单说一下上面函数的要点:

  1. Allocate acl_db entry :p_acl_cb = btm_bda_to_acl(bda, BT_TRANSPORT_BR_EDR); 
    1. btsnd_hcic_read_rmt_clk_offset (p->hci_handle);//Command: HCI_Read_Clock_Offset
    2. btsnd_hcic_rmt_ver_req (p->hci_handle);//Command: HCI_Read_Remote_Version_Information
    3. btm_read_remote_features (p->hci_handle);//Command: HCI_Read_Remote_Supported_Features
  2.   btm_sec_execute_procedure (p_dev_rec)
    1. RNR 已经做过.
    2. Start authentication 即将要做
    3. Start encryption  后续要做
    4. Start authorization 可能要做  

这里贴一下 btm_bda_to_acl 的代码,就不分析了.

/*******************************************************************************
**
** Function btm_acl_created
**
** Description This function is called by L2CAP when an ACL connection
** is created.
**
** Returns void
**
*******************************************************************************/
void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, BD_NAME bdn,
UINT16 hci_handle, UINT8 link_role, tBT_TRANSPORT transport)
{
tBTM_SEC_DEV_REC *p_dev_rec = NULL;
tACL_CONN *p;
UINT8 xx; BTM_TRACE_DEBUG ("btm_acl_created hci_handle=%d link_role=%d transport=%d",
hci_handle,link_role, transport);
/* Ensure we don't have duplicates */
p = btm_bda_to_acl(bda, transport); //find btm_cb.acl_db ,if found then return .
if (p != (tACL_CONN *)NULL)
{
p->hci_handle = hci_handle;
p->link_role = link_role;
#if BLE_INCLUDED == TRUE
p->transport = transport;
#endif
BTM_TRACE_DEBUG ("Duplicate btm_acl_created: RemBdAddr: %02x%02x%02x%02x%02x%02x",
bda[], bda[], bda[], bda[], bda[], bda[]);
BTM_SetLinkPolicy(p->remote_addr, &btm_cb.btm_def_link_policy);
return;
} /* Allocate acl_db entry */
for (xx = , p = &btm_cb.acl_db[]; xx < MAX_L2CAP_LINKS; xx++, p++)
{
if (!p->in_use)
{
p->in_use = TRUE;
p->hci_handle = hci_handle;
p->link_role = link_role;
p->link_up_issued = FALSE;
memcpy (p->remote_addr, bda, BD_ADDR_LEN);
... /* if BR/EDR do something more */
if (transport == BT_TRANSPORT_BR_EDR)
{
btsnd_hcic_read_rmt_clk_offset (p->hci_handle);//Command: HCI_Read_Clock_Offset
}
btsnd_hcic_rmt_ver_req (p->hci_handle);//Command: HCI_Read_Remote_Version_Information
p_dev_rec = btm_find_dev_by_handle (hci_handle);
... #if (BLE_INCLUDED == TRUE)
/* If here, features are not known yet */
if (p_dev_rec && transport == BT_TRANSPORT_LE)
{
...
}
else
#endif
{
BTM_TRACE_API("%s: begin to btm_read_remote_features", __FUNCTION__);
btm_read_remote_features (p->hci_handle);//Command: HCI_Read_Remote_Supported_Features
} /* read page 1 - on rmt feature event for buffer reasons */
return;
}
}
}

下面简单分析一下btm_sec_execute_procedure 的流程:

btm_Sec.c

/******************************************************************
** S T A T I C F U N C T I O N S
*******************************************************************/ /*******************************************************************************
**
** Function btm_sec_execute_procedure
**
** Description This function is called to start required security
** procedure. There is a case when multiplexing protocol
** calls this function on the originating side, connection to
** the peer will not be established. This function in this
** case performs only authorization.
**
** Returns BTM_SUCCESS - permission is granted
** BTM_CMD_STARTED - in process
** BTM_NO_RESOURCES - permission declined
**
*******************************************************************************/
static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec)
{
BTM_TRACE_EVENT ("btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d",
p_dev_rec->security_required, p_dev_rec->sec_flags, p_dev_rec->sec_state);//this time required = 0x10=BTM_SEC_OUT_AUTHENTICATE p_dev_rec->sec_flags = 0x88--name known /* There is a chance that we are getting name. Wait until done. */
if (p_dev_rec->sec_state != )
return(BTM_CMD_STARTED); /* If any security is required, get the name first */
if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN)
&& (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE))
{
BTM_TRACE_EVENT ("Security Manager: Start get name");
if (!btm_sec_start_get_name (p_dev_rec))
{
return(BTM_NO_RESOURCES);
}
return(BTM_CMD_STARTED);
} /* If connection is not authenticated and authentication is required */
/* start authentication and return PENDING to the caller */
if ((((!(p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))
&& (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE))
|| (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHENTICATE))))
|| (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED)
&& (!p_dev_rec->is_originator
&& (p_dev_rec->security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN))))
&& (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE))
{
/*
* We rely on BTM_SEC_16_DIGIT_PIN_AUTHED being set if MITM is in use,
* as 16 DIGIT is only needed if MITM is not used. Unfortunately, the
* BTM_SEC_AUTHENTICATED is used for both MITM and non-MITM
* authenticated connections, hence we cannot distinguish here.
*/ BTM_TRACE_EVENT ("Security Manager: Start authentication"); /*
* If we do have a link-key, but we end up here because we need an
* upgrade, then clear the link-key known and authenticated flag before
* restarting authentication.
* WARNING: If the controller has link-key, it is optional and
* recommended for the controller to send a Link_Key_Request.
* In case we need an upgrade, the only alternative would be to delete
* the existing link-key. That could lead to very bad user experience
* or even IOP issues, if a reconnect causes a new connection that
* requires an upgrade.
*/
if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)
&& (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED)
&& (!p_dev_rec->is_originator && (p_dev_rec->security_required
& BTM_SEC_IN_MIN_16_DIGIT_PIN)))) {
p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED
| BTM_SEC_AUTHENTICATED);//if BTM_SEC_LINK_KEY_KNOWN ,then clear the flag.
} if (!btm_sec_start_authentication (p_dev_rec))//start authentication
{
return(BTM_NO_RESOURCES);
}
return(BTM_CMD_STARTED);
} /* If connection is not encrypted and encryption is required */
/* start encryption and return PENDING to the caller */
if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)
&& (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT))
|| (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT)))
&& (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE))
{ BTM_TRACE_EVENT ("Security Manager: Start encryption"); if (!btm_sec_start_encryption (p_dev_rec))
{
return(BTM_NO_RESOURCES);
}
return(BTM_CMD_STARTED);
} if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) &&
(p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256))
{
BTM_TRACE_EVENT("%s: Security Manager: SC only service, but link key type is 0x%02x -",
"security failure", __FUNCTION__, p_dev_rec->link_key_type);
return (BTM_FAILED_ON_SECURITY);
} /* If connection is not authorized and authorization is required */
/* start authorization and return PENDING to the caller */
if (!(p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED)
&& (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHORIZE))
|| (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHORIZE))))
{
BTM_TRACE_EVENT ("service id:%d, is trusted:%d",
p_dev_rec->p_cur_service->service_id,
(BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask,
p_dev_rec->p_cur_service->service_id)));
if ((btm_sec_are_all_trusted(p_dev_rec->trusted_mask) == FALSE) &&
(p_dev_rec->p_cur_service->service_id < BTM_SEC_MAX_SERVICES) &&
(BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask,
p_dev_rec->p_cur_service->service_id) == FALSE))
{
BTM_TRACE_EVENT ("Security Manager: Start authorization");
return(btm_sec_start_authorization (p_dev_rec));
}
} /* All required security procedures already established */
p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_IN_AUTHORIZE |
BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE |
BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT |
BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER |
BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); BTM_TRACE_EVENT ("Security Manager: trusted:0x%04x%04x", p_dev_rec->trusted_mask[], p_dev_rec->trusted_mask[]);
BTM_TRACE_EVENT ("Security Manager: access granted"); return(BTM_SUCCESS);
}

上面包含多个过程,但是当前就是执行start authentication 的流程.

现在我们看看  btu_hcif_connection_comp_evt 中的另一个函数:

l2c_link_hci_conn_comp (status, handle, bda);

l2c_link.c

/*******************************************************************************
**
** Function l2c_link_hci_conn_comp
**
** Description This function is called when an HCI Connection Complete
** event is received.
**
** Returns void
**
*******************************************************************************/
BOOLEAN l2c_link_hci_conn_comp (UINT8 status, UINT16 handle, BD_ADDR p_bda)
{
tL2C_CONN_INFO ci;
tL2C_LCB *p_lcb;
tL2C_CCB *p_ccb;
tBTM_SEC_DEV_REC *p_dev_info = NULL;
L2CAP_TRACE_WARNING ("enter l2c_link_hci_conn_comp libs_liu");
btm_acl_update_busy_level (BTM_BLI_PAGE_DONE_EVT); /* Save the parameters */
ci.status = status;
memcpy (ci.bd_addr, p_bda, BD_ADDR_LEN); /* See if we have a link control block for the remote device */
p_lcb = l2cu_find_lcb_by_bd_addr (ci.bd_addr, BT_TRANSPORT_BR_EDR); /* If we don't have one, this is an error */
if (!p_lcb)
{
L2CAP_TRACE_WARNING ("L2CAP got conn_comp for unknown BD_ADDR");
return (FALSE);
} if (p_lcb->link_state != LST_CONNECTING)
{
L2CAP_TRACE_ERROR ("L2CAP got conn_comp in bad state: %d status: 0x%d", p_lcb->link_state, status); if (status != HCI_SUCCESS)
l2c_link_hci_disc_comp (p_lcb->handle, status); return (FALSE);
} /* Save the handle */
p_lcb->handle = handle; if (ci.status == HCI_SUCCESS)
{
/* Connected OK. Change state to connected */
p_lcb->link_state = LST_CONNECTED;
counter_add("l2cap.conn.ok", ); /* Get the peer information if the l2cap flow-control/rtrans is supported */
l2cu_send_peer_info_req (p_lcb, L2CAP_EXTENDED_FEATURES_INFO_TYPE);// Code: Information request l2cap /* Tell BTM Acl management about the link */
if ((p_dev_info = btm_find_dev (p_bda)) != NULL)
btm_acl_created (ci.bd_addr, p_dev_info->dev_class,
p_dev_info->sec_bd_name, handle,
p_lcb->link_role, BT_TRANSPORT_BR_EDR);// Command: HCI_Write_Link_Policy_Settings
else
btm_acl_created (ci.bd_addr, NULL, NULL, handle, p_lcb->link_role, BT_TRANSPORT_BR_EDR); BTM_SetLinkSuperTout (ci.bd_addr, btm_cb.btm_def_link_super_tout);// Command: HCI_Write_Link_Supervision_Timeout /* If dedicated bonding do not process any further */
if (p_lcb->is_bonding) //marked at start bonding: l2cu_update_lcb_4_bonding (bd_addr, TRUE);
{
if (l2cu_start_post_bond_timer(handle))
/*start I2cap bonding timeout. the defaut idle: l2cb.idle_timeout = L2CAP_LINK_INACTIVITY_TOUT = 4(l2c_main.c)
now set a new bonding timeout :#define L2CAP_BONDING_TIMEOUT 3 (bt_target.h)*/
return (TRUE);//return
}
...
return (TRUE);
}
}

上面函数做的主要的事情是:

  1. 设置link state:p_lcb->link_state = LST_CONNECTED;
  2. Code: Information request   l2cap
  3. Command: HCI_Write_Link_Policy_Settings
  4. Command: HCI_Write_Link_Supervision_Timeout
  5. l2cu_start_post_bond_timer   bonding timer

当host端发起HCI_Authentication_Requested之后,往往contrller端还会请求Event: Link Key Request .

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_link_key_request_evt
**
** Description Process event HCI_LINK_KEY_REQUEST_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_link_key_request_evt (UINT8 *p)
{
BD_ADDR bda; STREAM_TO_BDADDR (bda, p);
btm_sec_link_key_request (bda);
}

对于该请求还是回到了btm_sec.c

/*******************************************************************************
**
** Function btm_sec_link_key_request
**
** Description This function is called when controller requests link key
**
** Returns Pointer to the record or NULL
**
*******************************************************************************/
void btm_sec_link_key_request (UINT8 *p_bda)
{
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); BTM_TRACE_EVENT ("btm_sec_link_key_request() BDA: %02x:%02x:%02x:%02x:%02x:%02x",
p_bda[], p_bda[], p_bda[], p_bda[], p_bda[], p_bda[]);
if( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) &&
(btm_cb.collision_start_time != ) &&
(memcmp (btm_cb.p_collided_dev_rec->bd_addr, p_bda, BD_ADDR_LEN) == ) )
{
... /*collision */
return;
}
if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)
{
btsnd_hcic_link_key_req_reply (p_bda, p_dev_rec->link_key);/*if stored link key*/
return;
} /* Notify L2CAP to increase timeout */
l2c_pin_code_request (p_bda); /* increase link timeout to L2CAP_LINK_CONNECT_TOUT_EXT = 120 */ /* The link key is not in the database and it is not known to the manager */
btsnd_hcic_link_key_neg_reply (p_bda); // Command: HCI_Link_Key_Request_Negative_Reply
}

这个处理很简单,有link key 就发送btsnd_hcic_link_key_req_reply  没有的话就HCI_Link_Key_Request_Negative_Reply

下面看一下 Event: Read Remote Version Information Complete 的处理:

btm_acl.c

/*******************************************************************************
**
** Function btm_read_remote_version_complete
**
** Description This function is called when the command complete message
** is received from the HCI for the remote version info.
**
** Returns void
**
*******************************************************************************/
void btm_read_remote_version_complete (UINT8 *p)
{
tACL_CONN *p_acl_cb = &btm_cb.acl_db[];
UINT8 status;
UINT16 handle;
int xx;
BTM_TRACE_DEBUG ("btm_read_remote_version_complete");
STREAM_TO_UINT8 (status, p);
if (status == HCI_SUCCESS)
{
STREAM_TO_UINT16 (handle, p); /* Look up the connection by handle and copy features */
for (xx = ; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++)
{
if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle))
{
STREAM_TO_UINT8 (p_acl_cb->lmp_version, p);//btm_cb.acl_db[x].lmp_version
STREAM_TO_UINT16 (p_acl_cb->manufacturer, p);//btm_cb.acl_db[x].manufacturer
STREAM_TO_UINT16 (p_acl_cb->lmp_subversion, p);//btm_cb.acl_db[x].lmp_subversion
break;
}
}
}
}

其就是将几个LMP 的version 保存到btm_cb.acl_db[x]结构中.

接下来我们看看Event: Read Remote Supported Features Complete的处理:

btm_acl.c

/*******************************************************************************
**
** Function btm_read_remote_features_complete
**
** Description This function is called when the remote supported features
** complete event is received from the HCI.
**
** Returns void
**
*******************************************************************************/
void btm_read_remote_features_complete (UINT8 *p)
{
tACL_CONN *p_acl_cb;
UINT8 status;
UINT16 handle;
UINT8 acl_idx; BTM_TRACE_DEBUG ("btm_read_remote_features_complete");
STREAM_TO_UINT8 (status, p); if (status != HCI_SUCCESS)
{
BTM_TRACE_ERROR ("btm_read_remote_features_complete failed (status 0x%02x)", status);
return;
} STREAM_TO_UINT16 (handle, p); if ((acl_idx = btm_handle_to_acl_index(handle)) >= MAX_L2CAP_LINKS)
{
BTM_TRACE_ERROR("btm_read_remote_features_complete handle=%d invalid", handle);
return;
} p_acl_cb = &btm_cb.acl_db[acl_idx]; /* Copy the received features page */
STREAM_TO_ARRAY(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0], p,
HCI_FEATURE_BYTES_PER_PAGE);//save remote feature in btm_cb.acl_db[acl_idx].peer_lmp_features if ((HCI_LMP_EXTENDED_SUPPORTED(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) &&
(controller_get_interface()->supports_reading_remote_extended_features()))
{
/* if the remote controller has extended features and local controller supports
** HCI_Read_Remote_Extended_Features command then start reading these feature starting
** with extended features page 1 */
BTM_TRACE_DEBUG ("Start reading remote extended features");
btm_read_remote_ext_features(handle, HCI_EXT_FEATURES_PAGE_1);// Command: HCI_Read_Remote_Extended_Features
return;
} ...
}

上面做了两件事:

  1. save remote feature in btm_cb.acl_db[acl_idx].peer_lmp_features  //临时
  2. Command: HCI_Read_Remote_Extended_Features (if support )

下面我们继续看看对于Event: Read_Remote_Extended_Features_Complete的处理:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_read_rmt_ext_features_comp_evt
**
** Description Process event HCI_READ_RMT_EXT_FEATURES_COMP_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_read_rmt_ext_features_comp_evt (UINT8 *p)
{
UINT8 *p_cur = p;
UINT8 status;
UINT16 handle; STREAM_TO_UINT8 (status, p_cur); if (status == HCI_SUCCESS)
btm_read_remote_ext_features_complete(p);
else
{
STREAM_TO_UINT16 (handle, p_cur);
btm_read_remote_ext_features_failed(status, handle);
}
}

btm_acl.c:


    /*******************************************************************************
**
** Function btm_read_remote_ext_features_complete
**
** Description This function is called when the remote extended features
** complete event is received from the HCI.
**
** Returns void
**
*******************************************************************************/
void btm_read_remote_ext_features_complete (UINT8 *p)
{
tACL_CONN *p_acl_cb;
UINT8 page_num, max_page;
UINT16 handle;
UINT8 acl_idx; ++p;
STREAM_TO_UINT16 (handle, p);
STREAM_TO_UINT8 (page_num, p);
STREAM_TO_UINT8 (max_page, p); /* Validate parameters */
if ((acl_idx = btm_handle_to_acl_index(handle)) >= MAX_L2CAP_LINKS)
{
BTM_TRACE_ERROR("btm_read_remote_ext_features_complete handle=%d invalid", handle);
return;
}
... p_acl_cb = &btm_cb.acl_db[acl_idx]; /* Copy the received features page */
STREAM_TO_ARRAY(p_acl_cb->peer_lmp_features[page_num], p, HCI_FEATURE_BYTES_PER_PAGE);//store remote extend feature in btm_cb.acl_db[acl_idx].peer_lmp_features /* If there is the next remote features page and
* we have space to keep this page data - read this page */
if ((page_num < max_page) && (page_num < HCI_EXT_FEATURES_PAGE_MAX))
{
page_num++;
BTM_TRACE_DEBUG("BTM reads next remote extended features page (%d)", page_num);
btm_read_remote_ext_features (handle, page_num);//if there is .
return;
} /* Process the pages */
btm_process_remote_ext_features (p_acl_cb, (UINT8) (page_num + )); /* Continue with HCI connection establishment */
btm_establish_continue (p_acl_cb);
}

上面做的事情:

  1. store remote extend feature in btm_cb.acl_db[acl_idx].peer_lmp_features
  2. btm_process_remote_ext_features
    1.  store remote feature in p_dev_rec->features
    2. p_dev_rec->sm4 = BTM_SM4_TRUE
  3. btm_establish_continue
    1. Command: HCI_Change_Connection_Packet_Type
    2. Command: HCI_Write_Link_Policy_Settings
    3. update remote feature to upper layer:btm_cb.p_bl_changed_cb
    4. btm_acl_update_busy_level (BTM_BLI_ACL_UP_EVT);

btm_acl.c

    /*******************************************************************************
**
** Function btm_process_remote_ext_features
**
** Description Local function called to process all extended features pages
** read from a remote device.
**
** Returns void
**
*******************************************************************************/
void btm_process_remote_ext_features (tACL_CONN *p_acl_cb, UINT8 num_read_pages)
{
UINT16 handle = p_acl_cb->hci_handle;
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle);
UINT8 page_idx; BTM_TRACE_DEBUG ("btm_process_remote_ext_features"); /* Make sure we have the record to save remote features information */
if (p_dev_rec == NULL)
{
/* Get a new device; might be doing dedicated bonding */
p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr);
} p_acl_cb->num_read_pages = num_read_pages;
p_dev_rec->num_read_pages = num_read_pages; /* Move the pages to placeholder */
for (page_idx = ; page_idx < num_read_pages; page_idx++)
{
if (page_idx > HCI_EXT_FEATURES_PAGE_MAX)
{
BTM_TRACE_ERROR("%s: page=%d unexpected", __FUNCTION__, page_idx);
break;
}
memcpy (p_dev_rec->features[page_idx], p_acl_cb->peer_lmp_features[page_idx],
HCI_FEATURE_BYTES_PER_PAGE);//store remote feature in p_dev_rec->features
}
... /* Store the Peer Security Capabilites (in SM4 and rmt_sec_caps) */
btm_sec_set_peer_sec_caps(p_acl_cb, p_dev_rec);
/*1.if remote device support ssp ,then set p_dev_rec->sm4 = BTM_SM4_TRUE
**2. otherwise set p_dev_rec->sm4 = BTM_SM4_KNOWN (means it does not support ssp)
**3. do more if SC is supported.
*/ ...
}

上面做了两件事:

  1. store remote feature in p_dev_rec->features
  2. p_dev_rec->sm4 = BTM_SM4_TRUE (if support) .这个其实在之前btm_sec_rmt_host_support_feat_evt 就已经知晓了对方的相关feature

当前分析的case, 是host端没有保存link key,那么配对的流程需要继续.下面我们看看

Event: HCI IO Capability Request 的处理:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_io_cap_request_evt
**
** Description Process event HCI_IO_CAPABILITY_REQUEST_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_io_cap_request_evt (UINT8 *p)
{
btm_io_capabilities_req(p);
}

btm_sec.c

    /*******************************************************************************
**
** Function btm_io_capabilities_req
**
** Description This function is called when LM request for the IO
** capability of the local device and
** if the OOB data is present for the device in the event
**
** Returns void
**
*******************************************************************************/
void btm_io_capabilities_req (UINT8 *p)
{
tBTM_SP_IO_REQ evt_data;
UINT8 err_code = ;
tBTM_SEC_DEV_REC *p_dev_rec;
BOOLEAN is_orig = TRUE;
UINT8 callback_rc = BTM_SUCCESS; STREAM_TO_BDADDR (evt_data.bd_addr, p); /* setup the default response according to compile options */
/* assume that the local IO capability does not change
* loc_io_caps is initialized with the default value */
evt_data.io_cap = btm_cb.devcb.loc_io_caps;
evt_data.oob_data = BTM_OOB_NONE;//none
evt_data.auth_req = BTM_DEFAULT_AUTH_REQ; p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); BTM_TRACE_DEBUG("%s:Security mode: %d, Num Read Remote Feat pages: %d", __FUNCTION__,
btm_cb.security_mode, p_dev_rec->num_read_pages);
... p_dev_rec->sm4 |= BTM_SM4_TRUE;//sm4 = 0x11 has updated before. BTM_TRACE_EVENT("%s: State: %s Flags: 0x%04x p_cur_service: 0x%08x p_dev_rec->sm4 = 0x%X libs_liu",
__FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state),
btm_cb.pairing_flags, p_dev_rec->p_cur_service,p_dev_rec->sm4); if (p_dev_rec->p_cur_service) //have not got one
{
BTM_TRACE_EVENT("%s: cur_service psm: 0x%04x, security_flags: 0x%04x",
__FUNCTION__, p_dev_rec->p_cur_service->psm,
p_dev_rec->p_cur_service->security_flags);
} switch (btm_cb.pairing_state)
{
... /* initiator, at this point it is expected to be dedicated bonding
initiated by local device */
case BTM_PAIR_STATE_WAIT_PIN_REQ:
if (!memcmp (evt_data.bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN))
{
evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ;//BTM_AUTH_AP_YES = 3
}
else
{
err_code = HCI_ERR_HOST_BUSY_PAIRING;
}
break; /* any other state is unexpected */
default:
break;
}
... evt_data.is_orig = is_orig; /* Notify L2CAP to increase timeout */
l2c_pin_code_request (evt_data.bd_addr);//L2CAP_LINK_CONNECT_TOUT_EXT = 120 memcpy (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN); /* coverity[uninit_use_in_call]
Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp"
False-positive: False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p);
*/
if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN))
memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS);//change btm_cb.pairing_state to BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS callback_rc = BTM_SUCCESS;
if (p_dev_rec->sm4 & BTM_SM4_UPGRADE)//BTM_SM4_UPGRADE = 0x04
{
p_dev_rec->sm4 &= ~BTM_SM4_UPGRADE; /* link key upgrade: always use SPGB_YES - assuming we want to save the link key */
evt_data.auth_req = BTM_AUTH_SPGB_YES;
}
else if (btm_cb.api.p_sp_callback)//bta_dm_sp_cback
{
/* the callback function implementation may change the IO capability... */
/*actually ,it is not changed */
callback_rc = (*btm_cb.api.p_sp_callback) (BTM_SP_IO_REQ_EVT, (tBTM_SP_EVT_DATA *)&evt_data);
} if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != evt_data.oob_data))//the user does not indicate "reply later" by setting the oob_data to unknown
{
if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD))
{
evt_data.auth_req = (BTM_AUTH_DD_BOND | (evt_data.auth_req & BTM_AUTH_YN_BIT));
}
... /* if the user does not indicate "reply later" by setting the oob_data to unknown */
/* send the response right now. Save the current IO capability in the control block */
btm_cb.devcb.loc_auth_req = evt_data.auth_req;
btm_cb.devcb.loc_io_caps = evt_data.io_cap; BTM_TRACE_EVENT("%s: State: %s IO_CAP:%d oob_data:%d auth_req:%d",
__FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state), evt_data.io_cap,
evt_data.oob_data, evt_data.auth_req); btsnd_hcic_io_cap_req_reply(evt_data.bd_addr, evt_data.io_cap,
evt_data.oob_data, evt_data.auth_req);
}
}

上面过程 主要就是

  1. 组建本端的IO capabilities
  2. change btm_cb.pairing_state to BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS
  3. Save the current IO capability in the control block: 
    1.   btm_cb.devcb.loc_auth_req   = evt_data.auth_req;
    2. btm_cb.devcb.loc_io_caps    = evt_data.io_cap;
  4. btsnd_hcic_io_cap_req_reply

IO的交互过程,本端已经将IO发送给对方,那么对方也会将其IO 发给我们,我们看下,对于对方IO的处理过程:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_io_cap_response_evt
**
** Description Process event HCI_IO_CAPABILITY_RESPONSE_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_io_cap_response_evt (UINT8 *p)
{
btm_io_capabilities_rsp(p);
}

btm_sec.c

    /*******************************************************************************
**
** Function btm_io_capabilities_rsp
**
** Description This function is called when the IO capability of the
** specified device is received
**
** Returns void
**
*******************************************************************************/
void btm_io_capabilities_rsp (UINT8 *p)
{
tBTM_SEC_DEV_REC *p_dev_rec;
tBTM_SP_IO_RSP evt_data; STREAM_TO_BDADDR (evt_data.bd_addr, p);
STREAM_TO_UINT8 (evt_data.io_cap, p);
STREAM_TO_UINT8 (evt_data.oob_data, p);
STREAM_TO_UINT8 (evt_data.auth_req, p); /* Allocate a new device record or reuse the oldest one */
p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); /* If no security is in progress, this indicates incoming security */
if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE)
{
...
} /* Notify L2CAP to increase timeout */
l2c_pin_code_request (evt_data.bd_addr); //L2CAP_LINK_CONNECT_TOUT_EXT = 120 /* We must have a device record here.
* Use the connecting device's CoD for the connection */
/* coverity[uninit_use_in_call]
Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp"
FALSE-POSITIVE error from Coverity test-tool. evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p);
*/
if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN))
memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN);
... /* save the IO capability in the device record */
p_dev_rec->rmt_io_caps = evt_data.io_cap;
p_dev_rec->rmt_auth_req = evt_data.auth_req; if (btm_cb.api.p_sp_callback)
(*btm_cb.api.p_sp_callback) (BTM_SP_IO_RSP_EVT, (tBTM_SP_EVT_DATA *)&evt_data);
}

上面函数做的事情:

  1. 保存remote IO  
    1. p_dev_rec->rmt_io_caps  = evt_data.io_cap;
    2. p_dev_rec->rmt_auth_req = evt_data.auth_req;
  2.   通知upper layer: p_sp_callback  : bta_dm_sp_cback ,保存IO
    1. pairing_cb.auth_req = auth_req;
    2. pairing_cb.io_cap = io_cap;

接下来就走到了

Event: HCI User Confirmation Request 的流程.

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_user_conf_request_evt
**
** Description Process event HCI_USER_CONFIRMATION_REQUEST_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_user_conf_request_evt (UINT8 *p)
{
btm_proc_sp_req_evt(BTM_SP_CFM_REQ_EVT, p);
}

btm_sec.c

    /*******************************************************************************
**
** Function btm_proc_sp_req_evt
**
** Description This function is called to process/report
** HCI_USER_CONFIRMATION_REQUEST_EVT
** or HCI_USER_PASSKEY_REQUEST_EVT
** or HCI_USER_PASSKEY_NOTIFY_EVT
**
** Returns void
**
*******************************************************************************/
void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p)
{
tBTM_STATUS status = BTM_ERR_PROCESSING;
tBTM_SP_EVT_DATA evt_data;
UINT8 *p_bda = evt_data.cfm_req.bd_addr;
tBTM_SEC_DEV_REC *p_dev_rec; /* All events start with bd_addr */
STREAM_TO_BDADDR (p_bda, p); BTM_TRACE_EVENT ("btm_proc_sp_req_evt() BDA: %08x%04x event: 0x%x, State: %s",
(p_bda[]<<) + (p_bda[]<<) + (p_bda[]<<) + p_bda[], (p_bda[] << ) + p_bda[],
event, btm_pair_state_descr(btm_cb.pairing_state)); if ( ((p_dev_rec = btm_find_dev (p_bda)) != NULL)
&& (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
&& (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == ) )
{
memcpy (evt_data.cfm_req.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
memcpy (evt_data.cfm_req.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); BCM_STRNCPY_S ((char *)evt_data.cfm_req.bd_name, sizeof(evt_data.cfm_req.bd_name), (char *)p_dev_rec->sec_bd_name, BTM_MAX_REM_BD_NAME_LEN); switch (event)
{
case BTM_SP_CFM_REQ_EVT:
/* Numeric confirmation. Need user to conf the passkey */
btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM); /* The device record must be allocated in the "IO cap exchange" step */
STREAM_TO_UINT32 (evt_data.cfm_req.num_val, p); evt_data.cfm_req.just_works = TRUE; /* process user confirm req in association with the auth_req param */
if ( (p_dev_rec->rmt_io_caps == BTM_IO_CAP_IO)
&& (btm_cb.devcb.loc_io_caps == BTM_IO_CAP_IO)
&& ((p_dev_rec->rmt_auth_req & BTM_AUTH_SP_YES) || (btm_cb.devcb.loc_auth_req & BTM_AUTH_SP_YES)) )
{
/* Both devices are DisplayYesNo and one or both devices want to authenticate
-> use authenticated link key */
evt_data.cfm_req.just_works = FALSE;
} BTM_TRACE_DEBUG ("btm_proc_sp_req_evt() just_works:%d, io loc:%d, rmt:%d, auth loc:%d, rmt:%d",
evt_data.cfm_req.just_works, btm_cb.devcb.loc_io_caps, p_dev_rec->rmt_io_caps,
btm_cb.devcb.loc_auth_req, p_dev_rec->rmt_auth_req); evt_data.cfm_req.loc_auth_req = btm_cb.devcb.loc_auth_req;
evt_data.cfm_req.rmt_auth_req = p_dev_rec->rmt_auth_req;
evt_data.cfm_req.loc_io_caps = btm_cb.devcb.loc_io_caps;
evt_data.cfm_req.rmt_io_caps = p_dev_rec->rmt_io_caps;
break; case BTM_SP_KEY_NOTIF_EVT:
/* Passkey notification (other side is a keyboard) */
...
break; case BTM_SP_KEY_REQ_EVT:
/* HCI_USER_PASSKEY_REQUEST_EVT */
btm_sec_change_pairing_state (BTM_PAIR_STATE_KEY_ENTRY);
break;
} if (btm_cb.api.p_sp_callback)
{
status = (*btm_cb.api.p_sp_callback) (event, (tBTM_SP_EVT_DATA *)&evt_data);
BTM_TRACE_DEBUG ("calling BTM_ConfirmReqReply with status: %d libs_liu", status);
if (status != BTM_NOT_AUTHORIZED)
{
BTM_TRACE_DEBUG(" return libs_liu");
return;
}
/* else BTM_NOT_AUTHORIZED means when the app wants to reject the req right now */
}
... }
}

上面函数做的事情:

  1. 设置btm_cb.pairing_state的状态为BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM
  2. evt_data.cfm_req.just_works = TRUE;
  3. 将本端和对端的IO以及authentication req信息写进evt_data.cfm_req
  4. 调用btm_cb.api.p_sp_callback(event (tBTM_SP_EVT_DATA *)&evt_data)  上报到upper layer. //bta_dm_sp_cback

下面看看bta_dm_sp_cback  对于该event 的实现:

bta_dm_sp_cback

/*******************************************************************************
**
** Function bta_dm_sp_cback
**
** Description simple pairing callback from BTM
**
** Returns void
**
*******************************************************************************/
static UINT8 bta_dm_sp_cback (tBTM_SP_EVT event, tBTM_SP_EVT_DATA *p_data)
{
tBTM_STATUS status = BTM_CMD_STARTED;
tBTA_DM_SEC sec_event;
tBTA_DM_SEC_EVT pin_evt = BTA_DM_SP_KEY_NOTIF_EVT; APPL_TRACE_EVENT("bta_dm_sp_cback: %d", event);
if (!bta_dm_cb.p_sec_cback){
APPL_TRACE_EVENT("bta_dm_sp_cback:libs_liu BTM_NOT_AUTHORIZED");
return BTM_NOT_AUTHORIZED;
} /* TODO_SP */
switch(event)
{
...
case BTM_SP_CFM_REQ_EVT:
pin_evt = BTA_DM_SP_CFM_REQ_EVT;
bta_dm_cb.just_works = sec_event.cfm_req.just_works = p_data->cfm_req.just_works;
sec_event.cfm_req.loc_auth_req = p_data->cfm_req.loc_auth_req;
sec_event.cfm_req.rmt_auth_req = p_data->cfm_req.rmt_auth_req;
sec_event.cfm_req.loc_io_caps = p_data->cfm_req.loc_io_caps;
sec_event.cfm_req.rmt_io_caps = p_data->cfm_req.rmt_io_caps; /* continue to next case */
/* Passkey entry mode, mobile device with output capability is very
unlikely to receive key request, so skip this event */
/*case BTM_SP_KEY_REQ_EVT: */
case BTM_SP_KEY_NOTIF_EVT:
if(BTM_SP_CFM_REQ_EVT == event)
{
/* Due to the switch case falling through below to BTM_SP_KEY_NOTIF_EVT,
call remote name request using values from cfm_req */
if(p_data->cfm_req.bd_name[] == )
{
...
}
else
{
/* Due to the switch case falling through below to BTM_SP_KEY_NOTIF_EVT,
copy these values into key_notif from cfm_req */
bdcpy(sec_event.key_notif.bd_addr, p_data->cfm_req.bd_addr);
BTA_COPY_DEVICE_CLASS(sec_event.key_notif.dev_class, p_data->cfm_req.dev_class);
BCM_STRNCPY_S((char*)sec_event.key_notif.bd_name, sizeof(BD_NAME),
(char*)p_data->cfm_req.bd_name, (BD_NAME_LEN-));
sec_event.key_notif.bd_name[BD_NAME_LEN-] = ;
}
} bta_dm_cb.num_val = sec_event.key_notif.passkey = p_data->key_notif.passkey;
if (BTM_SP_KEY_NOTIF_EVT == event)
{
...
}
bta_dm_cb.p_sec_cback(pin_evt, &sec_event);//p_sec_cback = bte_dm_evt break; default:
status = BTM_NOT_AUTHORIZED;
break;
}
APPL_TRACE_EVENT("dm status: %d", status);
return status;
}

这里做的事情:

  1. 构建 sec_event.cfm_req 结构,保存本端和对端的IO以及authentication req
  2. 调用 bta_dm_cb.p_sec_cback(pin_evt, &sec_event)  来向上层 汇报:p_sec_cback = bte_dm_evt

下面看看回调做的事情:

bte_dm_evt  -->btif_dm_upstreams_evt -->btif_dm_ssp_cfm_req_evt

btif_dm.c

/*******************************************************************************
**
** Function btif_dm_ssp_cfm_req_evt
**
** Description Executes SSP confirm request event in btif context
**
** Returns void
**
*******************************************************************************/
static void btif_dm_ssp_cfm_req_evt(tBTA_DM_SP_CFM_REQ *p_ssp_cfm_req)
{
bt_bdaddr_t bd_addr;
bt_bdname_t bd_name;
UINT32 cod;
BOOLEAN is_incoming = !(pairing_cb.state == BT_BOND_STATE_BONDING);
int dev_type; BTIF_TRACE_DEBUG("%s", __FUNCTION__); /* Remote properties update */
if (!btif_get_device_type(p_ssp_cfm_req->bd_addr, &dev_type))
{
dev_type = BT_DEVICE_TYPE_BREDR;
}
btif_update_remote_properties(p_ssp_cfm_req->bd_addr, p_ssp_cfm_req->bd_name,
p_ssp_cfm_req->dev_class, (tBT_DEVICE_TYPE) dev_type); bdcpy(bd_addr.address, p_ssp_cfm_req->bd_addr);
memcpy(bd_name.name, p_ssp_cfm_req->bd_name, BD_NAME_LEN); /* Set the pairing_cb based on the local & remote authentication requirements */
bond_state_changed(BT_STATUS_SUCCESS, &bd_addr, BT_BOND_STATE_BONDING); /* has been update before .so the state will be updated this time */ /* if just_works and bonding bit is not set treat this as temporary */ if (p_ssp_cfm_req->just_works && !(p_ssp_cfm_req->loc_auth_req & BTM_AUTH_BONDS) &&
!(p_ssp_cfm_req->rmt_auth_req & BTM_AUTH_BONDS) &&
!(check_cod((bt_bdaddr_t*)&p_ssp_cfm_req->bd_addr, COD_HID_POINTING)))
pairing_cb.bond_type = BOND_TYPE_TEMPORARY;
else
pairing_cb.bond_type = BOND_TYPE_PERSISTENT; btm_set_bond_type_dev(p_ssp_cfm_req->bd_addr, pairing_cb.bond_type);//save bond type :p_dev_rec->bond_type = BOND_TYPE_PERSISTENT pairing_cb.is_ssp = TRUE; /* If JustWorks auto-accept */
if (p_ssp_cfm_req->just_works)
{
/// Don't show user confirm dialog if just work pairing request. BTIF_TRACE_EVENT("%s: User consent needed for incoming pairing request. bond_type: %d, loc_io_caps: %d, rmt_io_caps: %d",
__FUNCTION__, pairing_cb.bond_type , p_ssp_cfm_req->loc_io_caps, p_ssp_cfm_req->rmt_io_caps);
BTIF_TRACE_EVENT("%s: Auto-accept JustWorks pairing", __FUNCTION__);
btif_dm_ssp_reply(&bd_addr, BT_SSP_VARIANT_CONSENT, TRUE, );// Command: HCI_User_Confirmation_Request_Reply
return;
...
} cod = devclass2uint(p_ssp_cfm_req->dev_class); if (cod == ) {
LOG_DEBUG("%s cod is 0, set as unclassified", __func__);
cod = COD_UNCLASSIFIED;
} pairing_cb.sdp_attempts = ;
/* notify upper level */
HAL_CBACK(bt_hal_cbacks, ssp_request_cb, &bd_addr, &bd_name, cod,
(p_ssp_cfm_req->just_works ? BT_SSP_VARIANT_CONSENT : BT_SSP_VARIANT_PASSKEY_CONFIRMATION),
p_ssp_cfm_req->num_val);
}

上面函数主要做了如下的事情:

  1. btif_update_remote_properties  //看起来没什么需要更新 的.
  2. bond_state_changed ,因为之前已经更新过BT_BOND_STATE_BONDING,所以也不会真正的更新.
  3. pairing_cb.bond_type = BOND_TYPE_PERSISTENT;
  4. btm_set_bond_type_dev(p_ssp_cfm_req->bd_addr, pairing_cb.bond_type);//save bond type :p_dev_rec->bond_type = BOND_TYPE_PERSISTENT
  5. pairing_cb.is_ssp = TRUE;
  6. btif_dm_ssp_reply(&bd_addr, BT_SSP_VARIANT_CONSENT, TRUE, 0);// Command: HCI_User_Confirmation_Request_Reply 
    1.   btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); 改变btm_cb.pairing_state的状态为BTM_PAIR_STATE_WAIT_AUTH_COMPLETE

  接下去就是等待simple pairing的结束.

我们看看

Event: HCI Simple Pairing Complete 的处理:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_simple_pair_complete_evt
**
** Description Process event HCI_SIMPLE_PAIRING_COMPLETE_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_simple_pair_complete_evt (UINT8 *p)
{
btm_simple_pair_complete(p);
}

btm_sec.c

/*******************************************************************************
**
** Function btm_simple_pair_complete
**
** Description This function is called when simple pairing process is
** complete
**
** Returns void
**
*******************************************************************************/
void btm_simple_pair_complete (UINT8 *p)
{
tBTM_SP_COMPLT evt_data;
tBTM_SEC_DEV_REC *p_dev_rec;
UINT8 status;
BOOLEAN disc = FALSE; status = *p++;
STREAM_TO_BDADDR (evt_data.bd_addr, p); if ((p_dev_rec = btm_find_dev (evt_data.bd_addr)) == NULL)
{
//error
return;
}
BTM_TRACE_EVENT ("btm_simple_pair_complete() Pair State: %s Status:%d sec_state: %u",
btm_pair_state_descr(btm_cb.pairing_state), status, p_dev_rec->sec_state); evt_data.status = BTM_ERR_PROCESSING;
if (status == HCI_SUCCESS)
{
evt_data.status = BTM_SUCCESS;
p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED;//0x88 | 0x02 = 0x8a
}
... /* Let the pairing state stay active, p_auth_complete_callback will report the failure */
memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); if (btm_cb.api.p_sp_callback)//bta_dm_sp_cback
(*btm_cb.api.p_sp_callback) (BTM_SP_COMPLT_EVT, (tBTM_SP_EVT_DATA *)&evt_data);
...
}

上面函数做的事情:

  1. 组建evt_data 结构
    1. evt_data.status = BTM_SUCCESS; (it depends)
    2. evt_data.bd_addr
    3. evt_data.dev_class
  2. (*btm_cb.api.p_sp_callback) (BTM_SP_COMPLT_EVT, (tBTM_SP_EVT_DATA *)&evt_data);//bta_dm_sp_cback   (do nothing about this event )
    1.    do not report this event - handled by link_key_callback or auth_complete_callback

"需要看看这个函数bta_dm_sp_cback (simple pairing)处理的event:"

从上面可以看到,上层的回调bta_dm_sp_cback 并不会处理这个BTM_SP_COMPLT_EVT 事件,等到link key 上报或者authentication complete 再处理.

接下来,我们继续看

Event: Link Key Notification 的处理:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_link_key_notification_evt
**
** Description Process event HCI_LINK_KEY_NOTIFICATION_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_link_key_notification_evt (UINT8 *p)
{
BD_ADDR bda;
LINK_KEY key;
UINT8 key_type; STREAM_TO_BDADDR (bda, p);
STREAM_TO_ARRAY16 (key, p);
STREAM_TO_UINT8 (key_type, p); btm_sec_link_key_notification (bda, key, key_type);
}

btm_sec.c

/*******************************************************************************
**
** Function btm_sec_link_key_notification
**
** Description This function is called when a new connection link key is
** generated
**
** Returns Pointer to the record or NULL
**
*******************************************************************************/
void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type)
{
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda);
BOOLEAN we_are_bonding = FALSE;
BOOLEAN ltk_derived_lk = FALSE; BTM_TRACE_EVENT ("btm_sec_link_key_notification() BDA:%04x%08x, TYPE: %d",
(p_bda[]<<)+p_bda[], (p_bda[]<<)+(p_bda[]<<)+(p_bda[]<<)+p_bda[],
key_type); if ((key_type >= BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_COMBINATION) &&
(key_type <= BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_AUTH_COMB_P_256))
{
ltk_derived_lk = TRUE;
key_type -= BTM_LTK_DERIVED_LKEY_OFFSET;
}
/* If connection was made to do bonding restore link security if changed */
btm_restore_mode(); if (key_type != BTM_LKEY_TYPE_CHANGED_COMB)
p_dev_rec->link_key_type = key_type; p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN;//0x8a | 0x10 = 0x9a /* BR/EDR connection, update the encryption key size to be 16 as always */
p_dev_rec->enc_key_size = ;
memcpy (p_dev_rec->link_key, p_link_key, LINK_KEY_LEN); if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
&& (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == ) )
{
if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)
we_are_bonding = TRUE;
...
} /* We will save link key only if the user authorized it - BTE report link key in all cases */ if (btm_cb.api.p_link_key_callback)//bta_dm_new_link_key_cback
{
if (ltk_derived_lk)
{
BTM_TRACE_DEBUG ("btm_sec_link_key_notification() LTK derived LK is saved already"
" (key_type = %d)", p_dev_rec->link_key_type);
}
else
{
(*btm_cb.api.p_link_key_callback) (p_bda, p_dev_rec->dev_class,
p_dev_rec->sec_bd_name,
p_link_key, p_dev_rec->link_key_type);
}
} }

这个函数做的事情:

  1. p_dev_rec->link_key_type = key_type = 4
  2. p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN;//0x8a | 0x10 = 0x9a
  3. p_dev_rec->enc_key_size = 16;
  4. memcpy (p_dev_rec->link_key, p_link_key, LINK_KEY_LEN);
  5. btm_cb.api.p_link_key_callback)//bta_dm_new_link_key_cback  上报upper layer (bta)

关于btm_cb.api回调函数的注册是在蓝牙enable的时候 就已经注册了.BTM_SecRegister((tBTM_APPL_INFO*)&bta_security);

我们看看bta_security的实现:

bta_dm_act.c

/* bta security callback */
const tBTM_APPL_INFO bta_security =
{
&bta_dm_authorize_cback,
&bta_dm_pin_cback,
&bta_dm_new_link_key_cback,
&bta_dm_authentication_complete_cback,
&bta_dm_bond_cancel_complete_cback,
#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE)
&bta_dm_sp_cback
#else
NULL
#endif
#if BLE_INCLUDED == TRUE
#if SMP_INCLUDED == TRUE
,&bta_dm_ble_smp_cback
#endif
,&bta_dm_ble_id_key_cback
#endif }

下面我们看看bta_dm_new_link_key_cback 的实现:

/*******************************************************************************
**
** Function bta_dm_new_link_key_cback
**
** Description Callback from BTM to notify new link key
**
** Returns void
**
*******************************************************************************/
static UINT8 bta_dm_new_link_key_cback(BD_ADDR bd_addr, DEV_CLASS dev_class,
BD_NAME bd_name, LINK_KEY key, UINT8 key_type)
{
tBTA_DM_SEC sec_event;
tBTA_DM_AUTH_CMPL *p_auth_cmpl;
UINT8 event;
UNUSED(dev_class); memset (&sec_event, , sizeof(tBTA_DM_SEC)); /* Not AMP Key type */
if (key_type != HCI_LKEY_TYPE_AMP_WIFI && key_type != HCI_LKEY_TYPE_AMP_UWB)
{
event = BTA_DM_AUTH_CMPL_EVT;
p_auth_cmpl = &sec_event.auth_cmpl; bdcpy(p_auth_cmpl->bd_addr, bd_addr); memcpy(p_auth_cmpl->bd_name, bd_name, (BD_NAME_LEN-));
p_auth_cmpl->bd_name[BD_NAME_LEN-] = ; p_auth_cmpl->key_present = TRUE;
p_auth_cmpl->key_type = key_type;
p_auth_cmpl->success = TRUE; memcpy(p_auth_cmpl->key, key, LINK_KEY_LEN);//strore key in p_auth_cmpl->key
sec_event.auth_cmpl.fail_reason = HCI_SUCCESS; // Report the BR link key based on the BR/EDR address and type
BTM_ReadDevInfo(bd_addr, &sec_event.auth_cmpl.dev_type, &sec_event.auth_cmpl.addr_type);
if(bta_dm_cb.p_sec_cback)//p_sec_cback = bte_dm_evt
bta_dm_cb.p_sec_cback(event, &sec_event);
}
...
return BTM_CMD_STARTED;
}

上面函数做的事情:

  1. 组建sec_event.auth_cmpl 结构.并赋值
    1.   p_auth_cmpl->bd_addr
    2. p_auth_cmpl->bd_name
    3. p_auth_cmpl->key_present
    4. p_auth_cmpl->key_type
    5. p_auth_cmpl->success
    6. p_auth_cmpl->key //store link key
  2.   bta_dm_cb.p_sec_cback)//p_sec_cback = bte_dm_evt  ,event = BTA_DM_AUTH_CMPL_EVT

下面看看 bte_dm_evt对于BTA_DM_AUTH_CMPL_EVT  的处理:

bte_dm_evt  -->btif_dm_upstreams_evt -->btif_dm_auth_cmpl_evt

btif_dm.c

/*******************************************************************************
**
** Function btif_dm_auth_cmpl_evt
**
** Description Executes authentication complete event in btif context
**
** Returns void
**
*******************************************************************************/
static void btif_dm_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl)
{
/* Save link key, if not temporary */
bt_bdaddr_t bd_addr;
bt_status_t status = BT_STATUS_FAIL;
bt_bond_state_t state = BT_BOND_STATE_NONE;
BOOLEAN skip_sdp = FALSE; BTIF_TRACE_DEBUG("%s: bond state=%d", __func__, pairing_cb.state); bdcpy(bd_addr.address, p_auth_cmpl->bd_addr);
if ( (p_auth_cmpl->success == TRUE) && (p_auth_cmpl->key_present) )
{
if ((p_auth_cmpl->key_type < HCI_LKEY_TYPE_DEBUG_COMB) ||
(p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB) ||
(p_auth_cmpl->key_type == HCI_LKEY_TYPE_CHANGED_COMB) ||
(p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB_P_256) ||
pairing_cb.bond_type == BOND_TYPE_PERSISTENT)
{
bt_status_t ret;
BTIF_TRACE_DEBUG("%s: Storing link key. key_type=0x%x, bond_type=%d",
__FUNCTION__, p_auth_cmpl->key_type, pairing_cb.bond_type);
ret = btif_storage_add_bonded_device(&bd_addr,
p_auth_cmpl->key, p_auth_cmpl->key_type,
pairing_cb.pin_code_len);//stote into bt_config.conf
ASSERTC(ret == BT_STATUS_SUCCESS, "storing link key failed", ret);
}
else
{
BTIF_TRACE_DEBUG("%s: Temporary key. Not storing. key_type=0x%x, bond_type=%d",
__FUNCTION__, p_auth_cmpl->key_type, pairing_cb.bond_type);
if(pairing_cb.bond_type == BOND_TYPE_TEMPORARY)
{
...
return;
}
}
} // Skip SDP for certain HID Devices
//because HID devices have do SDP before
if (p_auth_cmpl->success)
{ btif_storage_set_remote_addr_type(&bd_addr, p_auth_cmpl->addr_type);
btif_update_remote_properties(p_auth_cmpl->bd_addr,
p_auth_cmpl->bd_name, NULL, p_auth_cmpl->dev_type);//update to upper layer :HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
pairing_cb.timeout_retries = ;
status = BT_STATUS_SUCCESS;
state = BT_BOND_STATE_BONDED;
bdcpy(bd_addr.address, p_auth_cmpl->bd_addr); if (check_sdp_bl(&bd_addr) && check_cod_hid(&bd_addr, COD_HID_MAJOR))
{
LOG_WARN("%s:skip SDP", __FUNCTION__);
skip_sdp = TRUE;
}
if(!pairing_cb.is_local_initiated && skip_sdp)
{
bond_state_changed(status, &bd_addr, state);
...
/* Send the event to the BTIF */
HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
BT_STATUS_SUCCESS, &bd_addr, , &prop);
}
else
{
/* Trigger SDP on the device */
pairing_cb.sdp_attempts = ;; /* If bonded due to cross-key, save the static address too*/
if(pairing_cb.state == BT_BOND_STATE_BONDING &&
(bdcmp(p_auth_cmpl->bd_addr, pairing_cb.bd_addr) != ))
{
BTIF_TRACE_DEBUG("%s: bonding initiated due to cross key, adding static address",
__func__);
bdcpy(pairing_cb.static_bdaddr.address, p_auth_cmpl->bd_addr);
} if(btif_dm_inquiry_in_progress)
btif_dm_cancel_discovery(); btif_dm_get_remote_services(&bd_addr);//BTA_DmDiscover begin to discovery
}
// Do not call bond_state_changed_cb yet. Wait until remote service discovery is complete
}
else
...
}

上面流程主要做的事情:

  1. btif_storage_add_bonded_device(&bd_addr,p_auth_cmpl->key, p_auth_cmpl->key_type,pairing_cb.pin_code_len);//stote into bt_config.conf

  2. btif_update_remote_properties(p_auth_cmpl->bd_addr,p_auth_cmpl->bd_name, NULL, p_auth_cmpl->dev_type);//update to upper layer :HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,

  3. btif_dm_get_remote_services(&bd_addr);//BTA_DmDiscover begin to discovery

这里需要注意的是,当前并没有上报bond_state_changed_cb ,要等服务搜索完成之后才会做,(HID devices 有所不同)

接下来在看看

Event: Authentication Complete 的处理流程:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_authentication_comp_evt
**
** Description Process event HCI_AUTHENTICATION_COMP_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_authentication_comp_evt (UINT8 *p)
{
UINT8 status;
UINT16 handle; STREAM_TO_UINT8 (status, p);
STREAM_TO_UINT16 (handle, p); btm_sec_auth_complete (handle, status);
}

btm_sec.c

/*******************************************************************************
**
** Function btm_sec_auth_complete
**
** Description This function is when authentication of the connection is
** completed by the LM
**
** Returns void
**
*******************************************************************************/
void btm_sec_auth_complete (UINT16 handle, UINT8 status)
{
UINT8 old_sm4;
tBTM_PAIRING_STATE old_state = btm_cb.pairing_state;
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle);
BOOLEAN are_bonding = FALSE;
...
btm_cb.collision_start_time = ; btm_restore_mode(); ...
/* keep the old sm4 flag and clear the retry bit in control block */
old_sm4 = p_dev_rec->sm4;
p_dev_rec->sm4 &= ~BTM_SM4_RETRY; if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
&& (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)
&& (memcmp (p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == ) )
are_bonding = TRUE; if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
&& (memcmp (p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == ) )
btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE);//change state to BTM_PAIR_STATE_IDLE -->btm_cb.pairing_state //now p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING ...
/* Currently we do not notify user if it is a keyboard which connects */
/* User probably Disabled the keyboard while it was asleap. Let her try */
if (btm_cb.api.p_auth_complete_callback)
{
/* report the suthentication status */
if (old_state != BTM_PAIR_STATE_IDLE)
(*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr,
p_dev_rec->dev_class,
p_dev_rec->sec_bd_name, status);//bta_dm_authentication_complete_cback
} p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; /* If this is a bonding procedure can disconnect the link now */
if (are_bonding)
{
p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; if (status != HCI_SUCCESS)
{
...
}
else
{
BTM_TRACE_DEBUG ("TRYING TO DECIDE IF CAN USE SMP_BR_CHNL");
...
l2cu_start_post_bond_timer (p_dev_rec->hci_handle);//after bonded ,the link timeout is set to BTU_TTYPE_L2CAP_LINK = 2s
} return;
}
...
}

上面函数做的事情主要是:

  1. btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE);//change state to  BTM_PAIR_STATE_IDLE -->btm_cb.pairing_state
  2. (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr,p_dev_rec->dev_class,p_dev_rec->sec_bd_name, status);//bta_dm_authentication_complete_cback

  3. p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; 从BTM_SEC_STATE_AUTHENTICATING(1)-->BTM_SEC_STATE_IDLE(0)
  4. l2cu_start_post_bond_timer (p_dev_rec->hci_handle);//after bonded ,the link timeout is set to BTU_TTYPE_L2CAP_LINK = 2s 创建定时器,2s

下面我们看一下上面的回调:bta_dm_authentication_complete_cback

bta_dm_act.c

    /*******************************************************************************
**
** Function bta_dm_authentication_complete_cback
**
** Description Authentication complete callback from BTM
**
** Returns void
**
*******************************************************************************/
static UINT8 bta_dm_authentication_complete_cback(BD_ADDR bd_addr, DEV_CLASS dev_class,BD_NAME bd_name, int result)
{
tBTA_DM_SEC sec_event;
UNUSED(dev_class); if(result != BTM_SUCCESS)
{
...
} return BTM_SUCCESS;
}

仅仅进行错误处理.

到此authentication 的流程就已经结束了.

下面来看看encryption的流程.

这里分析的case 是音箱,那么主要是看A2DP的security level.其level 定义如下:

bta_api.h

#define BTA_SEC_AUTHENTICATE    (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_OUT_AUTHENTICATE)  //0x12

这个在A2dp service 启动的时候,会初始化security level

btif_av.c

/*******************************************************************************
**
** Function btif_av_execute_service
**
** Description Initializes/Shuts down the service
**
** Returns BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
**
*******************************************************************************/
bt_status_t btif_av_execute_service(BOOLEAN b_enable)
{
if (b_enable)
{
/* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
* handle this request in order to allow incoming connections to succeed.
* We need to put this back once support for this is added */ /* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
* auto-suspend av streaming on AG events(SCO or Call). The suspend shall
* be initiated by the app/audioflinger layers */
#if (AVRC_METADATA_INCLUDED == TRUE)
BTA_AvEnable(BTA_SEC_AUTHENTICATE,
BTA_AV_FEAT_RCTG|BTA_AV_FEAT_METADATA|BTA_AV_FEAT_VENDOR|BTA_AV_FEAT_NO_SCO_SSPD
#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
|BTA_AV_FEAT_RCCT
|BTA_AV_FEAT_ADV_CTRL
#endif
,bte_av_callback);
...
#endif
BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, , bte_av_media_callback);
}
else {
BTA_AvDeregister(btif_av_cb.bta_handle);
BTA_AvDisable();
}
return BT_STATUS_SUCCESS;
}

可以知道其初始化的level = BTA_SEC_AUTHENTICATE = 0x12

A2dp 建立连接的过程中,需要在l2cap 上面建立AVDTP 的channel.这里面就会涉及到security 的问题.

建立连接之前,有一个设置security level的步骤

avdt_ccb_act.c

/*******************************************************************************
**
** Function avdt_ccb_set_conn
**
** Description Set CCB variables associated with AVDT_ConnectReq().
**
**
** Returns void.
**
*******************************************************************************/
void avdt_ccb_set_conn(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data)
{
/* save callback */
p_ccb->p_conn_cback = p_data->connect.p_cback;//保存回调bta_av_stream0_cback /* set security level */
BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_AVDTP, p_data->connect.sec_mask,
AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG);
}

BTM_SetSecurityLevel 会将0x3092 接下来执行:

/*******************************************************************************
**
** Function avdt_ccb_chan_open
**
** Description This function calls avdt_ad_open_req() to
** initiate a signaling channel connection.
**
**
** Returns void.
**
*******************************************************************************/
void avdt_ccb_chan_open(tAVDT_CCB *p_ccb, tAVDT_CCB_EVT *p_data)
{
UNUSED(p_data); BTM_SetOutService(p_ccb->peer_addr, BTM_SEC_SERVICE_AVDTP, AVDT_CHAN_SIG);
avdt_ad_open_req(AVDT_CHAN_SIG, p_ccb, NULL, AVDT_INT);
}

这里做的事情:

  1. BTM_SetOutService(p_ccb->peer_addr, BTM_SEC_SERVICE_AVDTP, AVDT_CHAN_SIG);
    1. btm_cb.p_out_serv = p_serv_rec;
    2. p_dev_rec->p_cur_service = p_serv_rec;

继续看 avdt_ad_open_req(AVDT_CHAN_SIG, p_ccb, NULL, AVDT_INT);的实现:

avdt_ad.c  (ad = adapter)

/*******************************************************************************
**
** Function avdt_ad_open_req
**
** Description This function is called by a CCB or SCB to open a transport
** channel. This function allocates and initializes a
** transport channel table entry. The channel can be opened
** in two roles: as an initiator or acceptor. When opened
** as an initiator the function will start an L2CAP connection.
** When opened as an acceptor the function simply configures
** the table entry to listen for an incoming channel.
**
**
** Returns Nothing.
**
*******************************************************************************/
void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role)
{
tAVDT_TC_TBL *p_tbl;
UINT16 lcid; if((p_tbl = avdt_ad_tc_tbl_alloc(p_ccb)) == NULL)
{
AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl");
return;
} p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb);
AVDT_TRACE_DEBUG("avdt_ad_open_req: type: %d, role: %d, tcid:%d",
type, role, p_tbl->tcid); if (type == AVDT_CHAN_SIG)
{
/* if signaling, get mtu from registration control block */
p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu;
p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO;
}
else
... /* if we're acceptor, we're done; just sit back and listen */
if (role == AVDT_ACP)
{
p_tbl->state = AVDT_AD_ST_ACP;
}
/* else we're inititator, start the L2CAP connection */
else
{
p_tbl->state = AVDT_AD_ST_CONN; /* call l2cap connect req */
if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != )//l2cap connection
{
/* if connect req ok, store tcid in lcid table */
avdt_cb.ad.lcid_tbl[lcid - L2CAP_BASE_APPL_CID] = avdt_ad_tc_tbl_to_idx(p_tbl);
AVDT_TRACE_DEBUG("avdt_cb.ad.lcid_tbl[%d] = %d",
(lcid - L2CAP_BASE_APPL_CID), avdt_ad_tc_tbl_to_idx(p_tbl)); avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][p_tbl->tcid].lcid = lcid;
AVDT_TRACE_DEBUG("avdt_cb.ad.rt_tbl[%d][%d].lcid = 0x%x",
avdt_ccb_to_idx(p_ccb), p_tbl->tcid,
lcid);
}
else
{
/* if connect req failed, call avdt_ad_tc_close_ind() */
avdt_ad_tc_close_ind(p_tbl, );
}
}
}

这里的重点就是发起l2cap的连接.

l2cap_api.c

/*******************************************************************************
**
** Function L2CA_ConnectReq
**
** Description Higher layers call this function to create an L2CAP connection.
** Note that the connection is not established at this time, but
** connection establishment gets started. The callback function
** will be invoked when connection establishes or fails.
**
** Returns the CID of the connection, or 0 if it failed to start
**
*******************************************************************************/
UINT16 L2CA_ConnectReq (UINT16 psm, BD_ADDR p_bd_addr)
{
return L2CA_ErtmConnectReq (psm, p_bd_addr, NULL);
}
/*******************************************************************************
**
** Function L2CA_ErtmConnectReq
**
** Description Higher layers call this function to create an L2CAP connection.
** Note that the connection is not established at this time, but
** connection establishment gets started. The callback function
** will be invoked when connection establishes or fails.
**
** Parameters: PSM: L2CAP PSM for the connection
** BD address of the peer
** Enhaced retransmission mode configurations ** Returns the CID of the connection, or 0 if it failed to start
**
*******************************************************************************/
UINT16 L2CA_ErtmConnectReq (UINT16 psm, BD_ADDR p_bd_addr, tL2CAP_ERTM_INFO *p_ertm_info)
{
tL2C_LCB *p_lcb;
tL2C_CCB *p_ccb;
tL2C_RCB *p_rcb; counter_add("l2cap.conn.req", );
L2CAP_TRACE_API ("L2CA_ErtmConnectReq() PSM: 0x%04x BDA: %08x%04x p_ertm_info: 0x%08x allowed:0x%x preferred:%d", psm,
(p_bd_addr[]<<)+(p_bd_addr[]<<)+(p_bd_addr[]<<)+p_bd_addr[],
(p_bd_addr[]<<)+p_bd_addr[], p_ertm_info,
(p_ertm_info) ? p_ertm_info->allowed_modes : ,
(p_ertm_info) ? p_ertm_info->preferred_mode : ); /* Fail if the PSM is not registered */
if ((p_rcb = l2cu_find_rcb_by_psm (psm)) == NULL)
{
L2CAP_TRACE_WARNING ("L2CAP - no RCB for L2CA_conn_req, PSM: 0x%04x", psm);
return ();
} /* First, see if we already have a link to the remote */
/* assume all ERTM l2cap connection is going over BR/EDR for now */
if ((p_lcb = l2cu_find_lcb_by_bd_addr (p_bd_addr, BT_TRANSPORT_BR_EDR)) == NULL)
{
...
} /* Allocate a channel control block */
if ((p_ccb = l2cu_allocate_ccb (p_lcb, )) == NULL)
{
L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_req, PSM: 0x%04x", psm);
return ();
} /* Save registration info */
p_ccb->p_rcb = p_rcb; ... /* If link is up, start the L2CAP connection */
if (p_lcb->link_state == LST_CONNECTED)
{
l2c_csm_execute (p_ccb, L2CEVT_L2CA_CONNECT_REQ, NULL);//enter l2c_csm_execute sm
}
...
/* Return the local CID as our handle */
return (p_ccb->local_cid);
}

这里的重点 是进入到l2cap 的状态机来执行L2CEVT_L2CA_CONNECT_REQ  的事件.

l2c_csm.c

/*******************************************************************************
**
** Function l2c_csm_execute
**
** Description This function executes the state machine.
**
** Returns void
**
*******************************************************************************/
void l2c_csm_execute (tL2C_CCB *p_ccb, UINT16 event, void *p_data)
{
switch (p_ccb->chnl_state)
{
case CST_CLOSED:
l2c_csm_closed (p_ccb, event, p_data);
break;
...
    case L2CEVT_L2CA_CONNECT_REQ:                       /* API connect request  */
/* Cancel sniff mode if needed */
{
tBTM_PM_PWR_MD settings;
// btla-specific ++
memset((void*)&settings, , sizeof(settings));
// btla-specific --
settings.mode = BTM_PM_MD_ACTIVE;
/* COVERITY
Event uninit_use_in_call: Using uninitialized value "settings" (field "settings".timeout uninitialized) in call to function "BTM_SetPowerMode" [details]
Event uninit_use_in_call: Using uninitialized value "settings.max" in call to function "BTM_SetPowerMode" [details]
Event uninit_use_in_call: Using uninitialized value "settings.min" in call to function "BTM_SetPowerMode"
// FALSE-POSITIVE error from Coverity test-tool. Please do NOT remove following comment.
// coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode the other data members of tBTM_PM_PWR_MD are ignored
*/
BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p_ccb->p_lcb->remote_bd_addr, &settings);
}
L2CAP_TRACE_API ("L2CAP - after BTM_SetPowerMode libs_liu");
/* If sec access does not result in started SEC_COM or COMP_NEG are already processed */
if (btm_sec_l2cap_access_req (p_ccb->p_lcb->remote_bd_addr, p_ccb->p_rcb->psm,
p_ccb->p_lcb->handle, TRUE, &l2c_link_sec_comp, p_ccb) == BTM_CMD_STARTED)
p_ccb->chnl_state = CST_ORIG_W4_SEC_COMP;
break;

这里的重点 是 建立符合安全等级的channel.从这里也可以看出来,建立channel的第一步就要检查security level.

tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle,
CONNECTION_TYPE conn_type,
tBTM_SEC_CALLBACK *p_callback,
void *p_ref_data)
{
tBTM_SEC_DEV_REC *p_dev_rec;
tBTM_SEC_SERV_REC *p_serv_rec;
UINT16 security_required;
UINT16 old_security_required;
BOOLEAN old_is_originator;
tBTM_STATUS rc = BTM_SUCCESS;
BOOLEAN chk_acp_auth_done = FALSE;
BOOLEAN is_originator;
BOOLEAN transport = FALSE; /* should check PSM range in LE connection oriented L2CAP connection */ is_originator = conn_type; BTM_TRACE_DEBUG ("%s() is_originator:%d, 0x%x psm = %d ",__func__, is_originator, p_ref_data,psm); /* Find or get oldest record */
p_dev_rec = btm_find_or_alloc_dev (bd_addr); p_dev_rec->hci_handle = handle; /* Find the service record for the PSM */
p_serv_rec = btm_sec_find_first_serv (conn_type, psm); /* Services level0 by default have no security */
if ((btm_sec_is_serv_level0(psm)) && (!btm_cb.devcb.secure_connections_only))
{
(*p_callback) (bd_addr,transport, p_ref_data, BTM_SUCCESS_NO_SECURITY);
return(BTM_SUCCESS);
}
//delete something
{
if (btm_cb.security_mode == BTM_SEC_MODE_SC)
{
security_required = btm_sec_set_serv_level4_flags (p_serv_rec->security_flags,
is_originator);
}
else
{
security_required = p_serv_rec->security_flags;// 0x3092 update in :btm_sec_set_security_level
}
} BTM_TRACE_DEBUG("%s: security_required 0x%04x, is_originator 0x%02x, psm 0x%04x",
__FUNCTION__, security_required, is_originator, psm); if ((!is_originator) && (security_required & BTM_SEC_MODE4_LEVEL4))
...
... /* Save pointer to service record */
p_dev_rec->p_cur_service = p_serv_rec; /* Modify security_required in btm_sec_l2cap_access_req for Lisbon */
if (btm_cb.security_mode == BTM_SEC_MODE_SP ||
btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
btm_cb.security_mode == BTM_SEC_MODE_SC)
{
if (BTM_SEC_IS_SM4(p_dev_rec->sm4))
{
if (is_originator)
{
/* SM4 to SM4 -> always authenticate & encrypt */
security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT);//the security_required = 0x3092 | 0x20 = 0x30b2
}
else /* acceptor */
{
/* SM4 to SM4: the acceptor needs to make sure the authentication is already done */
chk_acp_auth_done = TRUE;
/* SM4 to SM4 -> always authenticate & encrypt */
security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT);
}
}
else if (!(BTM_SM4_KNOWN & p_dev_rec->sm4))
{
...
return (BTM_CMD_STARTED);
}
} BTM_TRACE_DEBUG ("%s() sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d", __func__,
p_dev_rec->sm4, p_dev_rec->sec_flags, security_required, chk_acp_auth_done); old_security_required = p_dev_rec->security_required;
old_is_originator = p_dev_rec->is_originator;
p_dev_rec->security_required = security_required;//update p_dev_rec->security_required
p_dev_rec->p_ref_data = p_ref_data;
p_dev_rec->is_originator = is_originator; /* If there are multiple service records used through the same PSM */
/* leave security decision for the multiplexor on the top */
... /* if the originator is using dynamic PSM in legacy mode, do not start any security process now
* The layer above L2CAP needs to carry out the security requirement after L2CAP connect
* response is received */
... p_dev_rec->p_callback = p_callback;//store callback = l2c_link_sec_comp if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID
|| p_dev_rec->last_author_service_id != p_dev_rec->p_cur_service->service_id)
{
/* Although authentication and encryption are per connection
** authorization is per access request. For example when serial connection
** is up and authorized and client requests to read file (access to other
** scn), we need to request user's permission again.
*/
p_dev_rec->sec_flags &= ~BTM_SEC_AUTHORIZED;//set it as non AUTHORIZED,actually ,the security_required does not conclude this one
} if (BTM_SEC_IS_SM4(p_dev_rec->sm4))
{
if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) &&
(p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256))
{
/* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case */
if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != )
{
p_dev_rec->sm4 |= BTM_SM4_UPGRADE;
}
p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED |
BTM_SEC_AUTHENTICATED);
BTM_TRACE_DEBUG ("%s: sec_flags:0x%x", __FUNCTION__, p_dev_rec->sec_flags);
}
else
{
/* If we already have a link key to the connected peer, is it secure enough? */
btm_sec_check_upgrade(p_dev_rec, is_originator);//no need update.
}
} BTM_TRACE_EVENT ("%s() PSM:%d Handle:%d State:%d Flags: 0x%x Required: 0x%x Service ID:%d",
__func__, psm, handle, p_dev_rec->sec_state, p_dev_rec->sec_flags,
p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED)//Start encryption
{
p_dev_rec->p_callback = NULL;
(*p_callback) (bd_addr, transport, p_dev_rec->p_ref_data, (UINT8)rc);
} return(rc);
}

这里做的事情:

  1. security_required = p_serv_rec->security_flags;//  0x3092  update in :btm_sec_set_security_level 首先获取初始化(经过设置)的security level
  2. p_dev_rec->p_cur_service = p_serv_rec;  //Save pointer to service record   与security  device关联.
  3. security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT);//the security_required = 0x3092 | 0x20 = 0x30b2   //重新规划 security_required
  4. p_dev_rec->security_required = security_required;//update p_dev_rec->security_required
  5. p_dev_rec->p_callback        = p_callback;//store callback  = l2c_link_sec_comp
  6. btm_sec_execute_procedure (p_dev_rec) //Start encryption

接下来 我们再次看看 btm_sec_execute_procedure 的流程,这里执行的就是encryption 流程

btm_sec.c

/******************************************************************
** S T A T I C F U N C T I O N S
*******************************************************************/ /*******************************************************************************
**
** Function btm_sec_execute_procedure
**
** Description This function is called to start required security
** procedure. There is a case when multiplexing protocol
** calls this function on the originating side, connection to
** the peer will not be established. This function in this
** case performs only authorization.
**
** Returns BTM_SUCCESS - permission is granted
** BTM_CMD_STARTED - in process
** BTM_NO_RESOURCES - permission declined
**
*******************************************************************************/
static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec)
{
BTM_TRACE_EVENT ("btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d",
p_dev_rec->security_required, p_dev_rec->sec_flags, p_dev_rec->sec_state); ... /* If any security is required, get the name first */
if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN)
&& (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE))
{
...
} /* If connection is not authenticated and authentication is required */
/* start authentication and return PENDING to the caller */
... /* If connection is not encrypted and encryption is required */
/* start encryption and return PENDING to the caller */
if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)
&& (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT))
|| (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT)))
&& (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE))
{ BTM_TRACE_EVENT ("Security Manager: Start encryption"); if (!btm_sec_start_encryption (p_dev_rec))//p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING;
{
return(BTM_NO_RESOURCES);
}
return(BTM_CMD_STARTED);
}
... /* If connection is not authorized and authorization is required */
/* start authorization and return PENDING to the caller */
...
/* All required security procedures already established */
p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_IN_AUTHORIZE |
BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE |
BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT |
BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER |
BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE);//clear all flag BTM_TRACE_EVENT ("Security Manager: trusted:0x%04x%04x", p_dev_rec->trusted_mask[], p_dev_rec->trusted_mask[]);
BTM_TRACE_EVENT ("Security Manager: access granted"); return(BTM_SUCCESS);
}

上面走的流程就是btm_sec_start_encryption .这里还是设置了 p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING;

接下来我们看看

Event: Encryption Change 的处理:

btu_hcif.c

/*******************************************************************************
**
** Function btu_hcif_encryption_change_evt
**
** Description Process event HCI_ENCRYPTION_CHANGE_EVT
**
** Returns void
**
*******************************************************************************/
static void btu_hcif_encryption_change_evt (UINT8 *p)
{
UINT8 status;
UINT16 handle;
UINT8 encr_enable; STREAM_TO_UINT8 (status, p);
STREAM_TO_UINT16 (handle, p);
STREAM_TO_UINT8 (encr_enable, p); btm_acl_encrypt_change (handle, status, encr_enable);
btm_sec_encrypt_change (handle, status, encr_enable);
}

btm_acl_encrypt_change 主要是处理role switch 相关,略去.

btm_sec.c

/*******************************************************************************
**
** Function btm_sec_encrypt_change
**
** Description This function is when encryption of the connection is
** completed by the LM
**
** Returns void
**
*******************************************************************************/
void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable)
{
tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle);
#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
tACL_CONN *p_acl = NULL;
UINT8 acl_idx = btm_handle_to_acl_index(handle);
#endif
BTM_TRACE_EVENT ("Security Manager: encrypt_change status:%d State:%d, encr_enable = %d",
status, (p_dev_rec) ? p_dev_rec->sec_state : , encr_enable);
BTM_TRACE_DEBUG ("before update p_dev_rec->sec_flags=0x%x", (p_dev_rec) ? p_dev_rec->sec_flags : );
... if ((status == HCI_SUCCESS) && encr_enable)
{
if (p_dev_rec->hci_handle == handle) {
p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED);
if (p_dev_rec->pin_code_length >= ||
p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB ||
p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) {
p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED;
}
}
else
{
p_dev_rec->sec_flags |= (BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED);
}
}
... BTM_TRACE_DEBUG ("after update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags ); #if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
if (acl_idx != MAX_L2CAP_LINKS)
p_acl = &btm_cb.acl_db[acl_idx]; if (p_acl != NULL)
btm_sec_check_pending_enc_req(p_dev_rec, p_acl->transport, encr_enable); if (p_acl && p_acl->transport == BT_TRANSPORT_LE)
...
else
{
/* BR/EDR connection, update the encryption key size to be 16 as always */
p_dev_rec->enc_key_size = ;
} BTM_TRACE_DEBUG ("in %s new_encr_key_256 is %d",
__func__, p_dev_rec->new_encryption_key_is_p256); if ((status == HCI_SUCCESS) && encr_enable && (p_dev_rec->hci_handle == handle))
...
#else
btm_sec_check_pending_enc_req (p_dev_rec, BT_TRANSPORT_BR_EDR, encr_enable);
#endif /* BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE */ /* If this encryption was started by peer do not need to do anything */
... p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;//set p_dev_rec->sec_state
/* If encryption setup failed, notify the waiting layer */
... /* Encryption setup succeeded, execute the next security procedure, if any */
status = (UINT8)btm_sec_execute_procedure (p_dev_rec);
/* If there is no next procedure, or procedure failed to start, notify the caller */
if (status != BTM_CMD_STARTED)
btm_sec_dev_rec_cback_event (p_dev_rec, status, FALSE);
}

上面函数要点:

  1. p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED);  //更新 sec_flag
  2. p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;//set p_dev_rec->sec_state
  3. status = (UINT8)btm_sec_execute_procedure (p_dev_rec);
  4. btm_sec_dev_rec_cback_event (p_dev_rec, status, FALSE);//notify the caller

这里简单看一下这个回调的内容:

/*******************************************************************************
**
** Function btm_sec_dev_rec_cback_event
**
** Description This function calls the callback function with the given
** result and clear the callback function.
**
** Parameters: void
**
*******************************************************************************/
void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res, BOOLEAN is_le_transport)
{
tBTM_SEC_CALLBACK *p_callback = p_dev_rec->p_callback;//l2c_link_sec_comp   if (p_dev_rec->p_callback)
{
p_dev_rec->p_callback = NULL; #if BLE_INCLUDED == TRUE
if (is_le_transport)
(*p_callback) (p_dev_rec->ble.pseudo_addr, BT_TRANSPORT_LE, p_dev_rec->p_ref_data, res);
else
#endif
(*p_callback) (p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, p_dev_rec->p_ref_data, res);
} btm_sec_check_pending_reqs();
}

这里就是调用l2c_link_sec_comp   继续去执行connection request以下的流程,并且把 p_dev_rec->p_callback 清掉.

到这里关于security 的流程就都已经结束了.

蓝牙speaker配对流程源码分析的更多相关文章

  1. Flume-NG启动过程源码分析(二)(原创)

    在上一节中讲解了——Flume-NG启动过程源码分析(一)(原创)  本节分析配置文件的解析,即PollingPropertiesFileConfigurationProvider.FileWatch ...

  2. spark源码阅读--shuffle读过程源码分析

    shuffle读过程源码分析 上一篇中,我们分析了shuffle在map阶段的写过程.简单回顾一下,主要是将ShuffleMapTask计算的结果数据在内存中按照分区和key进行排序,过程中由于内存限 ...

  3. [Android]从Launcher开始启动App流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5017056.html 从Launcher开始启动App流程源码 ...

  4. [Android]Android系统启动流程源码分析

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5013863.html Android系统启动流程源码分析 首先 ...

  5. Android系统默认Home应用程序(Launcher)的启动过程源码分析

    在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还须要有一个Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应 ...

  6. Android Content Provider的启动过程源码分析

    本文參考Android应用程序组件Content Provider的启动过程源码分析http://blog.csdn.net/luoshengyang/article/details/6963418和 ...

  7. Android应用程序绑定服务(bindService)的过程源码分析

    Android应用程序组件Service与Activity一样,既能够在新的进程中启动,也能够在应用程序进程内部启动:前面我们已经分析了在新的进程中启动Service的过程,本文将要介绍在应用程序内部 ...

  8. Spring加载流程源码分析03【refresh】

      前面两篇文章分析了super(this)和setConfigLocations(configLocations)的源代码,本文来分析下refresh的源码, Spring加载流程源码分析01[su ...

  9. 【高速接口-RapidIO】5、Xilinx RapidIO核例子工程源码分析

    提示:本文的所有图片如果不清晰,请在浏览器的新建标签中打开或保存到本地打开 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:Vivado2015.4.2 ...

随机推荐

  1. 你不可不知的Java引用类型之——虚引用

    定义 虚引用是使用PhantomReference创建的引用,虚引用也称为幽灵引用或者幻影引用,是所有引用类型中最弱的一个.一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也无法通过虚引用获 ...

  2. mysql之外键

    本文内容: 什么是外键 外键的增加 外键的修改和删除 外键的约束模式 首发日期:2018-04-12 什么是外键: 外键就是表中存在一个字段指向另外一个表的主键,那么这个字段就可以称为外键. 一张表可 ...

  3. Unity网页游戏

    Unity网页游戏是跑在浏览器的UnityWebPlayer插件中的,运行的模式是webplayer.unity3d+html 在嵌入UnityWebPlayer的网页中会调用UnityObject2 ...

  4. 老K漫谈区块链的共识(3)——分布式系统和区块链共识

    1. 啥是分布式系统 当我们评价一个新的事物或者介绍一个新的技术的时候,我们不能架空历史和环境,新的事物不可能脱离历史和环境凭空诞生.任何新的事物和新的技术总是或多或少的,与旧的事件以及过去的技术有所 ...

  5. Android 闪烁动画

    import android.view.View; import android.view.animation.AlphaAnimation; import android.view.animatio ...

  6. 安装Window 10系统------计算机经验

    为什么这次安装window10系统呢?不是和window7系统的安装方法一样么?如果你是这样的想的话,是不完全对的,因为window10系统的安装有些繁杂,需要耐心.下面我就准备了官方原版的windo ...

  7. MacOS远程Windows提示:远程桌面连接无法验证您希望连接的计算机的身份

    解决方法: 1.在Windows端,运行输入 “gpedit.msc”,打开本地组策略编辑器 2.依次打开[计算机配置]→[管理模板]→[windows组件]→[远程桌面服务]→[远程桌面会话主机]→ ...

  8. 15个实用的PHP正则表达式

    对于开发人员来说,正则表达式是一个非常有用的功能,它提供了 查找,匹配,替换 句子,单词,或者其他格式的字符串.这篇文章主要介绍了15个超实用的php正则表达式,需要的朋友可以参考下.在这篇文章里,我 ...

  9. ASP.NET -- WebForm -- 缓存Cache的使用

    ASP.NET -- WebForm --  缓存Cache的使用 把数据从数据库或文件中读取出来,放在内存中,后面的用户直接从内存中取数据,速度快.适用于经常被查询.但不经常变动的数据. 1. Te ...

  10. Python中进程线程协程小结

    进程与线程的概念 进程 程序仅仅只是一堆代码而已,而进程指的是程序的运行过程.需要强调的是:同一个程序执行两次,那也是两个进程. 进程:资源管理单位(容器). 线程:最小执行单位,管理线程的是进程. ...