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 並不會在配對完成就上報配對的狀態,而是要等服務搜索完成。咱們繼續分析,服務搜索的流程:api
/******************************************************************************* ** ** 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 來處理,咱們繼續分析:app
/******************************************************************************* ** ** 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)ide
這裏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 };
咱們繼續看:ui
/******************************************************************************* ** ** 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:this
/* API_SEARCH_DISC */ {BTA_DM_API_DISCOVER, BTA_DM_SEARCH_IGNORE, BTA_DM_DISCOVER_ACTIVE},
下一個狀態是BTA_DM_DISCOVER_ACTIVE,執行的動做是BTA_DM_API_DISCOVER,執行的函數:spa
/******************************************************************************* ** ** 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:code
/******************************************************************************* ** ** 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); } } } }
上面的代碼 實現看起來 比較亂,其實邏輯其實也不復雜。主要作的事情以下:
咱們下面繼續分析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); } }
上面的代碼也是分爲兩個部分:
咱們先看:
這個回調,咱們也很熟悉,每次搜索都會有這個回調,執行的函數是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
,下面咱們繼續分析第二點:
直接看狀態表:
/* 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 的代碼流程就分析完了。