SDP服务搜索流程源码分析

BREDR的设备 在进行配对完成之后,进行;连接之前都要进行服务的搜索,服务搜索走的流程是SDP,这篇文章就分析一下,bluedroid中SDP的代码流程,我们从配对完成的回调函数开始分析:

/*******************************************************************************
**
** 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;
...
    if (p_auth_cmpl->success)
    {
        btif_update_remote_properties(p_auth_cmpl->bd_addr,
                                      p_auth_cmpl->bd_name, NULL, p_auth_cmpl->dev_type);
        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)
        {
           ...
        }
        else
        {
            /* Trigger SDP on the device */
            pairing_cb.sdp_attempts = 1;;
...
            if(btif_dm_inquiry_in_progress)
                btif_dm_cancel_discovery();

            btif_dm_get_remote_services(&bd_addr);//进行服务搜索
        }
        // Do not call bond_state_changed_cb yet. Wait until remote service discovery is complete
    }
...

 我这里分析的设备是音箱,会直接走SDP的流程。也就是会执行上面的btif_dm_get_remote_services函数,这里注意一点就是,什么时候上报配对的状态?从上面的注释可以看到,bluedroid 并不会在配对完成就上报配对的状态,而是要等服务搜索完成。我们继续分析,服务搜索的流程:

/*******************************************************************************
**
** Function         btif_dm_get_remote_services
**
** Description      Start SDP to get remote services
**
** Returns          bt_status_t
**
*******************************************************************************/
bt_status_t btif_dm_get_remote_services(bt_bdaddr_t *remote_addr)
{
    bdstr_t bdstr;

    BTA_DmDiscover(remote_addr->address, BTA_ALL_SERVICE_MASK,
                   bte_dm_search_services_evt, TRUE);

    return BT_STATUS_SUCCESS;
}

调用到BTA的BTA_DmDiscover,将搜索的 指令开始往下发,其第一个参数是设备的地址,第二个参数是BTA_ALL_SERVICE_MASK = 0x3fffffff,第三个参数是回调,这样的回调我们见的太多,它的命名格式也是大致相同:bte_XXX_evt,

最后一个参数代表是否进行SDP 搜索,这里是true,这个函数实现在bta_dm_api.c里面,这里的函数,十有八九都是组建一个msg,然后调用bta_sys_sendmsg 把消息发送到BTU task 来处理,我们继续分析:

/*******************************************************************************
**
** Function         BTA_DmDiscover
**
** Description      This function does service discovery for services of a
**                  peer device
**
**
** Returns          void
**
*******************************************************************************/
void BTA_DmDiscover(BD_ADDR bd_addr, tBTA_SERVICE_MASK services,
                    tBTA_DM_SEARCH_CBACK *p_cback, BOOLEAN sdp_search)
{
    tBTA_DM_API_DISCOVER    *p_msg;

    if ((p_msg = (tBTA_DM_API_DISCOVER *) GKI_getbuf(sizeof(tBTA_DM_API_DISCOVER))) != NULL)
    {
        memset(p_msg, 0, sizeof(tBTA_DM_API_DISCOVER));

        p_msg->hdr.event = BTA_DM_API_DISCOVER_EVT;//discover msg
        bdcpy(p_msg->bd_addr, bd_addr);
        p_msg->services = services;
        p_msg->p_cback = p_cback;
        p_msg->sdp_search = sdp_search;
        bta_sys_sendmsg(p_msg);
    }
}

这里向BTU task 发送BTA_DM_API_DISCOVER_EVT :(BTA got event 0x202)

这里device manager search (BTA_ID_DM_SEARCH)注册到系统的处理句柄 是:

static const tBTA_SYS_REG bta_dm_search_reg =
{
    bta_dm_search_sm_execute,
    bta_dm_search_sm_disable
};

我们继续看:

/*******************************************************************************
**
** Function         bta_dm_search_sm_execute
**
** Description      State machine event handling function for DM
**
**
** Returns          void
**
*******************************************************************************/
BOOLEAN bta_dm_search_sm_execute(BT_HDR *p_msg)
{
    tBTA_DM_ST_TBL      state_table;
    UINT8               action;
    int                 i;

    APPL_TRACE_EVENT("bta_dm_search_sm_execute state:%d, event:0x%x",
        bta_dm_search_cb.state, p_msg->event);

    /* look up the state table for the current state */
    state_table = bta_dm_search_st_tbl[bta_dm_search_cb.state];

    bta_dm_search_cb.state = state_table[p_msg->event & 0x00ff][BTA_DM_SEARCH_NEXT_STATE];


    /* execute action functions */
    for (i = 0; i < BTA_DM_SEARCH_ACTIONS; i++)
    {
        if ((action = state_table[p_msg->event & 0x00ff][i]) != BTA_DM_SEARCH_IGNORE)
        {
            (*bta_dm_search_action[action])( (tBTA_DM_MSG*) p_msg);
        }
        else
        {
            break;
        }
    }
    return TRUE;
}

状态机轮转,一看也是熟悉的套路。我们看看状态的转换吧,一开始肯定是idle 的状态:bta_dm_search_idle_st_table:

/* API_SEARCH_DISC */       {BTA_DM_API_DISCOVER,              BTA_DM_SEARCH_IGNORE,          BTA_DM_DISCOVER_ACTIVE},

下一个状态是BTA_DM_DISCOVER_ACTIVE,执行的动作是BTA_DM_API_DISCOVER,执行的函数:

