BlueDroid HFP 源码分析笔记(1)

android 蓝牙hfp client实现简介 首先确认配置文件是否开启hfp profile功能。根据设备的角色(hfp client / hfp server)来配置hfp profile. profile 配置文件路径: alps/packages/apps/Bluetooth/res/values/config.xml alps/device/${customer}/${ProjectName}/overlay/packages/app... 阅读详情

BlueDroid 代码分析

对部分代码一眼无法知道运行结果的地方备注。

HPF Connect

可以理解为闭包吧!将参数和方法打包在一起传入队列中等待执行。最终执行的就是 connect_init(),参数就是bd_addr, connect_init。

static bt_status_t connect(RawAddress* bd_addr) {
  return btif_queue_connect(UUID_SERVCLASS_HF_HANDSFREE, bd_addr, connect_int);
}

这里的用到BlueDroid中最常用的消息传递方式,malloc出结构体后。赋值event和layer_specific。
然后通过bta_sys_sendmsg发送event到bta_sys_main.cc bta_sys_event中进行处理。

void BTA_HfClientOpen(const RawAddress& bd_addr, tBTA_SEC sec_mask,
                      uint16_t* p_handle) {
  APPL_TRACE_DEBUG("%s", __func__);
  tBTA_HF_CLIENT_API_OPEN* p_buf =
      (tBTA_HF_CLIENT_API_OPEN*)osi_malloc(sizeof(tBTA_HF_CLIENT_API_OPEN));

  if (!bta_hf_client_allocate_handle(bd_addr, p_handle)) {
    APPL_TRACE_ERROR("%s: could not allocate handle", __func__);
    return;
  }

  p_buf->hdr.event = BTA_HF_CLIENT_API_OPEN_EVT; //BTA_HF_CLIENT_API_OPEN_EVT = 27 << 8:
  p_buf->hdr.layer_specific = *p_handle;
  p_buf->bd_addr = bd_addr;
  p_buf->sec_mask = sec_mask;

  bta_sys_sendmsg(p_buf);
}

enum {
  /* these events are handled by the state machine */
  BTA_HF_CLIENT_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_HS), // BTA_ID_HS 27
  BTA_HF_CLIENT_API_CLOSE_EVT,
  BTA_HF_CLIENT_API_AUDIO_OPEN_EVT,
  BTA_HF_CLIENT_API_AUDIO_CLOSE_EVT,
  BTA_HF_CLIENT_RFC_OPEN_EVT,
  BTA_HF_CLIENT_RFC_CLOSE_EVT,
  BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT,
  BTA_HF_CLIENT_RFC_DATA_EVT,
  BTA_HF_CLIENT_DISC_ACP_RES_EVT,
  BTA_HF_CLIENT_DISC_INT_RES_EVT,
  BTA_HF_CLIENT_DISC_OK_EVT,
  BTA_HF_CLIENT_DISC_FAIL_EVT,
  BTA_HF_CLIENT_SCO_OPEN_EVT,
  BTA_HF_CLIENT_SCO_CLOSE_EVT,
  BTA_HF_CLIENT_SEND_AT_CMD_EVT,
  BTA_HF_CLIENT_MAX_EVT,

  /* these events are handled outside of the state machine */
  BTA_HF_CLIENT_API_ENABLE_EVT,
  BTA_HF_CLIENT_API_DISABLE_EVT
};


//记住这三个结构体,都包含有一个BT_HDR 的结构提。这结构体中包含一些共同的信息
/* data type for BTA_HF_CLIENT_API_OPEN_EVT */
typedef struct {
  BT_HDR hdr;
  RawAddress bd_addr;
  uint16_t* handle;
  tBTA_SEC sec_mask;
} tBTA_HF_CLIENT_API_OPEN;

/* data type for BTA_HF_CLIENT_DISC_RESULT_EVT */
typedef struct {
  BT_HDR hdr;
  uint16_t status;
} tBTA_HF_CLIENT_DISC_RESULT;

/* data type for RFCOMM events */
typedef struct {
  BT_HDR hdr;
  uint16_t port_handle;
} tBTA_HF_CLIENT_RFC;

