SDP代码流程

基于Android 14

在配对完成之后需要进行SDP完成服务的搜索然后进行连接的,代码中对应的是处理BTA_DM_AUTH_CMPL_EVT事件,然后到btif_dm_auth_cmpl_evt函数中。

  1. btif_dm_auth_cmpl_evt
    认证完成,绑定消息的上报之前需要完成sdp的搜索
static void btif_dm_auth_cmpl_evt(tBTA_DM_AUTH_CMPL* p_auth_cmpl) {
  ...
  // 如果不是临时的lnk key 则进行保存
  if ((p_auth_cmpl->success) && (p_auth_cmpl->key_present)) {
    /**
    1. 如果key_type类型不是debug且未认证类型,或者bond_type是BOND_TYPE_PERSISTENT
    	如果地址不为空调用btif_storage_add_bonded_device,添加新的设备、link key、
    	key type、pin key length到NVRAM中;否则设置标志位ret为BT_STATUS_FAIL
    2. 不满足条件 1,认定link key为临时的,然后判断bond_type如果为BOND_TYPE_TEMPORARY,
	调用btif_storage_remove_bonded_device从NVRAM中移除绑定设备
    **/
  }

  if (p_auth_cmpl->success) {
    // save remote info to iot conf file
    btif_iot_update_remote_info(p_auth_cmpl, false, pairing_cb.is_ssp);
	// 大段的注释,简单的意思是没有经过配对流程收到新的link key的认证设备不进行SDP。
	// link key从安全LTK派生的情况下需要进行SDP。
	/**
	1. 认证地址和配对地址不同,并且没有收到对端的加密key就跳过SDP
	2. 保存设备类型
	3. is_crosskey的判断,牵扯到ctkd配对模式,如果不是crosskey则btif_update_remote_properties
	4. sdp黑名单, 并且是hid设备跳过sdp
    **/
    if (!pairing_cb.is_local_initiated && skip_sdp) {
      //不是本地发起的配对连接,并且不需要跳过SDP,则断定为hid设备,更新prop并回调
    } else {
      // crosskey,保存静态地址
      if (!is_crosskey ||
          !(stack_config_get_interface()->get_pts_crosskey_sdp_disable())) {
          /**
          1. sdp期间停掉inquiry(cancel_discovery)
		  2. 触发设备的sdp(pairing_cb.sdp_attempts = 1;)
		  3. 如果crossk-key配对时发生绑定,发送地址合并回调,否则就更新绑定状态
          **/
		// sdp_over_classic在前面尚未赋值过,为初始值NOT_STARTED
        if (pairing_cb.sdp_over_classic ==
            btif_dm_pairing_cb_t::ServiceDiscoveryState::NOT_STARTED) {
          LOG_INFO("scheduling SDP for %s", ADDRESS_TO_LOGGABLE_CSTR(bd_addr));
          pairing_cb.sdp_over_classic =
              btif_dm_pairing_cb_t::ServiceDiscoveryState::SCHEDULED;
          // 通过地址和通道来进行设备的SDP
          btif_dm_get_remote_services(bd_addr, BT_TRANSPORT_BR_EDR);
        }
      }
    }
    // Do not call bond_state_changed_cb yet. Wait until remote service
    // discovery is complete
  } else {
  // 认证失败的一些处理
  }
}

内容太多了,使用中文进行了简单描述,另外最后的原文注释中有提到在service discovery完成之后才调用bond_state_changed_cb

  1. btif_dm_get_remote_services
void btif_dm_get_remote_services(RawAddress remote_addr, const int transport) {
  BTIF_TRACE_EVENT("%s: transport=%d, remote_addr=%s", __func__, transport,
                   ADDRESS_TO_LOGGABLE_CSTR(remote_addr));

  BTM_LogHistory(
      kBtmLogTag, remote_addr, "Service discovery",
      base::StringPrintf("transport:%s", bt_transport_text(transport).c_str()));

  BTA_DmDiscover(remote_addr, btif_dm_search_services_evt, transport);
}
  1. BTA_DmDiscover
    到BTA中进行消息发送与处理,相比之前多了个参数,是回调