/*******************************************************************************
**
** Function         bta_dm_discover
**
** Description      Discovers services on a remote device
**
**
** Returns          void
**
*******************************************************************************/
void bta_dm_discover (tBTA_DM_MSG *p_data)
{
    /* save the search condition */
    bta_dm_search_cb.services = p_data->discover.services;

    bta_dm_search_cb.p_search_cback = p_data->discover.p_cback;//前面注册callbak = bte_dm_search_services_evt
    bta_dm_search_cb.sdp_search = p_data->discover.sdp_search;//true
    bta_dm_search_cb.services_to_search = bta_dm_search_cb.services;//0x3fffffff
    bta_dm_search_cb.service_index = 0;
    bta_dm_search_cb.services_found = 0;
    bta_dm_search_cb.peer_name[0] = 0;
    bta_dm_search_cb.sdp_search = p_data->discover.sdp_search;//bluedroid 竟然执行了两遍
    bta_dm_search_cb.p_btm_inq_info = BTM_InqDbRead (p_data->discover.bd_addr);//查找数据库
    bta_dm_search_cb.transport = p_data->discover.transport;

    bta_dm_search_cb.name_discover_done = FALSE;//标志 名字搜索没有完成
    memcpy(&bta_dm_search_cb.uuid, &p_data->discover.uuid, sizeof(tSDP_UUID));
    bta_dm_discover_device(p_data->discover.bd_addr);//开始搜索
}

 下面我们接着分析bta_dm_discover_device:

/*******************************************************************************
**
** Function         bta_dm_discover_device
**
** Description      Starts name and service discovery on the device
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_discover_device(BD_ADDR remote_bd_addr)
{
    tBTA_DM_MSG * p_msg;
    tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;

    /* Reset transport state for next discovery */
    bta_dm_search_cb.transport = BTA_TRANSPORT_UNKNOWN;//这里为啥这样设置?看上面注释,为了下一次的搜索。这一次的搜索因为知道了transport

    bdcpy(bta_dm_search_cb.peer_bdaddr, remote_bd_addr);
...

    /* if name discovery is not done and application needs remote name */
    if ((!bta_dm_search_cb.name_discover_done)
       && (( bta_dm_search_cb.p_btm_inq_info == NULL )
            ||(bta_dm_search_cb.p_btm_inq_info && (!bta_dm_search_cb.p_btm_inq_info->appl_knows_rem_name))))
    {
        if (bta_dm_read_remote_device_name(bta_dm_search_cb.peer_bdaddr, transport) == TRUE)//如果需要discovery name ,那么将继续进行
            return;

        /* starting name discovery failed */
        bta_dm_search_cb.name_discover_done = TRUE;
    }
    APPL_TRACE_DEBUG("bta_dm_search_cb.services %d libs_liu", bta_dm_search_cb.services);

    /* if application wants to discover service */
    if ( bta_dm_search_cb.services )//0x3fffffff
    {
        /* initialize variables */
        bta_dm_search_cb.service_index      = 0;
        bta_dm_search_cb.services_found     = 0;
        bta_dm_search_cb.services_to_search = bta_dm_search_cb.services;
...
        /* if seaching with EIR is not completed */
        if(bta_dm_search_cb.services_to_search)
        {
            /* check whether connection already exists to the device
               if connection exists, we don't have to wait for ACL
               link to go down to start search on next device */
            if (BTM_IsAclConnectionUp(bta_dm_search_cb.peer_bdaddr, BT_TRANSPORT_BR_EDR))
                bta_dm_search_cb.wait_disc = FALSE;
            else
                bta_dm_search_cb.wait_disc = TRUE;
...
            {
                bta_dm_search_cb.sdp_results = FALSE;
                bta_dm_find_services(bta_dm_search_cb.peer_bdaddr);//继续向下传达搜索指令
                return;
            }
        }
    }

    /* name discovery and service discovery are done for this device */
   ...
}

我们直接看bta_dm_find_services:这里是真正的搜索的逻辑:

/*******************************************************************************
**
** Function         bta_dm_find_services
**
** Description      Starts discovery on a device
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_find_services ( BD_ADDR bd_addr)
{

    tSDP_UUID    uuid;
    UINT16       num_attrs = 1;
    tBTA_DM_MSG *p_msg;

    memset (&uuid, 0, sizeof(tSDP_UUID));

    while(bta_dm_search_cb.service_index < BTA_MAX_SERVICE_ID)//32,涉及很多的sevice,gatt、source、sink、hsp、hfp、map等
    {
        if( bta_dm_search_cb.services_to_search
            & (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index)))//搜索所有的服务
        {
            if((bta_dm_search_cb.p_sdp_db = (tSDP_DISCOVERY_DB *)GKI_getbuf(BTA_DM_SDP_DB_SIZE)) != NULL)
            {
                /* try to search all services by search based on L2CAP UUID */
                if(bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK )
                {
                    if (bta_dm_search_cb.services_to_search & BTA_RES_SERVICE_MASK)/*reserved,id = 1,这里发现了其处理和其他的service不一样*/
                    {
                        uuid.uu.uuid16 = bta_service_id_to_uuid_lkup_tbl[0];//这个reserved 服务就是UUID_SERVCLASS_PNP_INFORMATION
                        bta_dm_search_cb.services_to_search &= ~BTA_RES_SERVICE_MASK;//消除掉这个标志位,下次就会进入到else 流程
                    }
                    else
                    {
                        uuid.uu.uuid16 = UUID_PROTOCOL_L2CAP;//搜索所有基于l2cap的服务
                        bta_dm_search_cb.services_to_search = 0;//注意这里设置为0,
                    }
                }
                else
                {
...
                }

                if (uuid.len == 0)
                    uuid.len = LEN_UUID_16;

                if (bta_dm_search_cb.service_index == BTA_USER_SERVICE_ID)
                {
                    memcpy(&uuid, &bta_dm_search_cb.uuid, sizeof(tSDP_UUID));
                }

                SDP_InitDiscoveryDb (bta_dm_search_cb.p_sdp_db, BTA_DM_SDP_DB_SIZE, 1, &uuid, 0, NULL);//对SDP的数据库进行初始化

                memset(g_disc_raw_data_buf, 0, sizeof(g_disc_raw_data_buf));
                bta_dm_search_cb.p_sdp_db->raw_data = g_disc_raw_data_buf;

                bta_dm_search_cb.p_sdp_db->raw_size = MAX_DISC_RAW_DATA_BUF;

                if (!SDP_ServiceSearchAttributeRequest (bd_addr, bta_dm_search_cb.p_sdp_db, &bta_dm_sdp_callback))//调用到sdp_api.c里面的接口
                {
                    /* if discovery not successful with this device
                    proceed to next one */
                    GKI_freebuf(bta_dm_search_cb.p_sdp_db);
                    bta_dm_search_cb.p_sdp_db = NULL;
                    bta_dm_search_cb.service_index = BTA_MAX_SERVICE_ID;

                }
                else
                {
                    bta_dm_search_cb.service_index++;
                    return;//直接返回,这里猜想,前一次搜索结果回来之后,应该还是会触发下一次继续进行搜索
                }
            }
            else
            {
                APPL_TRACE_ERROR("#### Failed to allocate SDP DB buffer! ####");
            }
        }

        bta_dm_search_cb.service_index++;
    }

    /* no more services to be discovered *///全部搜索完成会发送BTA_DM_DISCOVERY_RESULT_EVT
    if(bta_dm_search_cb.service_index >= BTA_MAX_SERVICE_ID)
    {
        if ((p_msg = (tBTA_DM_MSG *) GKI_getbuf(sizeof(tBTA_DM_MSG))) != NULL)
        {
            p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT;
            p_msg->disc_result.result.disc_res.services = bta_dm_search_cb.services_found;
            bdcpy (p_msg->disc_result.result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr);
            BCM_STRNCPY_S((char*)p_msg->disc_result.result.disc_res.bd_name, sizeof(BD_NAME),
                    bta_dm_get_remname(), (BD_NAME_LEN-1));

            /* make sure the string is terminated */
            p_msg->disc_result.result.disc_res.bd_name[BD_NAME_LEN-1] = 0;

            bta_sys_sendmsg(p_msg);
        }
    }
}