/* generic purpose data type for other events */
typedef struct {
  BT_HDR hdr;
  bool bool_val;
  uint8_t uint8_val;
  uint32_t uint32_val1;
  uint32_t uint32_val2;
  char str[BTA_HF_CLIENT_NUMBER_LEN + 1];
} tBTA_HF_CLIENT_DATA_VAL;

//注意这是一个联合体!!! 后面会知道这个联合体的用处。
/* union of all event datatypes */
typedef union {
  BT_HDR hdr;
  tBTA_HF_CLIENT_API_OPEN api_open;
  tBTA_HF_CLIENT_DISC_RESULT disc_result;
  tBTA_HF_CLIENT_RFC rfc;
  tBTA_HF_CLIENT_DATA_VAL val;

} tBTA_HF_CLIENT_DATA;

这个方法就是处理event。传进来的参数已经是BT_HDR *,因为这个方法是通用的处理。不止HFP会发送消息到这里进行处理。其他的profile也会发送消息过来,所以需要一个共同的结构体。

  • id = (uint8_t)(p_msg->event >> 8);
    获取的id是右移8位,为什么要这样了。是因为BTA_HF_CLIENT_API_OPEN_EVT就是在 BTA_ID_HS的基础上左移8位,同时这个值又被enum包括,这样后续的值再右移8位后得到的值仍然是BTA_ID_HS。既保证了Event的不同,同时保证这些Event指向同一个ID。

  • (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg)
    从上面我们知道id等于BTA_ID_HS,那就从数组中去除对应register进行处理,使用BTA_ID_HS进行注册的是在bta_hf_client_main.cc bta_hf_client_api_enable()方法中。最终调用 bta_hf_client_hdl_event()。
    这个过程还是比较绕的。

//bta_sys_main.cc
void bta_sys_event(BT_HDR* p_msg) {
  uint8_t id;
  bool freebuf = true;

  APPL_TRACE_EVENT("%s: Event 0x%x", __func__, p_msg->event);

  /* get subsystem id from event */
  id = (uint8_t)(p_msg->event >> 8);

  /* verify id and call subsystem event handler */
  if ((id < BTA_ID_MAX) && (bta_sys_cb.reg[id] != NULL)) {
    freebuf = (*bta_sys_cb.reg[id]->evt_hdlr)(p_msg);
  } else {
    APPL_TRACE_WARNING("%s: Received unregistered event id %d", __func__, id);
  }

  if (freebuf) {
    osi_free(p_msg);
  }
}
// 2 BTA_HF_CLIENT_API_OPEN_EVT = BTA_ID_HS << 8; 这样后续的值通过右移恢复都会得到同样的值BTA_ID_HS
enum {
  /* these events are handled by the state machine */
  BTA_HF_CLIENT_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_HS), // BTA_ID_HS 27
  BTA_HF_CLIENT_API_CLOSE_EVT,
  BTA_HF_CLIENT_API_AUDIO_OPEN_EVT,
  BTA_HF_CLIENT_API_AUDIO_CLOSE_EVT,
  BTA_HF_CLIENT_RFC_OPEN_EVT,
  BTA_HF_CLIENT_RFC_CLOSE_EVT,
  BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT,
  BTA_HF_CLIENT_RFC_DATA_EVT,
  BTA_HF_CLIENT_DISC_ACP_RES_EVT,
  BTA_HF_CLIENT_DISC_INT_RES_EVT,
  BTA_HF_CLIENT_DISC_OK_EVT,
  BTA_HF_CLIENT_DISC_FAIL_EVT,
  BTA_HF_CLIENT_SCO_OPEN_EVT,
  BTA_HF_CLIENT_SCO_CLOSE_EVT,
  BTA_HF_CLIENT_SEND_AT_CMD_EVT,
  BTA_HF_CLIENT_MAX_EVT,

  /* these events are handled outside of the state machine */
  BTA_HF_CLIENT_API_ENABLE_EVT,
  BTA_HF_CLIENT_API_DISABLE_EVT
};