void BTA_DmDiscover(const RawAddress& bd_addr, tBTA_DM_SEARCH_CBACK* p_cback,
                    tBT_TRANSPORT transport) {
  tBTA_DM_API_DISCOVER* p_msg =
      (tBTA_DM_API_DISCOVER*)osi_calloc(sizeof(tBTA_DM_API_DISCOVER));

  p_msg->hdr.event = BTA_DM_API_DISCOVER_EVT;
  p_msg->bd_addr = bd_addr;
  p_msg->transport = transport;
  p_msg->p_cback = p_cback;

  bta_sys_sendmsg(p_msg);
}
  1. bta_dm_search_sm_execute
    为Device manager处理各种状态机的evt
bool bta_dm_search_sm_execute(BT_HDR_RIGID* p_msg) {
  ...
  tBTA_DM_MSG* message = (tBTA_DM_MSG*)p_msg;
  switch (bta_dm_search_get_state()) {
    case BTA_DM_SEARCH_IDLE:
      switch (p_msg->event) {
        ...
        case BTA_DM_API_DISCOVER_EVT:
          // 先将状态设置为BTA_DM_DISCOVER_ACTIVE,然后处理消息,后面处就会在BTA_DM_DISCOVER_ACTIVE状态处理消息
          bta_dm_search_set_state(BTA_DM_DISCOVER_ACTIVE);
          bta_dm_discover(message);
          break;

扫描完成后会bta_dm_search_set_state为idle,这里初始状态为idle

  1. bta_dm_discover
    结构体里的参数都预置个值方便后面使用
void bta_dm_discover(tBTA_DM_MSG* p_data) {
  /* save the search condition */
  bta_dm_search_cb.services = BTA_ALL_SERVICE_MASK;//0x7FFFFFFF

  bta_dm_gattc_register();

  bta_dm_search_cb.p_search_cback = p_data->discover.p_cback;
  bta_dm_search_cb.services_to_search = bta_dm_search_cb.services;
  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.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;
  bta_dm_discover_device(p_data->discover.bd_addr);
}
  1. bta_dm_discover_device
    涉及EIR相关
static void bta_dm_discover_device(const RawAddress& remote_bd_addr) {
  /**
  1. 设置transport
  2. bta_dm_search_cb保存对端地址
  3. 判断p_btm_inq_info是否有内容,打印内容;p_btm_inq_info是BTM保存在数据库中的inquiry的response信息,EIR相关
  4. ble设备在inquiry之后不进行rnr,(INTEROP_DISABLE_NAME_REQUEST名单匹配)
  5. 知道名字的情况下,跳过rnr
  6. 名字未知且需要查询的时候进行的操作
  7. 为下次discovery重置transport
  **/
  
  /* if application wants to discover service */
  if (bta_dm_search_cb.services) { //根据前面赋值知道0x7FFFFFFF
    /* bta_dm_discover中也有进行过,因为此函数被多次调用,这里再次初始化变量 */
    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;//0x7FFFFFFF

    /* if seaching with EIR is not completed */
    if (bta_dm_search_cb.services_to_search) {
      /* 
      1. ACL链路检查,起了就设置wait_disc为false,否则为true;wait_disc跟下个设备的EIR相关
      2. 判断并打印p_btm_inq_info内容 
	  */

      if (transport == BT_TRANSPORT_LE) {
        if (bta_dm_search_cb.services_to_search & BTA_BLE_SERVICE_MASK) {
          ...
          /* start GATT for service discovery,BLE的处理,这里是BR就跳过,后续再看 */
          btm_dm_start_gatt_discovery(bta_dm_search_cb.peer_bdaddr);
          return;
        }
      } else {
        LOG_INFO("bta_dm_discovery: starting SDP discovery on %s",
                 ADDRESS_TO_LOGGABLE_CSTR(bta_dm_search_cb.peer_bdaddr));
        bta_dm_search_cb.sdp_results = false;
        //终于开始了SDP的内容了
        bta_dm_find_services(bta_dm_search_cb.peer_bdaddr);
        return;
      }
    }
  }
  // 名字和service discovery完成后发送BTA_DM_DISCOVERY_RESULT_EVT
  ...
  bta_sys_sendmsg(p_msg);//BTA_DM_DISCOVERY_RESULT_EVT
}
  1. bta_dm_find_services
    循环的查找service
static void bta_dm_find_services(const RawAddress& bd_addr) {
  while (bta_dm_search_cb.service_index < BTA_MAX_SERVICE_ID) {
    Uuid uuid = Uuid::kEmpty;
    if (bta_dm_search_cb.services_to_search &
        (tBTA_SERVICE_MASK)(
            BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index))) {
      ...
      /* 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) {
          uuid = Uuid::From16Bit(bta_service_id_to_uuid_lkup_tbl[0]);
          // 第一次搜完PNP之后,services_to_search = 0x7FFFFFFE,下此循环不在进行这个
          bta_dm_search_cb.services_to_search &= ~BTA_RES_SERVICE_MASK;
        } else {// services_to_search 置0后,while里的第一个if就进不来了
          uuid = Uuid::From16Bit(UUID_PROTOCOL_L2CAP);
          bta_dm_search_cb.services_to_search = 0;
        }
      } else {
        /* for LE only profile */
        if (bta_dm_search_cb.service_index == BTA_BLE_SERVICE_ID) {
          uuid = Uuid::From16Bit(
              bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index]);

          bta_dm_search_cb.services_to_search &= (tBTA_SERVICE_MASK)(~(
              BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index)));
        } else {
          /* remove the service from services to be searched  */
          bta_dm_search_cb.services_to_search &= (tBTA_SERVICE_MASK)(~(
              BTA_SERVICE_ID_TO_SERVICE_MASK(bta_dm_search_cb.service_index)));
          uuid = Uuid::From16Bit(
              bta_service_id_to_uuid_lkup_tbl[bta_dm_search_cb.service_index]);
        }
      }
      // 初始换discovery数据库
      SDP_InitDiscoveryDb(bta_dm_search_cb.p_sdp_db, BTA_DM_SDP_DB_SIZE, 1,
                          &uuid, 0, NULL);

      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进行失败,service_index设置为最大,进行下个设备的 */
        osi_free_and_reset((void**)&bta_dm_search_cb.p_sdp_db);
        bta_dm_search_cb.service_index = BTA_MAX_SERVICE_ID;
      } else {
        if (uuid == Uuid::From16Bit(UUID_PROTOCOL_L2CAP)) {
        // 通过INTEROP_DISABLE_PCE_SDP_AFTER_PAIRING名单匹配
          if (!is_sdp_pbap_pce_disabled(bd_addr)) {
            LOG_DEBUG("SDP search for PBAP Client ");
            // 针对单个uuid进行查询的接口
            BTA_SdpSearch(bd_addr, Uuid::From16Bit(UUID_SERVCLASS_PBAP_PCE));
          }
        }
        bta_dm_search_cb.service_index++;//只在SDP通道建立成功时
        return;//当前的uuid的跳出,等待对端回应后在执行下次循环
      }
    }
    bta_dm_search_cb.service_index++; // while 循环的,while内的第一个if判断没进
  }

  /* services discovery 完之后,发送BTA_DM_DISCOVERY_RESULT_EVT */
  if (bta_dm_search_cb.service_index >= BTA_MAX_SERVICE_ID) {
    ...
    bta_sys_sendmsg(p_msg);//BTA_DM_DISCOVERY_RESULT_EVT
  }
}