从上面的流程,我们可以知道,它是首先进行UUID_SERVCLASS_PNP_INFORMATION的搜索,然后再进行所有基于L2cap的服务的搜索,同时将bta_dm_search_cb.services_to_search设置为0,当所有的服务搜索完成之后,会上报BTA_DM_DISCOVERY_RESULT_EVT 进行处理。那大概的流程,我们心理已经基本清楚,我们再继续详细分析其流程:

我们首先分析SDP_ServiceSearchAttributeRequest的代码流程:

/*******************************************************************************
**
** Function         SDP_ServiceSearchAttributeRequest
**
** Description      This function queries an SDP server for information.
**
**                  The difference between this API function and the function
**                  SDP_ServiceSearchRequest is that this one does a
**                  combined ServiceSearchAttributeRequest SDP function.
**                  (This is for Unplug Testing)
**
** Returns          TRUE if discovery started, FALSE if failed.
**
*******************************************************************************/
BOOLEAN SDP_ServiceSearchAttributeRequest (UINT8 *p_bd_addr, tSDP_DISCOVERY_DB *p_db,
                                           tSDP_DISC_CMPL_CB *p_cb)
{
#if SDP_CLIENT_ENABLED == TRUE
    tCONN_CB     *p_ccb;

    /* Specific BD address */
    p_ccb = sdp_conn_originate (p_bd_addr);//建立sdp 的channel

    if (!p_ccb)
        return(FALSE);

    p_ccb->disc_state = SDP_DISC_WAIT_CONN;
    p_ccb->p_db       = p_db;
    p_ccb->p_cb       = p_cb;

    p_ccb->is_attr_search = TRUE;

    return(TRUE);
#else
    return(FALSE);
#endif
}

这里发现,每次SDP_ServiceSearchAttributeRequest 的时候都会执行sdp_conn_originate,那我们也可以预期,但是这一次的SDP的搜索行为结束(收到response),应该就会把这个channel 断开,从log 中也确实是如此的。

下面我们继续看看sdp_conn_originate的实现:

/*******************************************************************************
**
** Function         sdp_conn_originate
**
** Description      This function is called from the API to originate a
**                  connection.
**
** Returns          void
**
*******************************************************************************/
tCONN_CB* sdp_conn_originate (UINT8 *p_bd_addr)
{
    tCONN_CB              *p_ccb;
    UINT16                cid;

    /* Allocate a new CCB. Return if none available. */
    if ((p_ccb = sdpu_allocate_ccb()) == NULL)//在sdp_cb.ccb中分配新的channel cb,sdp_cb里面保存了最大ccb是4
    {
        SDP_TRACE_WARNING ("SDP - no spare CCB for orig");
        return (NULL);
    }

    /* We are the originator of this connection */
    p_ccb->con_flags |= SDP_FLAGS_IS_ORIG;

    /* Save the BD Address and Channel ID. */
    memcpy (&p_ccb->device_address[0], p_bd_addr, sizeof (BD_ADDR));

    /* Transition to the next appropriate state, waiting for connection confirm. */
    p_ccb->con_state = SDP_STATE_CONN_SETUP;

    cid = L2CA_ConnectReq (SDP_PSM, p_bd_addr);//为这次transaction 建立l2cap 连接

    /* Check if L2CAP started the connection process */
    if (cid != 0)
    {
        p_ccb->connection_id = cid;

        return (p_ccb);
    }
    else
    {
        SDP_TRACE_WARNING ("SDP - Originate failed");
        sdpu_release_ccb (p_ccb);
        return (NULL);
    }
}

其实也很简单,主要就是先分配相应的l2cap的channel,为了下面的SDP流程做好准备。

从hci 的log中 可以看到,l2cap channel 建立起来之后,还会进行对于l2cap 传输参数的配置,log 截图如下:

要分析这个过程,我们需要先 看一下sdp的 注册到l2cap中的情况,实现是在sdp_init 中,这是在整个bluedroid 初始化的时候就会执行,下面我们分析一下整个函数:

/*******************************************************************************
**
** Function         sdp_init
**
** Description      This function initializes the SDP unit.
**
** Returns          void
**
*******************************************************************************/
void sdp_init (void)
{
    /* Clears all structures and local SDP database (if Server is enabled) */
    memset (&sdp_cb, 0, sizeof (tSDP_CB));

    /* Initialize the L2CAP configuration. We only care about MTU and flush */
    sdp_cb.l2cap_my_cfg.mtu_present       = TRUE;
    sdp_cb.l2cap_my_cfg.mtu               = SDP_MTU_SIZE;//672
    sdp_cb.l2cap_my_cfg.flush_to_present  = TRUE;
    sdp_cb.l2cap_my_cfg.flush_to          = SDP_FLUSH_TO;

    sdp_cb.max_attr_list_size             = SDP_MTU_SIZE - 16;
    sdp_cb.max_recs_per_search            = SDP_MAX_DISC_SERVER_RECS;
...
#if SDP_CLIENT_ENABLED == TRUE
    /* Register with Security Manager for the specific security level */
    if (!BTM_SetSecurityLevel (TRUE, SDP_SERVICE_NAME, BTM_SEC_SERVICE_SDP_SERVER,
                               SDP_SECURITY_LEVEL, SDP_PSM, 0, 0))
    {
        SDP_TRACE_ERROR ("Security Registration for Client failed");
        return;
    }
#endif

#if defined(SDP_INITIAL_TRACE_LEVEL)
    sdp_cb.trace_level = SDP_INITIAL_TRACE_LEVEL;//设置trace level
#else
    sdp_cb.trace_level = BT_TRACE_LEVEL_NONE;    /* No traces */
#endif

    sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind;//接受到对端发过来的连接消息
    sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm;//接受到对端的连接反馈
    sdp_cb.reg_info.pL2CA_ConnectPnd_Cb = NULL;
    sdp_cb.reg_info.pL2CA_ConfigInd_Cb  = sdp_config_ind;//收到对端的配置信息
    sdp_cb.reg_info.pL2CA_ConfigCfm_Cb  = sdp_config_cfm;//收到对端的配置反馈
    sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind;
    sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm;
    sdp_cb.reg_info.pL2CA_QoSViolationInd_Cb = NULL;
    sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind;
    sdp_cb.reg_info.pL2CA_CongestionStatus_Cb = NULL;
    sdp_cb.reg_info.pL2CA_TxComplete_Cb       = NULL;

    /* Now, register with L2CAP */
    if (!L2CA_Register (SDP_PSM, &sdp_cb.reg_info))//注册到来l2cap
    {
        SDP_TRACE_ERROR ("SDP Registration failed");
    }

}

这里的注册和GATT的注册的思路也是一样的。

当对端 回复我们 connect 的消息的时候,执行sdp_connect_cfm,我们看看其具体的实现:

/*******************************************************************************
**
** Function         sdp_connect_cfm
**
** Description      This function handles the connect confirm events
**                  from L2CAP. This is the case when we are acting as a
**                  client and have sent a connect request.
**
** Returns          void
**
*******************************************************************************/
static void sdp_connect_cfm (UINT16 l2cap_cid, UINT16 result)
{
    tCONN_CB    *p_ccb;
    tL2CAP_CFG_INFO cfg;

    /* Find CCB based on CID */
    if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
    {
        SDP_TRACE_WARNING ("SDP - Rcvd conn cnf for unknown CID 0x%x", l2cap_cid);
        return;
    }

    /* If the connection response contains success status, then */
    /* Transition to the next state and startup the timer.      */
    if ((result == L2CAP_CONN_OK) && (p_ccb->con_state == SDP_STATE_CONN_SETUP))
    {
        p_ccb->con_state = SDP_STATE_CFG_SETUP;

        cfg = sdp_cb.l2cap_my_cfg;

        if (cfg.fcr_present)
        {
/*打印*/
        }

        if ((!L2CA_ConfigReq (l2cap_cid, &cfg)) && cfg.fcr_present
             && cfg.fcr.mode != L2CAP_FCR_BASIC_MODE)//开始配置
        {
            /* FCR not desired; try again in basic mode */
            cfg.fcr_present = FALSE;
            cfg.fcr.mode = L2CAP_FCR_BASIC_MODE;
            L2CA_ConfigReq (l2cap_cid, &cfg);
        }

        SDP_TRACE_EVENT ("SDP - got conn cnf, sent cfg req, CID: 0x%x", p_ccb->connection_id);
    }
    else
    {
        ...
     }
        sdpu_release_ccb (p_ccb);
    }
}

从上面的代码,我们看到其从Code: Connection Request到Code: Configure Request流程的转换,我们继续看sdp_config_cfm的实现,预计这里应该会转到真正做服务搜索的动作。前面我们在分析SDP_ServiceSearchAttributeRequest的时候是没有看见其做搜索的动作,只是在挑选一些参数,然后做l2cap的连接。