//这个方法是在通过 init方法调用的,里面初始化的bta_hf_client_cb_arr 这个重要的结构体。
tBTA_STATUS bta_hf_client_api_enable(tBTA_HF_CLIENT_CBACK* p_cback,
                                     tBTA_SEC sec_mask,
                                     tBTA_HF_CLIENT_FEAT features,
                                     const char* p_service_name) {
  ...
  
  /* register with BTA system manager */
  bta_sys_register(BTA_ID_HS, &bta_hf_client_reg);
  
  ...
  return BTA_SUCCESS;
}

小结
  1. 看到 bta_sys_sendmsg(p_buf);
  2. 找到Event 对应的id.
  3. 找到对应id注册到bta_sys_main中的处理方法
  4. 直接跳转对应的处理方法。
状态机的切换

下面的代码会比较绕,一眼看过还是需要回头在细细品味一下的。我直接在代码中添加注释了。

//注意此处传进来的参数是tBTA_HF_CLIENT_DATA * 联合体,这是因为 tBTA_HF_CLIENT_API_OPEN
//tBTA_HF_CLIENT_DISC_RESULT tBTA_HF_CLIENT_RFC  tBTA_HF_CLIENT_DATA_VAL 都会在这里进行处理
void bta_hf_client_sm_execute(uint16_t event, tBTA_HF_CLIENT_DATA* p_data) {
  //p_data->hdr.layer_specific 是 handle = 1,此处的结构体是tBTA_HF_CLIENT_CB 
  //(ps:CB control bolck 千万不要联想是callback)
  tBTA_HF_CLIENT_CB* client_cb =
      bta_hf_client_find_cb_by_handle(p_data->hdr.layer_specific);
  if (client_cb == NULL) {
    APPL_TRACE_ERROR("%s: cb not found for handle %d", __func__,
                     p_data->hdr.layer_specific);
    return;
  }

  tBTA_HF_CLIENT_ST_TBL state_table;
  uint8_t action;
  int i;
  //BTA_HF_CLIENT_API_OPEN_EVT = BTA_SYS_EVT_START(BTA_ID_HS) ===> 
  uint16_t in_event = event;
  //client_cb-> 初始状态是 0
  uint8_t in_state = client_cb->state;

  /* Ignore displaying of AT results when not connected (Ignored in state
   * machine) */
  if (client_cb->state == BTA_HF_CLIENT_OPEN_ST) {
    APPL_TRACE_EVENT("HF Client evt : State %d (%s), Event 0x%04x (%s)",
                     client_cb->state,
                     bta_hf_client_state_str(client_cb->state), event,
                     bta_hf_client_evt_str(event));
  }
  //!!! 经过与操作后 event就是值是从0开始,后续的值就是从0的基础上加1。
  event &= 0x00FF;
  if (event >= (BTA_HF_CLIENT_MAX_EVT & 0x00FF)) {
    APPL_TRACE_ERROR("HF Client evt out of range, ignoring...");
    return;
  }

  //bta_hf_client_st_tbl是一组状态表。 (ps:st_tbl state_table)
  //client_cb->state 当前是0, 对应状态表:bta_hf_client_st_init 
  /* look up the state table for the current state */
  state_table = bta_hf_client_st_tbl[client_cb->state];

  /* set next state */
  //通过bta_hf_client_st_init 对应下一个状态。  event = 0, BTA_HF_CLIENT_NEXT_STATE = 2
  //就是第一行的最后一个参数就是下一个状态。
  //   Event              Action 1                 Action 2                 Next State
  // API_OPEN_EVT | {BTA_HF_CLIENT_START_OPEN, BTA_HF_CLIENT_IGNORE, BTA_HF_CLIENT_OPENING_ST},
  //client_cb->state = BTA_HF_CLIENT_OPENING_ST; (1)
  client_cb->state = state_table[event][BTA_HF_CLIENT_NEXT_STATE];

  /* execute action functions */
  // bta_hf_client_action 是一个action 函数指针 数组
  // action = state_table[event][i]; 就是通过查表找对对应action 执行,
  // 此处的action BTA_HF_CLIENT_START_OPEN 2 
  // 执行bta_hf_client_act.cc bta_hf_client_start_open 方法
  for (i = 0; i < BTA_HF_CLIENT_ACTIONS; i++) {
    action = state_table[event][i];
    if (action != BTA_HF_CLIENT_IGNORE) {
      (*bta_hf_client_action[action])(p_data);
    } else {
      break;
    }
  }

  //当前的client_cb->state 为BTA_HF_CLIENT_OPENING_ST,虽然与前一个状态不同。但是不会执行下面的方法。
  /* If the state has changed then notify the app of the corresponding change */
  if (in_state != client_cb->state) {
    VLOG(1) << __func__ << ": notifying state change to " << in_state << " -> "
            << client_cb->state << " device " << client_cb->peer_addr;
    tBTA_HF_CLIENT evt;
    memset(&evt, 0, sizeof(evt));
    evt.bd_addr = client_cb->peer_addr;
    if (client_cb->state == BTA_HF_CLIENT_INIT_ST) {
      bta_hf_client_app_callback(BTA_HF_CLIENT_CLOSE_EVT, &evt);
    } else if (client_cb->state == BTA_HF_CLIENT_OPEN_ST) {
      evt.open.handle = client_cb->handle;
      bta_hf_client_app_callback(BTA_HF_CLIENT_OPEN_EVT, &evt);
    }
  }

  /* if the next state is INIT then release the cb for future use */
  /* If the collision timer is scheduled, don't release the cb */
  if ((client_cb->state == BTA_HF_CLIENT_INIT_ST) &&
      (!alarm_is_scheduled(client_cb->collision_timer))) {
    APPL_TRACE_DEBUG("%s: marking CB handle %d to false", __func__,
                     client_cb->handle);
    client_cb->is_allocated = false;
  }
  ...
}