第一次的uuid为UUID_SERVCLASS_PNP_INFORMATION,第二次services_to_search = 0x7FFFFFFE就会进行UUID_PROTOCOL_L2CAP的,但是看逻辑怎么进行除上两个之外的?
两个疑问:

  • L2CAP的搜完之后,bta_dm_search_cb.services_to_search = 0怎么进行下一次?并且uuid没有找到其他的
    使用的是

  • 每次循环完成前, 都会return,该怎么进行循环?
    当前的uuid搜索的时候不会在进行下一次,直到收到对端回应后才会进行下一次的,并且最多只能同时建立4条通道#define SDP_MAX_CONNECTIONS 4

是不是说这里的while循环就没有必要了呢?L2CAP的搜完后,此函数中没有其他的uuid赋值了?循环但是内容执行?

  1. SDP_ServiceSearchAttributeRequest
    查询SDP server的信息,组合了SDP_SERVICE_SEARCH_REQSDP_SERVICE_ATTR_REQ
bool SDP_ServiceSearchAttributeRequest(const RawAddress& p_bd_addr,
                                       tSDP_DISCOVERY_DB* p_db,
                                       tSDP_DISC_CMPL_CB* p_cb) {
  tCONN_CB* p_ccb;

  /* Specific BD address */
  p_ccb = sdp_conn_originate(p_bd_addr); // 建立SDP通道

  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);
}
  1. sdp_conn_originate
    建立通道