/*******************************************************************************
**
** Function         sdp_config_cfm
**
** Description      This function processes the L2CAP configuration confirmation
**                  event.
**
** Returns          void
**
*******************************************************************************/
static void sdp_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
{
    tCONN_CB    *p_ccb;

    /* Find CCB based on CID */
    if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
    {
        SDP_TRACE_WARNING ("SDP - Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
        return;
    }

    /* For now, always accept configuration from the other side */
    if (p_cfg->result == L2CAP_CFG_OK)
    {
        p_ccb->con_flags |= SDP_FLAGS_MY_CFG_DONE;

        if (p_ccb->con_flags & SDP_FLAGS_HIS_CFG_DONE)
        {
            p_ccb->con_state = SDP_STATE_CONNECTED;

            if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
                sdp_disc_connected (p_ccb);//这里开始引向搜索
            else
                /* Start inactivity timer */
                btu_start_timer (&p_ccb->timer_entry, BTU_TTYPE_SDP, SDP_INACT_TIMEOUT);//如果我们不是org,那么过timeout 时间会断开
        }
    }
    else
    {
        /* If peer has rejected FCR and suggested basic then try basic */
        if (p_cfg->fcr_present)//如果失败会retry
        {
            tL2CAP_CFG_INFO cfg = sdp_cb.l2cap_my_cfg;
            cfg.fcr_present = FALSE;
            L2CA_ConfigReq (l2cap_cid, &cfg);

            /* Remain in configure state */
            return;
        }

#if SDP_CLIENT_ENABLED == TRUE
        sdp_disconnect(p_ccb, SDP_CFG_FAILED);
#endif
    }
}

 上面过程的核心的就是sdp_disc_connected ,

/*******************************************************************************
**
** Function         sdp_disc_connected
**
** Description      This function is called when an SDP discovery attempt is
**                  connected.
**
** Returns          void
**
*******************************************************************************/
void sdp_disc_connected (tCONN_CB *p_ccb)
{
    if (p_ccb->is_attr_search)
    {
        p_ccb->disc_state = SDP_DISC_WAIT_SEARCH_ATTR;

        process_service_search_attr_rsp (p_ccb, NULL);
    }
    else
    {
        /* First step is to get a list of the handles from the server. */
        /* We are not searching for a specific attribute, so we will   */
        /* first search for the service, then get all attributes of it */

        p_ccb->num_handles = 0;
        sdp_snd_service_search_req(p_ccb, 0, NULL);//开始搜索
    }

}

也就是 红框中的过程:

这里发现,当数据传输完毕,还会把这条l2cap channel 解除掉,我们继续分析源码来寻找该过程的实现:

可以猜想,该过程肯定是在 sdp response中引向 channel 断开的流程的。我们先看看sdp response的执行函数:

根据sdp_init的注册信息:

  sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind;

我们一下子就能想到 执行的是 sdp_data_ind,在sdp_init 中,我们没有发现其实是处理client 端的信息还是server端的信息,这种情况说明肯定会在这个函数中细分,根据不同的情况,路由到不同的函数去处理:

/*******************************************************************************
**
** Function         sdp_data_ind
**
** Description      This function is called when data is received from L2CAP.
**                  if we are the originator of the connection, we are the SDP
**                  client, and the received message is queued up for the client.
**
**                  If we are the destination of the connection, we are the SDP
**                  server, so the message is passed to the server processing
**                  function.
**
** Returns          void
**
*******************************************************************************/
static void sdp_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
{
    tCONN_CB    *p_ccb;

    /* Find CCB based on CID */
    if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) != NULL)
    {
        if (p_ccb->con_state == SDP_STATE_CONNECTED)
        {
            if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG)
                sdp_disc_server_rsp (p_ccb, p_msg);//本地是client
            else
                sdp_server_handle_client_req (p_ccb, p_msg);
        }
        else
        {
            SDP_TRACE_WARNING ("SDP - Ignored L2CAP data while in state: %d, CID: 0x%x",
                                p_ccb->con_state, l2cap_cid);
        }
    }
    else
    {
        SDP_TRACE_WARNING ("SDP - Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid);
    }

    GKI_freebuf (p_msg);
}

我们继续看看sdp_disc_server_rsp的实现:这里面主要做的就是 对于服务器发过来的信息的保存和处理,具体就不分析了:

/*******************************************************************************
**
** Function         process_service_search_attr_rsp
**
** Description      This function is called when there is a search attribute
**                  response from the server.
**
** Returns          void
**
*******************************************************************************/
static void process_service_search_attr_rsp (tCONN_CB *p_ccb, UINT8 *p_reply)
{
    UINT8           *p, *p_start, *p_end, *p_param_len;
    UINT8           type;
    UINT32          seq_len;
    UINT16          param_len, lists_byte_count = 0;
    BOOLEAN         cont_request_needed = FALSE;
...
    /* Since we got everything we need, disconnect the call */
    sdp_disconnect (p_ccb, SDP_SUCCESS);//全部处理完成,断开这个ccb连接
}

 分析到这里,其实我们只分析了搜索service的一个回合,sdp 的搜索往往有很多回合的,下面我们分析一下,本次搜索介绍是如何触发下一个回合的搜索的。这里我们可以自己先思考一下,第一次搜索的时候,我们搜索的reserved 的服务,并且在bta_dm_search_cb中把已经搜索过的标志清掉了:bta_dm_search_cb.services_to_search &= ~BTA_RES_SERVICE_MASK

那我们猜想,下一次搜索前肯定还是会判断bta_dm_search_cb.services_to_search来check 服务搜索是否完成,

下面我们继续分析,看看对端给本端发了sdp disconnect 的response 之后,本端是如何处理的:

/*******************************************************************************
**
** Function         sdp_disconnect_cfm
**
** Description      This function handles a disconnect confirm event from L2CAP.
**
** Returns          void
**
*******************************************************************************/
static void sdp_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
{
    tCONN_CB    *p_ccb;
    UNUSED(result);

    /* Find CCB based on CID */
    if ((p_ccb = sdpu_find_ccb_by_cid (l2cap_cid)) == NULL)
    {
        SDP_TRACE_WARNING ("SDP - Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid);
        return;
    }

    /* Tell the user if he has a callback */
    if (p_ccb->p_cb)
        (*p_ccb->p_cb) (p_ccb->disconnect_reason);//调用callback=bta_dm_sdp_callback
    else if (p_ccb->p_cb2)
        (*p_ccb->p_cb2) (p_ccb->disconnect_reason, p_ccb->user_data);

    sdpu_release_ccb (p_ccb);//这里才真正去释放ccb
}

我们继续看bta_dm_sdp_callback回调:

/*******************************************************************************
**
** Function         bta_dm_sdp_callback
**
** Description      Callback from sdp with discovery status
**
** Returns          void
**
*******************************************************************************/
static void bta_dm_sdp_callback (UINT16 sdp_status)
{

    tBTA_DM_SDP_RESULT * p_msg;

    if ((p_msg = (tBTA_DM_SDP_RESULT *) GKI_getbuf(sizeof(tBTA_DM_SDP_RESULT))) != NULL)
    {
        p_msg->hdr.event = BTA_DM_SDP_RESULT_EVT;//0x205
        p_msg->sdp_result = sdp_status;
        bta_sys_sendmsg(p_msg);

    }
}

我们看看这个时间的处理:

BOOLEAN bta_dm_search_sm_execute(BT_HDR *p_msg)
{
    tBTA_DM_ST_TBL      state_table;
    UINT8               action;
    int                 i;
    /* look up the state table for the current state */
    state_table = bta_dm_search_st_tbl[bta_dm_search_cb.state];

    bta_dm_search_cb.state = state_table[p_msg->event & 0x00ff][BTA_DM_SEARCH_NEXT_STATE];

    /* execute action functions */
    for (i = 0; i < BTA_DM_SEARCH_ACTIONS; i++)
    {
        if ((action = state_table[p_msg->event & 0x00ff][i]) != BTA_DM_SEARCH_IGNORE)
        {
            (*bta_dm_search_action[action])( (tBTA_DM_MSG*) p_msg);
        }
    }
    return TRUE;
}

看看其状态表:

/* SDP_RESULT_EVT */        {BTA_DM_SDP_RESULT,                BTA_DM_SEARCH_IGNORE,          BTA_DM_DISCOVER_ACTIVE},

下一个状态还是BTA_DM_DISCOVER_ACTIVE,执行的行为是BTA_DM_SDP_RESULT:我们看看其实现,因为代码量非常的大,这里删除了一些无关的代码:

    void bta_dm_sdp_result (tBTA_DM_MSG *p_data)
    {
    
        tSDP_DISC_REC    *p_sdp_rec = NULL;
        tBTA_DM_MSG     *p_msg;
        BOOLEAN          scn_found = FALSE;
        UINT16             service = 0xFFFF;
        tSDP_PROTOCOL_ELEM    pe;
    
        UINT32 num_uuids = 0;
        UINT8  uuid_list[32][MAX_UUID_SIZE]; // assuming a max of 32 services
    
        if((p_data->sdp_event.sdp_result == SDP_SUCCESS)
            || (p_data->sdp_event.sdp_result == SDP_NO_RECS_MATCH)
            || (p_data->sdp_event.sdp_result == SDP_DB_FULL))//处理结果
    {
    do
    {
        p_sdp_rec = NULL;
        if( bta_dm_search_cb.service_index == BTA_USER_SERVICE_ID+1) 
        {
...
        }
        else
        {
        service = bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index-1];//转换成相应的UUID
        p_sdp_rec = SDP_FindServiceInDb(bta_dm_search_cb.p_sdp_db, service, p_sdp_rec);//讲搜索到的service 都保存在p_sdp_rec中
                }
                
                {
                    /* SDP_DB_FULL means some records with the
                       required attributes were received */
        if (((p_data->sdp_event.sdp_result == SDP_DB_FULL) &&
        bta_dm_search_cb.services != BTA_ALL_SERVICE_MASK) ||
                            (p_sdp_rec != NULL))
            {
        if (service != UUID_SERVCLASS_PNP_INFORMATION)//说明是基于l2cap的搜索
                {
            UINT16 tmp_svc = 0xFFFF;
            bta_dm_search_cb.services_found |=    (tBTA_SERVICE_MASK)(BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index-1));
            tmp_svc = bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index-1];
                            /* Add to the list of UUIDs */
                            sdpu_uuid16_to_uuid128(tmp_svc, uuid_list[num_uuids]);//保存在uuid_list 里面
                            num_uuids++;
                        }
                    }
                }
    
                if(bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK &&
                    bta_dm_search_cb.services_to_search == 0)
                {
                        bta_dm_search_cb.service_index++;
                }
                else /* regular one service per search or PNP search */
                    break;
                }
    while(bta_dm_search_cb.service_index <= BTA_MAX_SERVICE_ID);
    
    //          GKI_freebuf(bta_dm_search_cb.p_sdp_db);
    //          bta_dm_search_cb.p_sdp_db = NULL;
    
            /* Collect the 128-bit services here and put them into the list */
            if(bta_dm_search_cb.services == BTA_ALL_SERVICE_MASK)
            {
                p_sdp_rec = NULL;
                do
                {
                    tBT_UUID temp_uuid;
                    /* find a service record, report it */
                    p_sdp_rec = SDP_FindServiceInDb_128bit(bta_dm_search_cb.p_sdp_db, p_sdp_rec);
                    if (p_sdp_rec)
                    {
                        if (SDP_FindServiceUUIDInRec_128bit(p_sdp_rec, &temp_uuid))
                        {
                            memcpy(uuid_list[num_uuids], temp_uuid.uu.uuid128, MAX_UUID_SIZE);
                            num_uuids++;
                        }
                    }
                } while (p_sdp_rec);
            }
            /* if there are more services to search for */
            if(bta_dm_search_cb.services_to_search)//如果只是搜索了reserved的服务,那么继续搜索
            {
                /* Free up the p_sdp_db before checking the next one */
                bta_dm_free_sdp_db(NULL);
                bta_dm_find_services(bta_dm_search_cb.peer_bdaddr);
            }
            else//否则发送BTA_DM_DISCOVERY_RESULT_EVT 消息
            {
                /* callbacks */
                /* start next bd_addr if necessary */
    BTM_SecDeleteRmtNameNotifyCallback(&bta_dm_service_search_remname_cback);
    
    
                if ((p_msg = (tBTA_DM_MSG *) GKI_getbuf(sizeof(tBTA_DM_MSG))) != NULL)
                {
                    p_msg->hdr.event = BTA_DM_DISCOVERY_RESULT_EVT;
                    p_msg->disc_result.result.disc_res.result = BTA_SUCCESS;
                    p_msg->disc_result.result.disc_res.p_raw_data = NULL;
                    p_msg->disc_result.result.disc_res.raw_data_size = 0;
                    p_msg->disc_result.result.disc_res.num_uuids = num_uuids;
                    p_msg->disc_result.result.disc_res.p_uuid_list = NULL;
                    if (num_uuids > 0) {
                        p_msg->disc_result.result.disc_res.p_uuid_list = (UINT8*)GKI_getbuf(num_uuids*MAX_UUID_SIZE);
                        if (p_msg->disc_result.result.disc_res.p_uuid_list) {
                            memcpy(p_msg->disc_result.result.disc_res.p_uuid_list, uuid_list,
                                   num_uuids*MAX_UUID_SIZE);
                        }
                        ...
                    }
                    //copy the raw_data to the discovery result  structure
                    //
    
                    if (  bta_dm_search_cb.p_sdp_db != NULL && bta_dm_search_cb.p_sdp_db->raw_used != 0   &&
                        bta_dm_search_cb.p_sdp_db->raw_data != NULL) {
    
                        p_msg->disc_result.result.disc_res.p_raw_data = GKI_getbuf(bta_dm_search_cb.p_sdp_db->raw_used);
                        if ( NULL != p_msg->disc_result.result.disc_res.p_raw_data    ) {
                            memcpy(     p_msg->disc_result.result.disc_res.p_raw_data,
                                        bta_dm_search_cb.p_sdp_db->raw_data,
                                        bta_dm_search_cb.p_sdp_db->raw_used );
    
                            p_msg->disc_result.result.disc_res.raw_data_size =
                                bta_dm_search_cb.p_sdp_db->raw_used;
    
                        }
                        ...
                        bta_dm_search_cb.p_sdp_db->raw_data = NULL;     //no need to free this - it is a global assigned.
                        bta_dm_search_cb.p_sdp_db->raw_used = 0;
                        bta_dm_search_cb.p_sdp_db->raw_size = 0;
                    }

                    /* Done with p_sdp_db. Free it */
                    bta_dm_free_sdp_db(NULL);
                    p_msg->disc_result.result.disc_res.services = bta_dm_search_cb.services_found;
                    ...
                    bdcpy (p_msg->disc_result.result.disc_res.bd_addr, bta_dm_search_cb.peer_bdaddr);
                    BCM_STRNCPY_S((char*)p_msg->disc_result.result.disc_res.bd_name, sizeof(BD_NAME),
                            bta_dm_get_remname(), (BD_NAME_LEN-1));
                    /* make sure the string is null terminated */
                    p_msg->disc_result.result.disc_res.bd_name[BD_NAME_LEN-1] = 0;
                    bta_sys_sendmsg(p_msg);
                }
            }
        }
    }