/* state table */
const tBTA_HF_CLIENT_ST_TBL bta_hf_client_st_tbl[] = {
    bta_hf_client_st_init, 
    bta_hf_client_st_opening, 
    bta_hf_client_st_open,
    bta_hf_client_st_closing};


/* action functions table, indexed with action enum */
const tBTA_HF_CLIENT_ACTION bta_hf_client_action[] = {
    /* BTA_HF_CLIENT_RFC_DO_CLOSE */ bta_hf_client_rfc_do_close,
    /* BTA_HF_CLIENT_START_CLOSE */ bta_hf_client_start_close,
    /* BTA_HF_CLIENT_START_OPEN */ bta_hf_client_start_open,
    /* BTA_HF_CLIENT_RFC_ACP_OPEN */ bta_hf_client_rfc_acp_open,
    /* BTA_HF_CLIENT_SCO_LISTEN */ NULL,
    /* BTA_HF_CLIENT_SCO_CONN_OPEN */ bta_hf_client_sco_conn_open,
    /* BTA_HF_CLIENT_SCO_CONN_CLOSE*/ bta_hf_client_sco_conn_close,
    /* BTA_HF_CLIENT_SCO_OPEN */ bta_hf_client_sco_open,
    /* BTA_HF_CLIENT_SCO_CLOSE */ bta_hf_client_sco_close,
    /* BTA_HF_CLIENT_FREE_DB */ bta_hf_client_free_db,
    /* BTA_HF_CLIENT_OPEN_FAIL */ bta_hf_client_open_fail,
    /* BTA_HF_CLIENT_RFC_OPEN */ bta_hf_client_rfc_open,
    /* BTA_HF_CLIENT_RFC_FAIL */ bta_hf_client_rfc_fail,
    /* BTA_HF_CLIENT_DISC_INT_RES */ bta_hf_client_disc_int_res,
    /* BTA_HF_CLIENT_RFC_DO_OPEN */ bta_hf_client_rfc_do_open,
    /* BTA_HF_CLIENT_DISC_FAIL */ bta_hf_client_disc_fail,
    /* BTA_HF_CLIENT_RFC_CLOSE */ bta_hf_client_rfc_close,
    /* BTA_HF_CLIENT_RFC_DATA */ bta_hf_client_rfc_data,
    /* BTA_HF_CLIENT_DISC_ACP_RES */ bta_hf_client_disc_acp_res,
    /* BTA_HF_CLIENT_SVC_CONN_OPEN */ bta_hf_client_svc_conn_open,
    /* BTA_HF_CLIENT_SEND_AT_CMD */ bta_hf_client_send_at_cmd,
};
小结
  1. 不同的状态对应不同的 状态表(state table) 找到初始状态。
  2. Event &0x00FF后就是枚举表中的顺序,再将其转化成state table中的位置。
  3. 找到对应的列后,最后一个值就是下一个状态,第一个值对应是当前action。
  4. 根据action值(既数组中下标)去action 函数指针数组中找到对应的函数指针。
