蓝牙speaker配对流程源码分析
Posted libs-liu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了蓝牙speaker配对流程源码分析相关的知识,希望对你有一定的参考价值。
这篇文章简单分析一下 蓝牙音箱配对流程.现在的音箱基本都支持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) == 0) #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, 0, NULL, 0 );//0 else status = BTM_SecBondByTransport ( p_data->bond.bd_addr, p_data->bond.transport, 0, NULL, 0 ); 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[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); /* 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 != 0)) //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[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) && (p_dev_rec->dev_class[2] & 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 = 0; 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[0], p_features[1], p_features[2], p_features[3]); BTM_TRACE_EVENT(" %02x-%02x-%02x-%02x", p_features[4], p_features[5], p_features[6], p_features[7]); } 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) == 0) { /* 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[0], remote_bda[1], remote_bda[2], remote_bda[3], remote_bda[4], remote_bda[5]); /* 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, 0); } 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 = 0; for(xx = 0;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 -= (1 + 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[0], bda[1], bda[2], bda[3], bda[4], bda[5]); } ... /* 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)==0)) || 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] = 0; rem_name.status = BTM_SUCCESS; temp_evt_len = rem_name.length; while (temp_evt_len > 0) { *p_n1++ = *bdn++; temp_evt_len--; } rem_name.remote_bd_name[rem_name.length] = 0;//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 = 0; rem_name.remote_bd_name[0] = 0; } /* Reset the remote BAD to zero and call callback if possible */ memset(p_inq->remname_bda, 0, 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[0] = 0; } 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 = 0;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) == 0) ) { ... 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) == 0) { 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) == 0) { 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[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); 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[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); 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 = 0; btm_acl_resubmit_page(); if (!p_dev_rec) { ... } else /* Update the timestamp for this device */ { bit_shift = (handle == p_dev_rec->ble_hci_handle) ? 8 :0; 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) == 0) && (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) == 0) ) { /* 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; }
简单说一下上面函数的要点:
- Allocate acl_db entry :p_acl_cb = btm_bda_to_acl(bda, 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
- btm_read_remote_features (p->hci_handle);//Command: HCI_Read_Remote_Supported_Features
- btm_sec_execute_procedure (p_dev_rec)
- RNR 已经做过.
- Start authentication 即将要做
- Start encryption 后续要做
- 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[0], bda[1], bda[2], bda[3], bda[4], bda[5]); BTM_SetLinkPolicy(p->remote_addr, &btm_cb.btm_def_link_policy); return; } /* Allocate acl_db entry */ for (xx = 0, p = &btm_cb.acl_db[0]; 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 != 0) 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[1], p_dev_rec->trusted_mask[0]); 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", 1); /* 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); } }
上面函数做的主要的事情是:
- 设置link state:p_lcb->link_state = LST_CONNECTED;
- Code: Information request l2cap
- Command: HCI_Write_Link_Policy_Settings
- Command: HCI_Write_Link_Supervision_Timeout
- 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[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); if( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) && (btm_cb.collision_start_time != 0) && (memcmp (btm_cb.p_collided_dev_rec->bd_addr, p_bda, BD_ADDR_LEN) == 0) ) { ... /*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[0]; 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 = 0; 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; } ... }
上面做了两件事:
- save remote feature in btm_cb.acl_db[acl_idx].peer_lmp_features //临时
- 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 + 1)); /* Continue with HCI connection establishment */ btm_establish_continue (p_acl_cb); }
上面做的事情:
- store remote extend feature in btm_cb.acl_db[acl_idx].peer_lmp_features
- btm_process_remote_ext_features
- store remote feature in p_dev_rec->features
- p_dev_rec->sm4 = BTM_SM4_TRUE
- btm_establish_continue
- Command: HCI_Change_Connection_Packet_Type
- Command: HCI_Write_Link_Policy_Settings
- update remote feature to upper layer:btm_cb.p_bl_changed_cb
- 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 = 0; 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. */ ... }
上面做了两件事:
- store remote feature in p_dev_rec->features
- 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 = 0; 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); } }
上面过程 主要就是
- 组建本端的IO capabilities
- change btm_cb.pairing_state to BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS
- 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;
- 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); }
上面函数做的事情:
- 保存remote IO
- p_dev_rec->rmt_io_caps = evt_data.io_cap;
- p_dev_rec->rmt_auth_req = evt_data.auth_req;
- 通知upper layer: p_sp_callback : bta_dm_sp_cback ,保存IO
- pairing_cb.auth_req = auth_req;
- 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[0]<<24) + (p_bda[1]<<16) + (p_bda[2]<<8) + p_bda[3], (p_bda[4] << 8) + p_bda[5], 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) == 0) ) { 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 */ } ... } }
上面函数做的事情:
- 设置btm_cb.pairing_state的状态为BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM
- evt_data.cfm_req.just_works = TRUE;
- 将本端和对端的IO以及authentication req信息写进evt_data.cfm_req
- 调用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[0] == 0) { ... } 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-1)); sec_event.key_notif.bd_name[BD_NAME_LEN-1] = 0; } } 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; }
这里做的事情:
- 构建 sec_event.cfm_req 结构,保存本端和对端的IO以及authentication req
- 调用 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, 0);// Command: HCI_User_Confirmation_Request_Reply return; ... } cod = devclass2uint(p_ssp_cfm_req->dev_class); if (cod == 0) { LOG_DEBUG("%s cod is 0, set as unclassified", __func__); cod = COD_UNCLASSIFIED; } pairing_cb.sdp_attempts = 0; /* 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); }
上面函数主要做了如下的事情:
- btif_update_remote_properties //看起来没什么需要更新 的.
- bond_state_changed ,因为之前已经更新过BT_BOND_STATE_BONDING,所以也不会真正的更新.
- 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;
- btif_dm_ssp_reply(&bd_addr, BT_SSP_VARIANT_CONSENT, TRUE, 0);// Command: HCI_User_Confirmation_Request_Reply
- 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); ... }
上面函数做的事情:
- 组建evt_data 结构
- evt_data.status = BTM_SUCCESS; (it depends)
- evt_data.bd_addr
- evt_data.dev_class
- (*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 )
- 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[0]<<8)+p_bda[1], (p_bda[2]<<24)+(p_bda[3]<<16)+(p_bda[4]<<8)+p_bda[5], 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 = 16; 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) == 0) ) { 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); } } }
这个函数做的事情:
- p_dev_rec->link_key_type = key_type = 4
- p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN;//0x8a | 0x10 = 0x9a
- p_dev_rec->enc_key_size = 16;
- memcpy (p_dev_rec->link_key, p_link_key, LINK_KEY_LEN);
- 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, 0, 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-1)); p_auth_cmpl->bd_name[BD_NAME_LEN-1] = 0; 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; }
上面函数做的事情:
- 组建sec_event.auth_cmpl 结构.并赋值
- p_auth_cmpl->bd_addr
- p_auth_cmpl->bd_name
- p_auth_cmpl->key_present
- p_auth_cmpl->key_type
- p_auth_cmpl->success
- p_auth_cmpl->key //store link key
- 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 = 0; 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, 1, &prop); } else { /* Trigger SDP on the device */ pairing_cb.sdp_attempts = 1;; /* 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) != 0)) { 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 ... }
上面流程主要做的事情:
-
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
-
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,
- 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 = 0; 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) == 0) ) 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) == 0) ) 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; } ... }
上面函数做的事情主要是:
- btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE);//change state to BTM_PAIR_STATE_IDLE -->btm_cb.pairing_state
-
(*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; 从BTM_SEC_STATE_AUTHENTICATING(1)-->BTM_SEC_STATE_IDLE(0)
- 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, 0, 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); }
这里做的事情:
- BTM_SetOutService(p_ccb->peer_addr, BTM_SEC_SERVICE_AVDTP, AVDT_CHAN_SIG);
- btm_cb.p_out_serv = p_serv_rec;
- 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)) != 0)//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, 0); } } }
这里的重点就是发起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", 1); 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[0]<<24)+(p_bd_addr[1]<<16)+(p_bd_addr[2]<<8)+p_bd_addr[3], (p_bd_addr[4]<<8)+p_bd_addr[5], p_ertm_info, (p_ertm_info) ? p_ertm_info->allowed_modes : 0, (p_ertm_info) ? p_ertm_info->preferred_mode : 0); /* 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 (0); } /* 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, 0)) == NULL) { L2CAP_TRACE_WARNING ("L2CAP - no CCB for L2CA_conn_req, PSM: 0x%04x", psm); return (0); } /* 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, 0, 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) != 0) { 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); }
这里做的事情:
- security_required = p_serv_rec->security_flags;// 0x3092 update in :btm_sec_set_security_level 首先获取初始化(经过设置)的security level
- p_dev_rec->p_cur_service = p_serv_rec; //Save pointer to service record 与security device关联.
- security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT);//the security_required = 0x3092 | 0x20 = 0x30b2 //重新规划 security_required
- p_dev_rec->security_required = security_required;//update p_dev_rec->security_required
- p_dev_rec->p_callback = p_callback;//store callback = l2c_link_sec_comp
- 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[1], p_dev_rec->trusted_mask[0]); 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 : 0, encr_enable); BTM_TRACE_DEBUG ("before update p_dev_rec->sec_flags=0x%x", (p_dev_rec) ? p_dev_rec->sec_flags : 0 ); ... 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 >= 16 || 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 = 16; } 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); }
上面函数要点:
- p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED); //更新 sec_flag
- p_dev_rec->sec_state = BTM_SEC_STATE_IDLE;//set p_dev_rec->sec_state
- status = (UINT8)btm_sec_execute_procedure (p_dev_rec);
- 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配对流程源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段