蓝牙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;
}

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

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

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

/*******************************************************************************
**
** Function         btm_acl_created
**
** Description      This function is called by L2CAP when an ACL connection
**                  is created.
**
** Returns          void
**
*******************************************************************************/
void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, BD_NAME bdn,
                      UINT16 hci_handle, UINT8 link_role, tBT_TRANSPORT transport)
{
    tBTM_SEC_DEV_REC *p_dev_rec = NULL;
    tACL_CONN        *p;
    UINT8             xx;

    BTM_TRACE_DEBUG ("btm_acl_created hci_handle=%d link_role=%d  transport=%d",
                      hci_handle,link_role, transport);
    /* Ensure we don't have duplicates */
    p = btm_bda_to_acl(bda, transport); //find btm_cb.acl_db ,if found then return .
    if (p != (tACL_CONN *)NULL)
    {
        p->hci_handle = hci_handle;
        p->link_role  = link_role;
#if BLE_INCLUDED == TRUE
        p->transport = transport;
#endif
        BTM_TRACE_DEBUG ("Duplicate btm_acl_created: RemBdAddr: %02x%02x%02x%02x%02x%02x",
                          bda[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);
    }
}

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

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

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

btu_hcif.c

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

    STREAM_TO_BDADDR (bda, p);
    btm_sec_link_key_request (bda);
}

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

/*******************************************************************************
**
** Function         btm_sec_link_key_request
**
** Description      This function is called when controller requests link key
**
** Returns          Pointer to the record or NULL
**
*******************************************************************************/
void btm_sec_link_key_request (UINT8 *p_bda)
{
    tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda);

    BTM_TRACE_EVENT ("btm_sec_link_key_request()  BDA: %02x:%02x:%02x:%02x:%02x:%02x",
                      p_bda[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;
    }

...
}

上面做了两件事:

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

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

btu_hcif.c

/*******************************************************************************
**
** Function         btu_hcif_read_rmt_ext_features_comp_evt
**
** Description      Process event HCI_READ_RMT_EXT_FEATURES_COMP_EVT
**
** Returns          void
**
*******************************************************************************/
static void btu_hcif_read_rmt_ext_features_comp_evt (UINT8 *p)
{
    UINT8 *p_cur = p;
    UINT8 status;
    UINT16 handle;

    STREAM_TO_UINT8 (status, p_cur);

    if (status == HCI_SUCCESS)
        btm_read_remote_ext_features_complete(p);
    else
    {
        STREAM_TO_UINT16 (handle, p_cur);
        btm_read_remote_ext_features_failed(status, handle);
    }
}

btm_acl.c:

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

上面做的事情:

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

btm_acl.c

  
    /*******************************************************************************
    **
    ** Function         btm_process_remote_ext_features
    **
    ** Description      Local function called to process all extended features pages
    **                  read from a remote device.
    **
    ** Returns          void
    **
    *******************************************************************************/
    void btm_process_remote_ext_features (tACL_CONN *p_acl_cb, UINT8 num_read_pages)
    {
        UINT16              handle = p_acl_cb->hci_handle;
        tBTM_SEC_DEV_REC    *p_dev_rec = btm_find_dev_by_handle (handle);
        UINT8               page_idx;
    
        BTM_TRACE_DEBUG ("btm_process_remote_ext_features");
    
        /* Make sure we have the record to save remote features information */
        if (p_dev_rec == NULL)
        {
            /* Get a new device; might be doing dedicated bonding */
            p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr);
        }
    
        p_acl_cb->num_read_pages = num_read_pages;
        p_dev_rec->num_read_pages = num_read_pages;
    
        /* Move the pages to placeholder */
        for (page_idx = 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.
            */
    
...
    }

上面做了两件事:

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

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

Event: HCI IO Capability Request 的处理:

btu_hcif.c

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

btm_sec.c

    
    /*******************************************************************************
    **
    ** Function         btm_io_capabilities_req
    **
    ** Description      This function is called when LM request for the IO
    **                  capability of the local device and
    **                  if the OOB data is present for the device in the event
    **
    ** Returns          void
    **
    *******************************************************************************/
    void btm_io_capabilities_req (UINT8 *p)
    {
        tBTM_SP_IO_REQ  evt_data;
        UINT8           err_code = 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);
        }
    }