SDP Discover

在连接之前需要通过SDP发现对端的服务。SDP_ServiceSearchAttributeRequest2()最后的结果会回调给bta_hf_client_sdp_cback。然后又是发送Event到bta_sys_main.cc 的bta_sys_event中进行处理。
最后还是回到bta_hf_client_main.cc 的 bta_hf_client_sm_execute()方法中。

void bta_hf_client_do_disc(tBTA_HF_CLIENT_CB* client_cb) {
  Uuid uuid_list[1];
  uint16_t num_uuid = 1;
  uint16_t attr_list[4];
  uint8_t num_attr;
  bool db_inited = false;

  /* initiator; get proto list and features */
  if (client_cb->role == BTA_HF_CLIENT_INT) {
    attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
    attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST;
    attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST;
    attr_list[3] = ATTR_ID_SUPPORTED_FEATURES;
    num_attr = 4;
    uuid_list[0] = Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE);
  }
  /* acceptor; get features */
  else {
    attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
    attr_list[1] = ATTR_ID_BT_PROFILE_DESC_LIST;
    attr_list[2] = ATTR_ID_SUPPORTED_FEATURES;
    num_attr = 3;
    uuid_list[0] = Uuid::From16Bit(UUID_SERVCLASS_AG_HANDSFREE);
  }

  /* allocate buffer for sdp database */
  client_cb->p_disc_db = (tSDP_DISCOVERY_DB*)osi_malloc(BT_DEFAULT_BUFFER_SIZE);

  /* set up service discovery database; attr happens to be attr_list len */
  db_inited = SDP_InitDiscoveryDb(client_cb->p_disc_db, BT_DEFAULT_BUFFER_SIZE,
                                  num_uuid, uuid_list, num_attr, attr_list);

  if (db_inited) {
    /*Service discovery not initiated */
    //会走到此方法中
    db_inited = SDP_ServiceSearchAttributeRequest2(
        client_cb->peer_addr, client_cb->p_disc_db, bta_hf_client_sdp_cback,
        (void*)client_cb);
  }

  if (!db_inited) {
    /*free discover db */
    osi_free_and_reset((void**)&client_cb->p_disc_db);
    /* sent failed event */
    tBTA_HF_CLIENT_DATA msg;
    msg.hdr.layer_specific = client_cb->handle;
    bta_hf_client_sm_execute(BTA_HF_CLIENT_DISC_FAIL_EVT, &msg);
  }
}

static void bta_hf_client_sdp_cback(uint16_t status, void* data) {
  uint16_t event;
  //注意这里是tBTA_HF_CLIENT_DISC_RESULT 结构体 之前是tBTA_HF_CLIENT_API_OPEN
  tBTA_HF_CLIENT_DISC_RESULT* p_buf = (tBTA_HF_CLIENT_DISC_RESULT*)osi_malloc(
      sizeof(tBTA_HF_CLIENT_DISC_RESULT));

  APPL_TRACE_DEBUG("bta_hf_client_sdp_cback status:0x%x", status);
  tBTA_HF_CLIENT_CB* client_cb = (tBTA_HF_CLIENT_CB*)data;

  /* set event according to int/acp */
  //role 是init
  if (client_cb->role == BTA_HF_CLIENT_ACP)
    event = BTA_HF_CLIENT_DISC_ACP_RES_EVT;
  else
    event = BTA_HF_CLIENT_DISC_INT_RES_EVT;

  p_buf->hdr.event = event;
  p_buf->hdr.layer_specific = client_cb->handle;
  p_buf->status = status;

  bta_sys_sendmsg(p_buf);
}