tCONN_CB* sdp_conn_originate(const RawAddress& p_bd_addr) {
  tCONN_CB* p_ccb;
  uint16_t cid;

  /* 分配connection control block,一个连接通道一个,最多4个*/
  p_ccb = sdpu_allocate_ccb();
  if (p_ccb == NULL) {
    SDP_TRACE_WARNING("%s: no spare CCB for peer %s", __func__,
                      ADDRESS_TO_LOGGABLE_CSTR(p_bd_addr));
    return (NULL);
  }

  /* We are the originator of this connection */
  p_ccb->con_flags |= SDP_FLAGS_IS_ORIG; //0x1
  /* Save the BD Address and Channel ID. */
  p_ccb->device_address = p_bd_addr;
  // Transition to the next appropriate state, waiting for connection confirm.
  p_ccb->con_state = SDP_STATE_CONN_SETUP;

  // Look for any active sdp connection on the remote device,遍历p_ccb
  cid = sdpu_get_active_ccb_cid(p_bd_addr);
  // sdp_serialization_is_enabled是不是rust定义的,触及到了知识盲区了。。。
  if (!bluetooth::common::init_flags::sdp_serialization_is_enabled() ||
      cid == 0) {//SDP串口没有开启或者没有active通道,创建L2cap连接
    p_ccb->con_state = SDP_STATE_CONN_SETUP;
    cid = L2CA_ConnectReq2(BT_PSM_SDP, p_bd_addr, BTM_SEC_NONE);
  } else {// 已有通道,等待连接
    p_ccb->con_state = SDP_STATE_CONN_PEND;
    SDP_TRACE_WARNING("SDP already active for peer %s. cid=%#0x",
                      ADDRESS_TO_LOGGABLE_CSTR(p_bd_addr), cid);
  }

  /* Check if L2CAP started the connection process,SDP是建立在L2cap的基础上的,协议定义 */
  if (cid == 0) {
    SDP_TRACE_WARNING("%s: SDP - Originate failed for peer %s", __func__,
                      ADDRESS_TO_LOGGABLE_CSTR(p_bd_addr));
    sdpu_release_ccb(*p_ccb);// 释放
    return (NULL);
  }
  p_ccb->connection_id = cid;
  return (p_ccb);
}

中间有一个疑问,bluetooth::common::init_flags::sdp_serialization_is_enabled() 定义在init_flags.rs文件中,是rust吗?
对应的hci如下:

  • 第一组建立一个L2CAP通道
  • 第二组配置通道的MTU
  • 第三组是配置成功的回应
  • 第四组是uuid的request与回应,有的时候会有多组回应,看Bytes for continuation length是否为0
  • 第五组为L2CAP的断开请求与回应
    至于为啥会确定这个是同一组的,可以通过Source CID和Dest. ID判断,另外有的时候会发现同一个L2CAP通道建立之后,会有一组以上的SDP(多个uuid的request)就对应了代码中的sdpu_get_active_ccb_cid
    SDP交互
  1. sdp_init
    蓝牙开启时event_start_up_stack里会调用sdp_init,其中的sdp_cb.reg_info为L2CAP的注册回调