上面过程 主要就是

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

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

btu_hcif.c

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

btm_sec.c

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

上面函数做的事情:

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

 接下来就走到了

Event: HCI User Confirmation Request 的流程.

btu_hcif.c

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

 btm_sec.c

    
    /*******************************************************************************
    **
    ** Function         btm_proc_sp_req_evt
    **
    ** Description      This function is called to process/report
    **                  HCI_USER_CONFIRMATION_REQUEST_EVT
    **                  or HCI_USER_PASSKEY_REQUEST_EVT
    **                  or HCI_USER_PASSKEY_NOTIFY_EVT
    **
    ** Returns          void
    **
    *******************************************************************************/
    void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p)
    {
        tBTM_STATUS status = BTM_ERR_PROCESSING;
        tBTM_SP_EVT_DATA evt_data;
        UINT8               *p_bda = evt_data.cfm_req.bd_addr;
        tBTM_SEC_DEV_REC *p_dev_rec;
    
        /* All events start with bd_addr */
        STREAM_TO_BDADDR (p_bda, p);
    
        BTM_TRACE_EVENT ("btm_proc_sp_req_evt() BDA: %08x%04x event: 0x%x, State: %s",
                          (p_bda[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 */
            }
...   

    }
        }

上面函数做的事情:

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

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

bta_dm_sp_cback




/*******************************************************************************
**
** Function         bta_dm_sp_cback
**
** Description      simple pairing callback from BTM
**
** Returns          void
**
*******************************************************************************/
static UINT8 bta_dm_sp_cback (tBTM_SP_EVT event, tBTM_SP_EVT_DATA *p_data)
{
    tBTM_STATUS status = BTM_CMD_STARTED;
    tBTA_DM_SEC sec_event;
    tBTA_DM_SEC_EVT pin_evt = BTA_DM_SP_KEY_NOTIF_EVT;

    APPL_TRACE_EVENT("bta_dm_sp_cback: %d", event);
    if (!bta_dm_cb.p_sec_cback){
        APPL_TRACE_EVENT("bta_dm_sp_cback:libs_liu BTM_NOT_AUTHORIZED");
        return BTM_NOT_AUTHORIZED;
        }

    /* TODO_SP */
    switch(event)
    {
    ...
    case BTM_SP_CFM_REQ_EVT:
        pin_evt = BTA_DM_SP_CFM_REQ_EVT;
        bta_dm_cb.just_works = sec_event.cfm_req.just_works = p_data->cfm_req.just_works;
        sec_event.cfm_req.loc_auth_req = p_data->cfm_req.loc_auth_req;
        sec_event.cfm_req.rmt_auth_req = p_data->cfm_req.rmt_auth_req;
        sec_event.cfm_req.loc_io_caps = p_data->cfm_req.loc_io_caps;
        sec_event.cfm_req.rmt_io_caps = p_data->cfm_req.rmt_io_caps;

        /* continue to next case */
    /* Passkey entry mode, mobile device with output capability is very
        unlikely to receive key request, so skip this event */
    /*case BTM_SP_KEY_REQ_EVT: */
    case BTM_SP_KEY_NOTIF_EVT:
        if(BTM_SP_CFM_REQ_EVT == event)
        {
          /* Due to the switch case falling through below to BTM_SP_KEY_NOTIF_EVT,
             call remote name request using values from cfm_req */
          if(p_data->cfm_req.bd_name[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;
}

这里做的事情:

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

下面看看回调做的事情:

 bte_dm_evt  -->btif_dm_upstreams_evt -->btif_dm_ssp_cfm_req_evt

btif_dm.c

/*******************************************************************************
**
** Function         btif_dm_ssp_cfm_req_evt
**
** Description      Executes SSP confirm request event in btif context
**
** Returns          void
**
*******************************************************************************/
static void btif_dm_ssp_cfm_req_evt(tBTA_DM_SP_CFM_REQ *p_ssp_cfm_req)
{
    bt_bdaddr_t bd_addr;
    bt_bdname_t bd_name;
    UINT32 cod;
    BOOLEAN is_incoming = !(pairing_cb.state == BT_BOND_STATE_BONDING);
    int dev_type;

    BTIF_TRACE_DEBUG("%s", __FUNCTION__);

    /* Remote properties update */
    if (!btif_get_device_type(p_ssp_cfm_req->bd_addr, &dev_type))
    {
        dev_type = BT_DEVICE_TYPE_BREDR;
    }
    btif_update_remote_properties(p_ssp_cfm_req->bd_addr, p_ssp_cfm_req->bd_name,
                                  p_ssp_cfm_req->dev_class, (tBT_DEVICE_TYPE) dev_type);

    bdcpy(bd_addr.address, p_ssp_cfm_req->bd_addr);
    memcpy(bd_name.name, p_ssp_cfm_req->bd_name, BD_NAME_LEN);

    /* Set the pairing_cb based on the local & remote authentication requirements */
    bond_state_changed(BT_STATUS_SUCCESS, &bd_addr, BT_BOND_STATE_BONDING); /* has been update before .so the state will be updated this time  */

    /* if just_works and bonding bit is not set treat this as temporary */

    if (p_ssp_cfm_req->just_works && !(p_ssp_cfm_req->loc_auth_req & BTM_AUTH_BONDS) &&
        !(p_ssp_cfm_req->rmt_auth_req & BTM_AUTH_BONDS) &&
        !(check_cod((bt_bdaddr_t*)&p_ssp_cfm_req->bd_addr, COD_HID_POINTING)))
        pairing_cb.bond_type = BOND_TYPE_TEMPORARY;
    else
        pairing_cb.bond_type = BOND_TYPE_PERSISTENT;

    btm_set_bond_type_dev(p_ssp_cfm_req->bd_addr, pairing_cb.bond_type);//save bond type :p_dev_rec->bond_type = BOND_TYPE_PERSISTENT

    pairing_cb.is_ssp = TRUE;

    /* If JustWorks auto-accept */
    if (p_ssp_cfm_req->just_works)
    {
        /// Don't show user confirm dialog if just work pairing request.

            BTIF_TRACE_EVENT("%s: User consent needed for incoming pairing request. bond_type: %d, loc_io_caps: %d, rmt_io_caps: %d",
                    __FUNCTION__, pairing_cb.bond_type , p_ssp_cfm_req->loc_io_caps, p_ssp_cfm_req->rmt_io_caps);
            BTIF_TRACE_EVENT("%s: Auto-accept JustWorks pairing", __FUNCTION__);
            btif_dm_ssp_reply(&bd_addr, BT_SSP_VARIANT_CONSENT, TRUE, 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);
}

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

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

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

我们看看

Event: HCI Simple Pairing Complete 的处理:

btu_hcif.c

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

btm_sec.c

/*******************************************************************************
**
** Function         btm_simple_pair_complete
**
** Description      This function is called when simple pairing process is
**                  complete
**
** Returns          void
**
*******************************************************************************/
void btm_simple_pair_complete (UINT8 *p)
{
    tBTM_SP_COMPLT  evt_data;
    tBTM_SEC_DEV_REC *p_dev_rec;
    UINT8           status;
    BOOLEAN         disc = FALSE;

    status = *p++;
    STREAM_TO_BDADDR (evt_data.bd_addr, p);

    if ((p_dev_rec = btm_find_dev (evt_data.bd_addr)) == NULL)
    {
        //error
        return;
    }
    BTM_TRACE_EVENT ("btm_simple_pair_complete()  Pair State: %s  Status:%d  sec_state: %u",
                      btm_pair_state_descr(btm_cb.pairing_state),  status, p_dev_rec->sec_state);

    evt_data.status = BTM_ERR_PROCESSING;
    if (status == HCI_SUCCESS)
    {
        evt_data.status = BTM_SUCCESS;
        p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED;//0x88 | 0x02 = 0x8a
    }
...

    /* Let the pairing state stay active, p_auth_complete_callback will report the failure */
    memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN);
    memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN);

    if (btm_cb.api.p_sp_callback)//bta_dm_sp_cback 
        (*btm_cb.api.p_sp_callback) (BTM_SP_COMPLT_EVT, (tBTM_SP_EVT_DATA *)&evt_data);
...
}

上面函数做的事情:

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

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

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

接下来,我们继续看

Event: Link Key Notification 的处理:

btu_hcif.c

/*******************************************************************************
**
** Function         btu_hcif_link_key_notification_evt
**
** Description      Process event HCI_LINK_KEY_NOTIFICATION_EVT
**
** Returns          void
**
*******************************************************************************/
static void btu_hcif_link_key_notification_evt (UINT8 *p)
{
    BD_ADDR  bda;
    LINK_KEY key;
    UINT8    key_type;

    STREAM_TO_BDADDR (bda, p);
    STREAM_TO_ARRAY16 (key, p);
    STREAM_TO_UINT8 (key_type, p);

    btm_sec_link_key_notification (bda, key, key_type);
}

btm_sec.c

/*******************************************************************************
**
** Function         btm_sec_link_key_notification
**
** Description      This function is called when a new connection link key is
**                  generated
**
** Returns          Pointer to the record or NULL
**
*******************************************************************************/
void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type)
{
    tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda);
    BOOLEAN         we_are_bonding = FALSE;
    BOOLEAN         ltk_derived_lk  = FALSE;

    BTM_TRACE_EVENT ("btm_sec_link_key_notification()  BDA:%04x%08x, TYPE: %d",
                      (p_bda[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);
            }
        }
    
}

这个函数做的事情:

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

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

我们看看bta_security的实现:

bta_dm_act.c

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

}

 下面我们看看bta_dm_new_link_key_cback 的实现:

/*******************************************************************************
**
** Function         bta_dm_new_link_key_cback
**
** Description      Callback from BTM to notify new link key
**
** Returns          void
**
*******************************************************************************/
static UINT8  bta_dm_new_link_key_cback(BD_ADDR bd_addr, DEV_CLASS dev_class,
                                        BD_NAME bd_name, LINK_KEY key, UINT8 key_type)
{
    tBTA_DM_SEC sec_event;
    tBTA_DM_AUTH_CMPL *p_auth_cmpl;
    UINT8             event;
    UNUSED(dev_class);

    memset (&sec_event, 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;
}

上面函数做的事情:

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

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

 bte_dm_evt  -->btif_dm_upstreams_evt -->btif_dm_auth_cmpl_evt 

btif_dm.c

/*******************************************************************************
**
** Function         btif_dm_auth_cmpl_evt
**
** Description      Executes authentication complete event in btif context
**
** Returns          void
**
*******************************************************************************/
static void btif_dm_auth_cmpl_evt (tBTA_DM_AUTH_CMPL *p_auth_cmpl)
{
    /* Save link key, if not temporary */
    bt_bdaddr_t bd_addr;
    bt_status_t status = BT_STATUS_FAIL;
    bt_bond_state_t state = BT_BOND_STATE_NONE;
    BOOLEAN skip_sdp = FALSE;

    BTIF_TRACE_DEBUG("%s: bond state=%d", __func__, pairing_cb.state);

    bdcpy(bd_addr.address, p_auth_cmpl->bd_addr);
    if ( (p_auth_cmpl->success == TRUE) && (p_auth_cmpl->key_present) )
    {
        if ((p_auth_cmpl->key_type < HCI_LKEY_TYPE_DEBUG_COMB) ||
            (p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB) ||
            (p_auth_cmpl->key_type == HCI_LKEY_TYPE_CHANGED_COMB) ||
            (p_auth_cmpl->key_type == HCI_LKEY_TYPE_AUTH_COMB_P_256) ||
            pairing_cb.bond_type == BOND_TYPE_PERSISTENT)
        {
            bt_status_t ret;
            BTIF_TRACE_DEBUG("%s: Storing link key. key_type=0x%x, bond_type=%d",
                __FUNCTION__, p_auth_cmpl->key_type, pairing_cb.bond_type);
            ret = btif_storage_add_bonded_device(&bd_addr,
                                p_auth_cmpl->key, p_auth_cmpl->key_type,
                                pairing_cb.pin_code_len);//stote into  bt_config.conf
            ASSERTC(ret == BT_STATUS_SUCCESS, "storing link key failed", ret);
        }
        else
        {
            BTIF_TRACE_DEBUG("%s: Temporary key. Not storing. key_type=0x%x, bond_type=%d",
                __FUNCTION__, p_auth_cmpl->key_type, pairing_cb.bond_type);
            if(pairing_cb.bond_type == BOND_TYPE_TEMPORARY)
            {
                ...
                return;
            }
        }
    }

    // Skip SDP for certain  HID Devices
    //because HID devices have do SDP before
    if (p_auth_cmpl->success)
    {

        btif_storage_set_remote_addr_type(&bd_addr, p_auth_cmpl->addr_type);
        btif_update_remote_properties(p_auth_cmpl->bd_addr,
                                      p_auth_cmpl->bd_name, NULL, p_auth_cmpl->dev_type);//update to upper layer :HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
        pairing_cb.timeout_retries = 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
    ...
}

上面流程主要做的事情:

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

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

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

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

接下来在看看

Event: Authentication Complete 的处理流程:

btu_hcif.c

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

    STREAM_TO_UINT8  (status, p);
    STREAM_TO_UINT16 (handle, p);

    btm_sec_auth_complete (handle, status);
}

btm_sec.c

/*******************************************************************************
**
** Function         btm_sec_auth_complete
**
** Description      This function is when authentication of the connection is
**                  completed by the LM
**
** Returns          void
**
*******************************************************************************/
void btm_sec_auth_complete (UINT16 handle, UINT8 status)
{
    UINT8            old_sm4;
    tBTM_PAIRING_STATE  old_state   = btm_cb.pairing_state;
    tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle);
    BOOLEAN             are_bonding = FALSE;
...
    btm_cb.collision_start_time = 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;
    }
    ...
}

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

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

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

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