最后绕到bta_hf_client_main.cc方法中,我们来分析一波。

  1. 首先从API_OPEN 状态 BTA_HF_CLIENT_INIT_ST, 之后将状态迁移到 BTA_HF_CLIENT_OPENING_ST。对应的state_stable是 bta_hf_client_st_opening。

  2. BTA_HF_CLIENT_DISC_INT_RES_EVT 再枚举中排第9。在bta_hf_client_st_opening找到下标为9那一列:

    /* DISC_INT_RES_EVT */ {BTA_HF_CLIENT_DISC_INT_RES, BTA_HF_CLIENT_IGNORE,
                            BTA_HF_CLIENT_OPENING_ST},
  1. 找相应的action 值,以及下一个状态。 BTA_HF_CLIENT_DISC_INT_RES 为 13,下一个迁移状态仍然是 BTA_HF_CLIENT_OPENING_ST

  2. 根据上面的action值,在bta_hf_client_action 函数指针数组中找到对应下标的函数。
    最后找到

    /* BTA_HF_CLIENT_DISC_INT_RES */ bta_hf_client_disc_int_res,

接下就是Refcom connect, 两端建立连接。最关键的在于这个状态表迁移的逻辑。如果能够理解整个流程还是比较好理解的。

MTK平台 -- 关于 HFP 代码流程 \alps\frameworks\base\services\java\com\android\serverSystemServer.java,BluetoothManagerServices.java,BluetoothSBluetooth.h, bt_hf.h,bt_hf_client.h, bt_av.h,Bt_sock.h, bt_hh.h, bt_hl.h,bt_pan.h, bt_mce.h, bt_gatt.h,bt_rc.h, bt_sdp.hbtif_core.c, bluetooth.c 阅读详情

相关推荐

【Bluedroid】HFP Client 连接初始化流程解析(四):连接回调处理

bta_hf_client_mgmt_cback函数是HF(Hands-Free)客户端模块中的RFCOMM管理回调。处理来自RFCOMM层的各种管理事件,如连接成功、连接失败等,并根据这些事件执行相应的操作。

byte轻骑兵的技术小窝 6504

bluedroid thread

bluedroid的消息传递机制 1 创建一个队列: btu_bta_msg_queue = fixed_queue_new(SIZE_MAX); fixed_queue_t *ret = osi_calloc(sizeof(fixed_queue_t)); //队列里面最大的容量 ret-&gt;capacity = capacity; //链表 ret-&gt;list = li...

x19910304xiaoyao的专栏 586

【Bluedroid】HFP Client 连接初始化流程解析(一)

connect函数的核心目标是发起一个连接到指定蓝牙设备,该设备支持蓝牙免提协议(HFP, Hands-Free Profile)的,通过调用 btif_queue_connect 来实际发起连接。btif_queue_connect 函数会将连接请求排队,等待蓝牙堆栈的适当时机来处理。

byte轻骑兵的技术小窝 7102

bluedroid 协议栈(二)

Bta: 1. ag(audio gateway):蓝牙音频网关的实现 2. Ar(audio/video registration):音频/视频注册模块的实现 3. Av(advanced audio/video):更先进的音频/视频实现 4. Dm(device manager):bta设备管理实现 5. Fs(file system):包含bta文件系统编译时间可配置常数以及回调函

william.shen的专栏 5094

【Bluedroid】HFP Client 连接初始化流程解析(三):RFCOMM DLC 参数协商与连接建立

本篇主要围绕RFCOMMDLC参数协商进行分析。RFCOMM(RadioFrequencyCOMmunication)作为一种基于蓝牙的串行通信协议,其核心在于通过模拟RS-232串行端口,实现设备间的数据传输。在RFCOMM协议框架下,数据链路连接(DLC)扮演着数据传输的基本角色,而DLC参数的协商则是建立有效RFCOMM连接的关键步骤。

byte轻骑兵的技术小窝 7297

【Bluedroid】HFP Client 连接初始化流程解析(二):RFCOMM Connect与UA Frame处理