上面的代码 实现看起来 比较乱,其实逻辑其实也不复杂。主要做的事情如下:

  1. 判断是否是UUID_SERVCLASS_PNP_INFORMATION,如果是,继续进行搜索
  2. 否则 说明已经完成了 基于L2CAP的service的搜索,那么进行数据保存,解析,以及上报,上报调用的时间是BTA_DM_DISCOVERY_RESULT_EVT

我们下面继续分析BTA_DM_DISCOVERY_RESULT_EVT 的流程:

BTA got event 0x207
/* DISCV_RES_EVT */         {BTA_DM_DISC_RESULT,               BTA_DM_SEARCH_IGNORE,          BTA_DM_DISCOVER_ACTIVE},

发现下一个状态依然是BTA_DM_DISCOVER_ACTIVE,执行的动作是BTA_DM_DISC_RESULT,我们继续看这个函数的实现:

/*******************************************************************************
**
** Function         bta_dm_disc_result
**
** Description      Service discovery result when discovering services on a device
**
** Returns          void
**
*******************************************************************************/
void bta_dm_disc_result (tBTA_DM_MSG *p_data)
{
    APPL_TRACE_EVENT("%s", __func__);
    bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT, &p_data->disc_result.result);//调用回调上报BTA_DM_DISC_RES_EVT

    tBTA_DM_MSG *p_msg = (tBTA_DM_MSG *) GKI_getbuf(sizeof(tBTA_DM_MSG));

    /* send a message to change state */
    if (p_msg != NULL)
    {
        p_msg->hdr.event = BTA_DM_SEARCH_CMPL_EVT;//再次发送event
        p_msg->hdr.layer_specific = BTA_DM_API_DISCOVER_EVT;
        bta_sys_sendmsg(p_msg);
    }
}

上面的代码也是分为两个部分:

  1. bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT, &p_data->disc_result.result); 进行上报数据和状态到 device manager
  2.  继续进行状态机的轮转,因为此刻的状态依然是BTA_DM_DISCOVER_ACTIVE,按照预期,最后应该是idle的状态。

我们先看:

bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT, &p_data->disc_result.result);

 这个回调,我们也很熟悉,每次搜索都会有这个回调,执行的函数是bte_dm_search_services_evt,其实这个函数也是另外的函数的封装,

/*******************************************************************************
**
** Function         bte_dm_search_services_evt
**
** Description      Switches context from BTE to BTIF for DM search services
**                  event
**
** Returns          void
**
*******************************************************************************/
static void bte_dm_search_services_evt(tBTA_DM_SEARCH_EVT event, tBTA_DM_SEARCH *p_data)
{
    UINT16 param_len = 0;
   if (p_data)
       param_len += sizeof(tBTA_DM_SEARCH);
   switch (event)
   {
         case BTA_DM_DISC_RES_EVT:
         {
             if ((p_data->disc_res.result == BTA_SUCCESS) && (p_data->disc_res.num_uuids > 0)) {
                  param_len += (p_data->disc_res.num_uuids * MAX_UUID_SIZE);
             }
         } break;
   }
   /* TODO: The only other member that needs a deep copy is the p_raw_data. But not sure
    * if raw_data is needed. */
   btif_transfer_context(btif_dm_search_services_evt, event, (char*)p_data, param_len,
         (param_len > sizeof(tBTA_DM_SEARCH)) ? search_services_copy_cb : NULL);
}

 我们继续看btif_dm_search_services_evt的实现,