void sdp_init(void) {
  /* Clears all structures and local SDP database (if Server is enabled) */
  memset(&sdp_cb, 0, sizeof(tSDP_CB));

  for (int i = 0; i < SDP_MAX_CONNECTIONS; i++) {	// 解释了前面说的最多4条通道
    sdp_cb.ccb[i].sdp_conn_timer = alarm_new("sdp.sdp_conn_timer");
  }

  /* Initialize the L2CAP configuration. We only care about MTU */
  sdp_cb.l2cap_my_cfg.mtu_present = true;
  sdp_cb.l2cap_my_cfg.mtu = SDP_MTU_SIZE;

  sdp_cb.max_attr_list_size = SDP_MTU_SIZE - 16;
  sdp_cb.max_recs_per_search = SDP_MAX_DISC_SERVER_RECS;

  sdp_cb.trace_level = BT_TRACE_LEVEL_WARNING;

  sdp_cb.reg_info.pL2CA_ConnectInd_Cb = sdp_connect_ind;	//收到对端发来的连接请求 L2CAP_CONNECTION_REQ
  sdp_cb.reg_info.pL2CA_ConnectCfm_Cb = sdp_connect_cfm;	//收到对端的连接回复 L2CAP_CONNECTION_RSP
  sdp_cb.reg_info.pL2CA_ConfigInd_Cb = sdp_config_ind;	//收到对端配置请求 L2CAP_CONFIGRATION_RQE
  sdp_cb.reg_info.pL2CA_ConfigCfm_Cb = sdp_config_cfm;	//收到对端配置回复 L2CAP_CONFIGRATION_RSP
  sdp_cb.reg_info.pL2CA_DisconnectInd_Cb = sdp_disconnect_ind;	//收到对端断开请求 L2CAP_DISCONNECTION_REQ
  sdp_cb.reg_info.pL2CA_DisconnectCfm_Cb = sdp_disconnect_cfm;	// 收到对端断开回复 L2CAP_DISCONNECTION_RSP
  sdp_cb.reg_info.pL2CA_DataInd_Cb = sdp_data_ind;
  sdp_cb.reg_info.pL2CA_Error_Cb = sdp_on_l2cap_error;

  /* Now, register with L2CAP */
  if (!L2CA_Register2(BT_PSM_SDP, sdp_cb.reg_info, true /* enable_snoop */,
                      nullptr, SDP_MTU_SIZE, 0, BTM_SEC_NONE)) {
    SDP_TRACE_ERROR("SDP Registration failed");
  }
}

前面是发送连接请求,根据交互知道会收到对端的L2CAP_CONNECTION_RSP,也就是L2CAP通道打开之后会调用pL2CA_ConnectCfm_Cb,也就是sdp_connect_cfm函数内容。

  1. sdp_connect_cfm
static void sdp_connect_cfm(uint16_t l2cap_cid, uint16_t result) {
  tCONN_CB* p_ccb;

  /* Find CCB based on CID */
  p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
  if (p_ccb == 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;
  } else {
    LOG(ERROR) << __func__ << ": invoked with non OK status";
  }
}

处理完sdp_connect_cfm之后怎么发送L2CAP_CONFIGRATION_RQE呢?其实发送connection request 之后,l2cap channel状态是等待对端回应的状态的,在收到L2CEVT_L2CAP_CONNECT_RSP之后,l2cap_channel状态机会调用函数L2CEVT_L2CAP_CONNECT_RSP发送个配置请求。

  1. l2c_csm_send_config_req
    l2cap配置的过程中只关心MTU,发送的request告诉server自己这边的最大的MTU,对端也会发送告诉client自己的mtu,然后选择个小的。
static void l2c_csm_send_config_req(tL2C_CCB* p_ccb) {
  tL2CAP_CFG_INFO config{};
  config.mtu_present = true;
  config.mtu = p_ccb->p_rcb->my_mtu;
  p_ccb->max_rx_mtu = config.mtu;
  if (p_ccb->p_rcb->ertm_info.preferred_mode != L2CAP_FCR_BASIC_MODE) {
    config.fcr_present = true;
    config.fcr = kDefaultErtmOptions;
  }
  p_ccb->our_cfg = config;
  l2c_csm_execute(p_ccb, L2CEVT_L2CA_CONFIG_REQ, &config);// l2cap_channel 状态机的
}

由于之前channel状态已经走到了CST_CONFIG,接下来l2c_csm_config会在本地处理L2CEVT_L2CA_CONFIG_REQ
共有两个步骤:

  • l2cu_process_our_cfg_req提取感兴趣的信息保存在ccb中
  • l2cu_send_peer_config_req建立并发送L2cap消息给到对方

相应的收到对端L2CEVT_L2CAP_CONFIG_RSP之后,最终会调用l2c_csm_indicate_connection_open,然后在调用api.pL2CA_ConfigCfm_Cb,实现在sdp_config_cfm,中间的L2CAP发送和处理消息的过程就省略了

  1. sdp_config_cfm
    更新了con_state 状态,另外sdp_config_ind中保存了对端发来的MTU