本文深入分析蓝牙HFP协议中RFCOMM连接建立与UA帧处理的核心流程。

byte轻骑兵的技术小窝 5359

BlueDroid代码学习分享

https://blog.youkuaiyun.com/zcc450959507/article/details/79402406

edmond999的专栏 477

【车载 AOSP 蓝牙(bluedroid) 协议分析】【整体计划】

controller: 蓝牙模组host: android 上层协议栈蓝牙模组 通过uart/spi/sdio 等接口,将数据传递给 host 侧。那host 如何解析controller 的数据。是通过 hci 规定的协议。双方才能正常交流。hci 层将数据解析完成后,会根据对应的分类,将数据继续上报给更高层次的协议。例如 两个设备 连接完成后,建立acl 通道。但是此时 两个设备之间 不知道彼此支持那些功能。

奔跑吧 android 的博客 4412

Bluedroid 的代码结构分析

Bluedroid 的代码结构分析 system/bt 的主要文件结构及相应功能介绍如下。 main bte_main.cc 该功能涉及BTE核心栈的初始化和卸载。 bte_main_in_hw_init:负责芯片硬件的初始化 bte_main_boot_entry:调用 GKI_init, bte_init.cc BTE_InitStack:初始化 BTE 控制块,如 RFCOMM、DUN、...

okclouderx的博客 2113

【车载 AOSP 蓝牙(bluedroid) 协议分析 14】【HFP详解 3】【hfp_client 协议连接时序及其状态机切换详解】

本文分析了AOSP中HFP客户端协议栈的状态机切换机制,重点探讨了车载平台与手机建立HFP连接时的流程。文章首先阐述了状态机在HFP协议中的核心作用,包括规范连接生命周期、定位异常行为等。随后通过源码分析,详细说明了从应用层发起连接请求到系统服务处理的完整链路,包括BluetoothDevice接口调用、AdapterService协调以及HeadsetClientService创建状态机的过程。研究状态机切换机制有助于解决车载蓝牙的稳定性问题,但也存在学习成本高、潜在竞态条件等挑战。该分析为定制化开发和故

奔跑吧 android 的博客 395

【HFP Client】音频连接建立机制详解[5]:SCO/eSCO音频连接建立流程解析(bta_hf_client_sco_create)

本文分析了Android Bluetooth协议栈中HFP客户端建立SCO/eSCO音频连接的全链路流程。研究重点包括:1)从应用层请求到底层HCI命令发送的完整处理过程;2)bta_hf_client_sco_create等核心函数的状态管理、参数协商和硬件适配机制;3)CVSD编解码器默认选择的原因及S3/S4配置差异;4)ESCO参数配置流程与数据包类型适配策略;5)系统级资源协调机制。研究为蓝牙音频开发提供了协议栈层面的深度参考,揭示了语音连接建立的复杂性和关键技术点。

byte轻骑兵的技术小窝 1万+

BlueDroid HFP消息传递机制详解:从bta_sys_sendmsg到事件处理的完整链路

本文深入解析了Android蓝牙协议栈BlueDroid中HFP(Hands-Free Profile)客户端的消息传递机制。详细阐述了从`bta_sys_sendmsg`发起事件投递,经由`bta_sys_event`进行系统级路由,最终在表驱动状态机`bta_hf_client_sm_execute`中完成精确处理的完整链路。通过源码分析,揭示了事件封装、跨线程通信、状态机流转以及多实例并发控制的核心设计,为深入理解或定制开发蓝牙协议栈提供了关键洞见。

pluto的博客 353

【Bluedroid】A2dp Source播放流程源码分析(8):Android蓝牙A2DP流启动与offload机制解析(btif_a2dp_on_started)

本文基于 Android 蓝牙协议栈核心代码(BTIF、BTA、HAL、UIPC 等模块),深入分析 A2DP(高级音频分发协议)流启动的完整逻辑。

byte轻骑兵的技术小窝 3526

蓝牙分类专栏目录

