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); } } } }
上面的代码 实现看起来 比较乱,其实逻辑其实也不复杂。主要做的事情如下:
- 判断是否是UUID_SERVCLASS_PNP_INFORMATION,如果是,继续进行搜索
- 否则 说明已经完成了 基于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); } }
上面的代码也是分为两个部分:
- bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT, &p_data->disc_result.result); 进行上报数据和状态到 device manager
- 继续进行状态机的轮转,因为此刻的状态依然是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 的代码流程就分析完了。