/*******************************************************************************
**
** Function         btif_dm_search_services_evt
**
** Description      Executes search services event in btif context
**
** Returns          void
**
*******************************************************************************/
static void btif_dm_search_services_evt(UINT16 event, char *p_param)
{
    tBTA_DM_SEARCH *p_data = (tBTA_DM_SEARCH*)p_param;

    switch (event)
    {
        case BTA_DM_DISC_RES_EVT:
        {
            bt_property_t prop;
            uint32_t i = 0;
            bt_bdaddr_t bd_addr;
            bt_status_t ret;

            bdcpy(bd_addr.address, p_data->disc_res.bd_addr);

            if  ((p_data->disc_res.result != BTA_SUCCESS) &&
                 (pairing_cb.state == BT_BOND_STATE_BONDING ) &&
                 (pairing_cb.sdp_attempts < BTIF_DM_MAX_SDP_ATTEMPTS_AFTER_PAIRING))//sdp的最大的retry的次数是BTIF_DM_MAX_SDP_ATTEMPTS_AFTER_PAIRING = 2
            {
                BTIF_TRACE_WARNING("%s:SDP failed after bonding re-attempting", __FUNCTION__);
                pairing_cb.sdp_attempts++;
                btif_dm_get_remote_services(&bd_addr);//失败之后,重新进行服务搜索
                return;
            }
            prop.type = BT_PROPERTY_UUIDS;
            prop.len = 0;
            if ((p_data->disc_res.result == BTA_SUCCESS) && (p_data->disc_res.num_uuids > 0))
            {
                 prop.val = p_data->disc_res.p_uuid_list;
                 prop.len = p_data->disc_res.num_uuids * MAX_UUID_SIZE;
                 for (i=0; i < p_data->disc_res.num_uuids; i++)
                 {
                      char temp[256];
                      uuid_to_string_legacy((bt_uuid_t*)(p_data->disc_res.p_uuid_list + (i*MAX_UUID_SIZE)), temp);
                      LOG_INFO("%s index:%d uuid:%s", __func__, i, temp);
                 }
            }

            /* onUuidChanged requires getBondedDevices to be populated.
            ** bond_state_changed needs to be sent prior to remote_device_property
            */
            if ((pairing_cb.state == BT_BOND_STATE_BONDING) &&
                ((bdcmp(p_data->disc_res.bd_addr, pairing_cb.bd_addr) == 0) ||
                 (bdcmp(p_data->disc_res.bd_addr, pairing_cb.static_bdaddr.address) == 0)) &&
                  pairing_cb.sdp_attempts > 0)
            {
                 BTIF_TRACE_DEBUG("%s Remote Service SDP done. Call bond_state_changed_cb BONDED",
                                   __FUNCTION__);
                 pairing_cb.sdp_attempts  = 0;//复原次变量,sdp流程完成

                 // If bonding occured due to cross-key pairing, send bonding callback
                 // for static address now
                 if (bdcmp(p_data->disc_res.bd_addr, pairing_cb.static_bdaddr.address) == 0)
                    bond_state_changed(BT_STATUS_SUCCESS, &bd_addr, BT_BOND_STATE_BONDING);

                 bond_state_changed(BT_STATUS_SUCCESS, &bd_addr, BT_BOND_STATE_BONDED);//sdp完成之后 才上传绑定完成的状态
            }

            if (p_data->disc_res.num_uuids != 0)
            {
                /* Also write this to the NVRAM */
                ret = btif_storage_set_remote_device_property(&bd_addr, &prop);//保存到文件
                /* Send the event to the BTIF */
                HAL_CBACK(bt_hal_cbacks, remote_device_properties_cb,
                                 BT_STATUS_SUCCESS, &bd_addr, 1, &prop);//调用remote_device_properties_cb 汇报到framework
            }
        }

上面的流程,非常简单,就是上报绑定完成的状态,以及汇报service discovery的结果,其实分析到这里,我们只是分析一个BTA_DM_DISC_RES_EVT,很容易想到,后续肯定还会分析discovery 完成事件:BTA_DM_DISC_CMPL_EVT

,下面我们继续分析第二点:

 继续进行状态机的轮转,发送BTA_DM_SEARCH_CMPL_EVT

 直接看状态表:

/* SEARCH_CMPL_EVT */       {BTA_DM_SEARCH_CMPL,               BTA_DM_SEARCH_IGNORE,          BTA_DM_SEARCH_IDLE},

我们终于看到了 状态最后轮转到idle 状态了:BTA_DM_SEARCH_IDLE,执行的action是BTA_DM_SEARCH_CMPL,执行的函数是:bta_dm_search_cmpl

/*******************************************************************************
**
** Function         bta_dm_search_cmpl
**
** Description      Sends event to application
**
** Returns          void
**
*******************************************************************************/
void bta_dm_search_cmpl (tBTA_DM_MSG *p_data)
{
    APPL_TRACE_EVENT("%s", __func__);

    if (p_data->hdr.layer_specific == BTA_DM_API_DI_DISCOVER_EVT)
        bta_dm_di_disc_cmpl(p_data);
    else
        bta_dm_search_cb.p_search_cback(BTA_DM_DISC_CMPL_EVT, NULL);//还是调用这个函数,上报BTA_DM_DISC_CMPL_EVT
}

而对于BTA_DM_DISC_CMPL_EVT 的处理,btif_dm_search_services_evt函数并没有去实现,不过这里也不需要做什么,搜索的数据和状态都已经上报给framework了,

那到这里,sdp 的代码流程就分析完了。


猜你喜欢

转载自www.cnblogs.com/libs-liu/p/9399517.html