蓝牙分类专栏目录1.传统蓝牙协议2.低功耗蓝牙协议3. Android BLE从理论到实战4.蓝牙mesh协议5.Android蓝牙mesh APP实战6.蓝牙AOA/AOD7.蓝牙LE Audio和LC3编解码8.跟着Andger学蓝牙,每天进步一点点 1.传统蓝牙协议 2.低功耗蓝牙协议 3. Android BLE从理论到实战 链接 4.蓝牙mesh协议 5.Android蓝牙mesh APP实战 6.蓝牙AOA/AOD 7.蓝牙LE Audio和LC3编解码 8.跟着Andger学蓝牙,每天进步一点

andger032的博客 1061

Android 蓝牙配对信息 link key存储路径

android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程 Android 蓝牙配对连接源码分析文档大全 - 点击下载 蓝牙配对是整个蓝牙连接过程中必不可少的环节,配对的目的是通过各种方法创建共享的链路密钥(Link Key),该密钥用于设备间连接时认证鉴权并加密相互交互的数据,使得蓝牙技术更具有安全行。密钥不但可以用于当前的连接中,还可以用于两个设备后续的重连过程中,但是鉴权过程中如果比较双方的Link Key不一致,则必须重新开始配对流程,从而创......

手机-手表蓝牙开发工程师的博客 1570

bluedroid协议栈源代码

google在新版本的android系统中,用博通开源蓝牙协议栈bluedroid取代了古老强大的bluez。 本资源为bluedroid源代码,供大家学习参考。

Android 蓝牙配对信息 link key存储路径和修改蓝牙默认名称

android-蓝牙A2dp-avrcp-hfp-opp-配对流程-ble-rfcomm源码流程 Android 蓝牙配对连接源码分析文档大全 - 点击下载 蓝牙配对是整个蓝牙连接过程中必不可少的环节,配对的目的是通过各种方法创建共享的 链路密钥(Link Key),该密钥用于设备间连接时认证鉴权并加密相互交互的数据,使得蓝牙技术更具有安全行。密钥不但可以用于当前的连接中,还可以用于两个设备后续的重连过程中,但是鉴权过程中如果比较双方的Link Key不一致,则必须重新开始配对流程,从而创建新的链路密钥(

行者-崛起之路 6026

嵌入式开发避坑指南:用DWT做延时函数,这几个细节不注意代码会跑飞

本文深入探讨了在Cortex-M嵌入式开发中使用DWT实现精确延时函数的关键技巧与常见陷阱。针对DWT_CYCCNT计数器应用中的内核兼容性、溢出处理、编译器优化等五大核心问题,提供了工业级稳健解决方案,帮助开发者避免代码跑飞风险,实现微秒级高精度延时控制。

weixin_42653691的博客 158

Android6.0 Bluetooth学习

蓝牙 bluetooth-之一 http://blog.youkuaiyun.com/shichaog/article/details/51931898 蓝牙bluetooth之二-源码分布 http://blog.youkuaiyun.com/shichaog/article/details/52066906 蓝牙之三-StateMachine http://blog.cs

788

一篇NS SAR ADC的学习记录

一篇NS SAR ADC的学习记录 内容包含: OverSampling Noise Shaping Dynamic Amplifier DAC with Redundancy 最近看了些有意思的SAR ADC结构,但是因为确实对模拟这个领域不太熟,导致了一系列的级联查找,最终达到了一个大概能懂的状态,为了理清思路,顺便别让看了这么些天的东西浪费了,还是计划写篇文章。 ps:由于不是一篇特别正紧的报告,参考文献就写的比较随便,且我还是个做数字的 A 13.5-ENOB, 107-μW Noise-Sha

weixin_45669724的博客 8708

【Python机器学习实战】 | 基于PCA主成分分析技术读入空气质量监测数据进行数据预处理并计算空气质量综合评测结果

主成分分析(Principal Component Analysis, PCA)是一种常用的机器学习和数据分析技术,用于降低数据维度、识别数据中的模式、发现变量之间的关系等。

一伦明悦的博客 1262
上一篇: Bluetooth L2CAP 学习
下一篇: Bluetooth OBEX&PBAP分析 (1)-协议
leoStack
博客等级 码龄11年 16粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值