bta_dm_act.c

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

仅仅进行错误处理.

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

 下面来看看encryption的流程.

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

bta_api.h

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

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

btif_av.c

/*******************************************************************************
**
** Function         btif_av_execute_service
**
** Description      Initializes/Shuts down the service
**
** Returns          BT_STATUS_SUCCESS on success, BT_STATUS_FAIL otherwise
**
*******************************************************************************/
bt_status_t btif_av_execute_service(BOOLEAN b_enable)
{
     if (b_enable)
     {
         /* TODO: Removed BTA_SEC_AUTHORIZE since the Java/App does not
          * handle this request in order to allow incoming connections to succeed.
          * We need to put this back once support for this is added */

         /* Added BTA_AV_FEAT_NO_SCO_SSPD - this ensures that the BTA does not
          * auto-suspend av streaming on AG events(SCO or Call). The suspend shall
          * be initiated by the app/audioflinger layers */
#if (AVRC_METADATA_INCLUDED == TRUE)
         BTA_AvEnable(BTA_SEC_AUTHENTICATE,
             BTA_AV_FEAT_RCTG|BTA_AV_FEAT_METADATA|BTA_AV_FEAT_VENDOR|BTA_AV_FEAT_NO_SCO_SSPD
#if (AVRC_ADV_CTRL_INCLUDED == TRUE)
             |BTA_AV_FEAT_RCCT
             |BTA_AV_FEAT_ADV_CTRL
#endif
             ,bte_av_callback);
...
#endif
         BTA_AvRegister(BTA_AV_CHNL_AUDIO, BTIF_AV_SERVICE_NAME, 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);
}

这里做的事情:

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

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

avdt_ad.c  (ad = adapter)

/*******************************************************************************
**
** Function         avdt_ad_open_req
**
** Description      This function is called by a CCB or SCB to open a transport
**                  channel.  This function allocates and initializes a
**                  transport channel table entry.  The channel can be opened
**                  in two roles:  as an initiator or acceptor.  When opened
**                  as an initiator the function will start an L2CAP connection.
**                  When opened as an acceptor the function simply configures
**                  the table entry to listen for an incoming channel.
**
**
** Returns          Nothing.
**
*******************************************************************************/
void avdt_ad_open_req(UINT8 type, tAVDT_CCB *p_ccb, tAVDT_SCB *p_scb, UINT8 role)
{
    tAVDT_TC_TBL    *p_tbl;
    UINT16          lcid;

    if((p_tbl = avdt_ad_tc_tbl_alloc(p_ccb)) == NULL)
    {
        AVDT_TRACE_ERROR("avdt_ad_open_req: Cannot allocate p_tbl");
        return;
    }


    p_tbl->tcid = avdt_ad_type_to_tcid(type, p_scb);
    AVDT_TRACE_DEBUG("avdt_ad_open_req: type: %d, role: %d, tcid:%d",
        type, role, p_tbl->tcid);

    if (type == AVDT_CHAN_SIG)
    {
        /* if signaling, get mtu from registration control block */
        p_tbl->my_mtu = avdt_cb.rcb.ctrl_mtu;
        p_tbl->my_flush_to = L2CAP_DEFAULT_FLUSH_TO;
    }
    else
    ...

    /* if we're acceptor, we're done; just sit back and listen */
    if (role == AVDT_ACP)
    {
        p_tbl->state = AVDT_AD_ST_ACP;
    }
    /* else we're inititator, start the L2CAP connection */
    else
    {
        p_tbl->state = AVDT_AD_ST_CONN;

        /* call l2cap connect req */
        if ((lcid = L2CA_ConnectReq(AVDT_PSM, p_ccb->peer_addr)) != 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);
}

 这里做的事情:

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

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

btm_sec.c

/******************************************************************
** S T A T I C     F U N C T I O N S
*******************************************************************/

/*******************************************************************************
**
** Function         btm_sec_execute_procedure
**
** Description      This function is called to start required security
**                  procedure.  There is a case when multiplexing protocol
**                  calls this function on the originating side, connection to
**                  the peer will not be established.  This function in this
**                  case performs only authorization.
**
** Returns          BTM_SUCCESS     - permission is granted
**                  BTM_CMD_STARTED - in process
**                  BTM_NO_RESOURCES  - permission declined
**
*******************************************************************************/
static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec)
{
    BTM_TRACE_EVENT ("btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d",
                      p_dev_rec->security_required, p_dev_rec->sec_flags, p_dev_rec->sec_state);

...

    /* If any security is required, get the name first */
    if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN)
        && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE))
    {
    ...
    }

    /* If connection is not authenticated and authentication is required */
    /* start authentication and return PENDING to the caller */
        ...
    

    /* If connection is not encrypted and encryption is required */
    /* start encryption and return PENDING to the caller */
    if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)
        && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT))
            || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT)))
        && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE))
    {

        BTM_TRACE_EVENT ("Security Manager: Start encryption");

        if (!btm_sec_start_encryption (p_dev_rec))//p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING;
        {
            return(BTM_NO_RESOURCES);
        }
        return(BTM_CMD_STARTED);
    }
    ...

    /* If connection is not authorized and authorization is required */
    /* start authorization and return PENDING to the caller */
    ...
    /* All required  security procedures already established */
    p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_IN_AUTHORIZE |
                                      BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE |
                                      BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT |
                                      BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER |
                                      BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE);//clear all flag

    BTM_TRACE_EVENT ("Security Manager: trusted:0x%04x%04x", p_dev_rec->trusted_mask[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);
}

上面函数要点:

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

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

/*******************************************************************************
**
** Function         btm_sec_dev_rec_cback_event
**
** Description      This function calls the callback function with the given
**                  result and clear the callback function.
**
** Parameters:      void
**
*******************************************************************************/
void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res, BOOLEAN is_le_transport)
{
    tBTM_SEC_CALLBACK   *p_callback = p_dev_rec->p_callback;//l2c_link_sec_comp  

    if (p_dev_rec->p_callback)
    {
        p_dev_rec->p_callback = NULL;

#if BLE_INCLUDED == TRUE
        if (is_le_transport)
           (*p_callback) (p_dev_rec->ble.pseudo_addr, BT_TRANSPORT_LE, p_dev_rec->p_ref_data, res);
        else
#endif
           (*p_callback) (p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, p_dev_rec->p_ref_data, res);
    }

    btm_sec_check_pending_reqs();
}

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

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

原文地址:https://www.cnblogs.com/libs-liu/p/10175949.html