static void sdp_config_cfm(uint16_t l2cap_cid, uint16_t initiator,
                           tL2CAP_CFG_INFO* p_cfg) {
  sdp_config_ind(l2cap_cid, p_cfg);	// 对端发的请求,保存对端发的MTK

  tCONN_CB* p_ccb;

  SDP_TRACE_EVENT("SDP - Rcvd cfg cfm, CID: 0x%x", l2cap_cid);

  /* Find CCB based on CID */
  p_ccb = sdpu_find_ccb_by_cid(l2cap_cid);
  if (p_ccb == 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 */
  p_ccb->con_state = SDP_STATE_CONNECTED;

  if (p_ccb->con_flags & SDP_FLAGS_IS_ORIG) { // true,p_ccb->con_flags在前面sdp_conn_originate时有赋值
    sdp_disc_connected(p_ccb);
  } else {
    /* Start inactivity timer */
    alarm_set_on_mloop(p_ccb->sdp_conn_timer, SDP_INACT_TIMEOUT_MS,
                       sdp_conn_timer_timeout, p_ccb);
  }
}
  1. sdp_disc_connected
void sdp_disc_connected(tCONN_CB* p_ccb) {
  if (p_ccb->is_attr_search) { //true,SDP_ServiceSearchAttributeRequest时有赋值
    p_ccb->disc_state = SDP_DISC_WAIT_SEARCH_ATTR;

    process_service_search_attr_rsp(p_ccb, NULL, 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);
  }
}

process_service_search_attr_rsp相比与sdp_snd_service_search_req多了一个attribute,hci区别如下:
在这里插入图片描述
attribute的回复中多了红框内容,包含配置及其依赖的协议,版本,支持的feature等。
在这里插入图片描述

  1. process_service_search_attr_rsp
static void process_service_search_attr_rsp(tCONN_CB* p_ccb, uint8_t* p_reply,
                                            uint8_t* p_reply_end) {
  uint8_t *p, *p_start, *p_end, *p_param_len;
  uint8_t type;
  uint32_t seq_len;
  uint16_t param_len, lists_byte_count = 0;
  bool cont_request_needed = false;

  /* If p_reply is NULL, we were called for the initial read */
  if (p_reply) {
	// 回复内容的处理,回复数据长度的安全检查
  }

  /* If continuation request (or first time request) */
  if ((cont_request_needed) || (!p_reply)) {
	//写发送的数据
  }

/*******************************************************************/
/* We now have the full response, which is a sequence of sequences */
/*******************************************************************/
  // attribute的数据处理
  ...
  /* Since we got everything we need, disconnect the call */
  sdpu_log_attribute_metrics(p_ccb->device_address, p_ccb->p_db);
  sdp_disconnect(p_ccb, SDP_SUCCESS);
}

开始还疑问是rsp,而不是req;看到逻辑得知,会发多次,并不知道之前有没有发或者有没有对端的rsp数据过来,所以先处理p_reply,如果为空在写发送的数据。
最后会调用sdp_disconnect去告诉用户断开原因,重置con_state为SDP_STATE_IDLE,并删除可能持有的任何响应指针清空response buffer
然后还是同样的l2cap中走一圈然后到sdp_disconnect_cfm

  1. sdp_disconnect_cfm
    回调并释放
static void sdp_disconnect_cfm(uint16_t l2cap_cid,
                               UNUSED_ATTR uint16_t result) {
  tCONN_CB* p_ccb;

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

  SDP_TRACE_EVENT("SDP - Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid);

  sdpu_callback(ccb, static_cast<tSDP_STATUS>(ccb.disconnect_reason));
  sdpu_process_pend_ccb_new_cid(ccb);
  sdpu_release_ccb(ccb);
}
  1. sdpu_callback
void sdpu_callback(tCONN_CB& ccb, tSDP_REASON reason) {
  if (ccb.p_cb) {
    (ccb.p_cb)(reason);
  } else if (ccb.p_cb2) {
    (ccb.p_cb2)(reason, ccb.user_data);
  }
}
  1. bta_dm_sdp_callback
    sdpu_callback怎么到bta_dm_sdp_callback呢,因为SDP_ServiceSearchAttributeRequest中给的回调。
static void bta_dm_sdp_callback(tSDP_STATUS sdp_status) {
  tBTA_DM_SDP_RESULT* p_msg =
      (tBTA_DM_SDP_RESULT*)osi_malloc(sizeof(tBTA_DM_SDP_RESULT));

  p_msg->hdr.event = BTA_DM_SDP_RESULT_EVT;
  p_msg->sdp_result = sdp_status;

  bta_sys_sendmsg(p_msg);
}
  1. bta_dm_search_sm_execute
    BTA_DM_DISCOVER_ACTIVE状态处理BTA_DM_SDP_RESULT_EVT
bool bta_dm_search_sm_execute(BT_HDR_RIGID* p_msg) {
  ...
    case BTA_DM_DISCOVER_ACTIVE:
      switch (p_msg->event) {
        ...
        case BTA_DM_SDP_RESULT_EVT:
          bta_dm_sdp_result(message);
          break;

为啥会在BTA_DM_DISCOVER_ACTIVE状态呢?
因为之前在BTA_DM_SEARCH_IDLE状态处理BTA_DM_API_DISCOVER_EVT消息时设置了此状态。

  1. bta_dm_sdp_result
    处理SDP结果
void bta_dm_sdp_result(tBTA_DM_MSG* p_data) {
  ...
  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)) { // 内容都在这个判断中
	/* 
	1. while循环,通过bta_dm_search_cb.service_index判断是对那个service查询的,是否需要继续查询这样
	2. 收集128位的service并将其放入到list数据库
	*/
	...
    /* 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 {
        /* 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) {
          // SDP_FindServiceUUIDInRec_128bit is used only once, refactor?
          Uuid temp_uuid;
          if (SDP_FindServiceUUIDInRec_128bit(p_sdp_rec, &temp_uuid)) {
            uuid_list.push_back(temp_uuid);
          }
        }
      } while (p_sdp_rec);
    }
	...
    /* if there are more services to search for */
    if (bta_dm_search_cb.services_to_search) { // 搜pnp之后值为 0x7FFFFFFE,但是搜完L2cap之后又变回0了
      /* Free up the p_sdp_db before checking the next one */
      bta_dm_free_sdp_db();
      bta_dm_find_services(bta_dm_search_cb.peer_bdaddr); // 继续下一次的service查找,pnp之后就是l2cap了
    } else {
      /* callbacks */
      /* start next bd_addr if necessary */
      ...
  } else {
  // 发送BTA_DM_DISCOVERY_RESULT_EVT事件
  }
}

这里需要解释下为啥看这个循环好像只循环了两次
第一次:
搜pnp,serice_to_search为0x7FFFFFFE,service_index为1,由于serice_to_search不为0,然后需要进行第二次搜索,也就是L2cap的搜索
第二次:
L2cap搜完之后,service_index为0,service_index为2。因为是组合了SDP_SERVICE_SEARCH_REQSDP_SERVICE_ATTR_REQ,所以回复中Continuation length bytes不为0,会有多次回复,attribute中包含了对端支持的所有的profile & protocol。后面的回复内容不为空,所以会将所有的内容进行存储。
在这里插入图片描述
后续的PCE是在bta_dm_find_services中有判断如果是UUID_PROTOCOL_L2CAP,则进行地址名单匹配。
再后面的Handsfree和AudioSink是对应的profile连接的时候发起的。

  1. bta_dm_search_sm_execute
    BTA_DM_DISCOVER_ACTIVE状态处理BTA_DM_DISCOVERY_RESULT_EVT
bool bta_dm_search_sm_execute(BT_HDR_RIGID* p_msg) {
  ...
    case BTA_DM_DISCOVER_ACTIVE:
      switch (p_msg->event) {
        ...
        case BTA_DM_DISCOVERY_RESULT_EVT:
          bta_dm_disc_result(message);
          break;
  1. bta_dm_disc_result
void bta_dm_disc_result(tBTA_DM_MSG* p_data) {
  APPL_TRACE_EVENT("%s", __func__);

  /* disc_res.device_type is set only when GATT discovery is finished in
   * bta_dm_gatt_disc_complete */
  bool is_gatt_over_ble = ((p_data->disc_result.result.disc_res.device_type &
                            BT_DEVICE_TYPE_BLE) != 0);

  /* if any BR/EDR service discovery has been done, report the event */
  if (!is_gatt_over_ble && (bta_dm_search_cb.services &
                            ((BTA_ALL_SERVICE_MASK | BTA_USER_SERVICE_MASK) &
                             ~BTA_BLE_SERVICE_MASK)))
    bta_dm_search_cb.p_search_cback(BTA_DM_DISC_RES_EVT,
                                    &p_data->disc_result.result);

  bta_dm_search_cmpl();
}

bta_dm_search_cb.p_search_cback对应的是btif_dm_search_services_evt,在调用BTA_DmDiscover是传入。
btif_dm_search_services_evt内容是uuid的存储,并更新device properties给到framework。
大致流程btif_dm_search_services_evt --> invoke_remote_device_properties_cb --> remote_device_properties_callback --> ```devicePropertyChangedCallback``

  1. bta_dm_search_cmpl
void bta_dm_search_cmpl() {
  bta_dm_search_set_state(BTA_DM_SEARCH_IDLE);

  uint16_t conn_id = bta_dm_search_cb.conn_id;

  tBTA_DM_SEARCH result;
  std::vector<Uuid> gatt_services;
  result.disc_ble_res.services = &gatt_services;
  result.disc_ble_res.bd_addr = bta_dm_search_cb.peer_bdaddr;
  strlcpy((char*)result.disc_ble_res.bd_name, bta_dm_get_remname(),
          BD_NAME_LEN + 1);

  bool send_gatt_results =
      bluetooth::common::init_flags::
              always_send_services_if_gatt_disc_done_is_enabled()
          ? bta_dm_search_cb.gatt_disc_active
          : false;

  /* no BLE connection, i.e. Classic service discovery end */
  if (conn_id == GATT_INVALID_CONN_ID) {
    if (bta_dm_search_cb.gatt_disc_active) {
      LOG_WARN(
          "GATT active but no BLE connection, likely disconnected midway "
          "through");
    } else {
      LOG_INFO("No BLE connection, processing classic results");
    }
  } else {
    btgatt_db_element_t* db = NULL;
    int count = 0;
    BTA_GATTC_GetGattDb(conn_id, 0x0000, 0xFFFF, &db, &count);
    if (count != 0) {
      for (int i = 0; i < count; i++) {
        // we process service entries only
        if (db[i].type == BTGATT_DB_PRIMARY_SERVICE) {
          gatt_services.push_back(db[i].uuid);
        }
      }
      osi_free(db);
      LOG_INFO(
          "GATT services discovered using LE Transport, will always send to "
          "upper layer");
      send_gatt_results = true;
    } else {
      LOG_WARN("Empty GATT database - no BLE services discovered");
    }
  }

  // send all result back to app
  if (send_gatt_results) {
    LOG_INFO("Sending GATT results to upper layer");
    bta_dm_search_cb.p_search_cback(BTA_DM_GATT_OVER_LE_RES_EVT, &result);
  }

  bta_dm_search_cb.p_search_cback(BTA_DM_DISC_CMPL_EVT, nullptr);//对应事件没有处理内容
  bta_dm_search_cb.gatt_disc_active = false;

#if TARGET_FLOSS
  if (DIS_ReadDISInfo(bta_dm_search_cb.peer_bdaddr, bta_dm_read_dis_cmpl,
                      DIS_ATTR_PNP_ID_BIT)) {
    return;
  }
#endif

  bta_dm_execute_queued_request();
}
  1. bta_dm_execute_queued_request
    将状态转为pending
void bta_dm_execute_queued_request() {
  tBTA_DM_MSG* p_pending_discovery = (tBTA_DM_MSG*)fixed_queue_try_dequeue(
      bta_dm_search_cb.pending_discovery_queue);
  if (p_pending_discovery) {
    LOG_INFO("%s Start pending discovery", __func__);
    bta_sys_sendmsg(p_pending_discovery);
  } else if (bta_dm_search_cb.p_pending_search) {
    LOG_INFO("%s Start pending search", __func__);
    bta_sys_sendmsg(bta_dm_search_cb.p_pending_search);
    bta_dm_search_cb.p_pending_search = NULL;
  }
}

参考:
https://www.cnblogs.com/libs-liu/p/9399517.html
http://aospxref.com/

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值