关键词:蓝牙blueZ A2DP、SINK、sink_connect、sink_disconnect、sink_suspend、sink_resume、sink_is_connected、sink_get_properties、AUDIO、DBUS
版本:基于android4.2之前版本 bluez
内核:linux/linux3.08
系统:android/android4.1.3.4
作者:xubin341719(欢迎转载,请注明作者,请尊重版权谢谢)
欢迎指正错误,共同学习、共同进步!!

Android bluetooth介绍(一):基本概念及硬件接口
Android bluetooth介绍(二): android 蓝牙代码架构及其uart 到rfcomm流程
Android bluetooth介绍(三): 蓝牙扫描(scan)设备分析
Android bluetooth介绍(四): a2dp connect流程分析

一、A2DP_CONNECT上层代码流程

二、从HCI log中看AVDTP 创建过程
1AVDTP l2cap建立过程

2AVDTP相关信令处理流程在HCI 中的流程

DISCOVER \GET_CAPABILITIES\SET_CONFIGURATION\OPEN\START\SUSPEND
三、audiosink函数注册、及命令处理流程
AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
(一)、sink_connect创建流程
整体流程如下所示


1、idh.code\external\bluetooth\bluez\audio\sink.c

static DBusMessage *sink_connect(DBusConnection *conn,DBusMessage *msg, void *data){…………if (!sink->session)//(1)、如果没有AVDTP会话,获取AVDTP连接状态;sink->session = avdtp_get(&dev->src, &dev->dst);if (!sink->session)//相关失败操作return btd_error_failed(msg, "Unable to get a session");if (sink->connect || sink->disconnect)//如果正在连接、断开,发送busy消息;return btd_error_busy(msg);if (sink->stream_state >= AVDTP_STATE_OPEN)//如果已经打开,发送已经连接消息;return btd_error_already_connected(msg);if (!sink_setup_stream(sink, NULL))//(2)、创建AVDTP流;return btd_error_failed(msg, "Failed to create a stream");dev->auto_connect = FALSE;pending = sink->connect;pending->conn = dbus_connection_ref(conn);//(3)、保存客户端dbus信息;pending->msg = dbus_message_ref(msg);DBG("stream creation in progress");return NULL;}

(1)、如果没有AVDTP会话,获取AVDTP连接状态;

sink->session = avdtp_get(&dev->src, &dev->dst);idh.code\external\bluetooth\hcidump\parser\avdtp.cstruct avdtp *avdtp_get(bdaddr_t *src, bdaddr_t *dst){………………session = avdtp_get_internal(src, dst);………………}avdtp_get_internal 中设置 session->state状态,session->state = AVDTP_SESSION_STATE_DISCONNECTED;

(2)、创建AVDTP流;
sink_setup_stream(sink,NULL)
idh.code\external\bluetooth\hcidump\parser\avdtp.c

gboolean sink_setup_stream(struct sink *sink, struct avdtp *session){…………avdtp_set_auto_disconnect(sink->session, FALSE);//不能自动断开;if (avdtp_discover(sink->session, discovery_complete, sink) < 0)//调用avdtp_discover,discovery_complete为回调函数;return FALSE;sink->connect = g_new0(struct pending_request, 1);return TRUE;}

idh.code\external\bluetooth\hcidump\parser\avdtp.c

int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,void *user_data){int err;if (session->discov_cb)return -EBUSY;if (session->seps) {session->discov_cb = cb;session->user_data = user_data;g_idle_add(process_discover, session);return 0;}err = send_request(session, FALSE, NULL, AVDTP_DISCOVER, NULL, 0);//发送AVDTP_DISCOVER命令出去if (err == 0) {session->discov_cb = cb;session->user_data = user_data;}return err;}
idh.code\external\bluetooth\hcidump\parser\avdtp.c
static int send_request(struct avdtp *session, gboolean priority,struct avdtp_stream *stream, uint8_t signal_id,void *buffer, size_t size){struct pending_req *req;if (stream && stream->abort_int && signal_id != AVDTP_ABORT) {DBG("Unable to send requests while aborting");return -EINVAL;}req = g_new0(struct pending_req, 1);req->signal_id = signal_id;req->data = g_malloc(size);memcpy(req->data, buffer, size);req->data_size = size;req->stream = stream;return send_req(session, priority, req);//这个函数我们后面分析;}

(3)、保存客户端dbus信息;

pending->conn = dbus_connection_ref(conn);pending->msg = dbus_message_ref(msg);

2、send_req 创建L2CAP连接
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static int send_req(struct avdtp *session, gboolean priority,struct pending_req *req){static int transaction = 0;int err;if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//如果AVDTP没有连接,session->io = l2cap_connect(session);//(1)、创建l2cap连接;if (!session->io) {err = -EIO;goto failed;}avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);}if (session->state < AVDTP_SESSION_STATE_CONNECTED ||session->req != NULL) {//如果AVDTP没连接queue_request(session, req, priority);//把相关参数放入队列return 0;//在这里返回,后面AVDTP sock建立完成后,会再次调用这个函数;}req->transaction = transaction++;transaction %= 16;/* FIXME: Should we retry to send if the bufferwas not totally sent or in case of EINTR? */if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,req->signal_id, req->data, req->data_size)) {//(2)、发送相关命令err = -EIO;goto failed;}…………}

(1)、创建l2cap连接
sink connect的过程本质上是建立一个avdtp 连接的过程,avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以这个图纸表示了建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送AVDPT_DISCOVER的请求;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

session->io = l2cap_connect(session);static GIOChannel *l2cap_connect(struct avdtp *session){GError *err = NULL;GIOChannel *io;io = bt_io_connect(BT_IO_L2CAP, avdtp_connect_cb, session,NULL, &err,BT_IO_OPT_SOURCE_BDADDR, &session->server->src,BT_IO_OPT_DEST_BDADDR, &session->dst,BT_IO_OPT_PSM, AVDTP_PSM,BT_IO_OPT_INVALID);if (!io) {error("%s", err->message);g_error_free(err);return NULL;}return io;}

这个函数中注意两点,1)、bt_io_connect;2)、avdtp_connect_cb回调函数;
1)、bt_io_connect
idh.code\external\bluetooth\bluez\btio\btio.c

GIOChannel *bt_io_connect(BtIOType type, BtIOConnect connect,gpointer user_data, GDestroyNotify destroy,GError **gerr, BtIOOption opt1, ...){…………io = create_io(type, FALSE, &opts, gerr);if (io == NULL)return NULL;sock = g_io_channel_unix_get_fd(io);switch (type) {case BT_IO_L2RAW:err = l2cap_connect(sock, &opts.dst, 0, opts.cid);break;//不同协议的连接,如L2CPA、RFCOMM、SCOcase BT_IO_L2CAP:err = l2cap_connect(sock, &opts.dst, opts.psm, opts.cid);break;case BT_IO_RFCOMM:err = rfcomm_connect(sock, &opts.dst, opts.channel);break;case BT_IO_SCO:err = sco_connect(sock, &opts.dst);break;…………connect_add(io, connect, user_data, destroy);return io;}

Btio中l2cap_connect的实现:
idh.code\external\bluetooth\bluez\btio\btio.c

static int l2cap_connect(int sock, const bdaddr_t *dst,uint16_t psm, uint16_t cid){int err;struct sockaddr_l2 addr;memset(&addr, 0, sizeof(addr));addr.l2_family = AF_BLUETOOTH;bacpy(&addr.l2_bdaddr, dst);if (cid)addr.l2_cid = htobs(cid);elseaddr.l2_psm = htobs(psm);err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));//建立BTPROTO_L2CAPif (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS))return err;return 0;}

2)、avdtp_connect_cb回调函数
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data){………………if (session->state == AVDTP_SESSION_STATE_CONNECTING) {//如果处于正在连接状态;DBG("AVDTP imtu=%u, omtu=%u", session->imtu, session->omtu);session->buf = g_malloc0(session->imtu);avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTED);//设置AVDTP状态为已经连接状态;if (session->io_id)g_source_remove(session->io_id);/* This watch should be low priority since otherwise the * connect callback might be dispatched before the session * callback if the kernel wakes us up at the same time for * them. This could happen if a headset is very quick in * sending the Start command after connecting the stream * transport channel. */session->io_id = g_io_add_watch_full(chan,G_PRIORITY_LOW,G_IO_IN | G_IO_ERR | G_IO_HUP| G_IO_NVAL,(GIOFunc) session_cb, session,NULL);………………process_queue(session);//发送DISCOVERreturn;…………}

3、process_queue(session)发送DISCOVER命令出去
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static int process_queue(struct avdtp *session){…………*queue = g_slist_remove(*queue, req);return send_req(session, FALSE, req);}

这个函数调用send_req,这个函数前面已经调用过,可是现在AVDTP的状态不同,第一次调用AVDTP_SESSION_STATE_DISCONNECTED状态,第二次调用为

AVDTP_SESSION_STATE_CONNECTED状态;
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static int send_req(struct avdtp *session, gboolean priority,struct pending_req *req){static int transaction = 0;int err;if (session->state == AVDTP_SESSION_STATE_DISCONNECTED) {//第二次调用时,就不走这段函数session->io = l2cap_connect(session);if (!session->io) {err = -EIO;goto failed;}avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);}if (session->state < AVDTP_SESSION_STATE_CONNECTED ||//第二次调用也越过这段函数session->req != NULL) {queue_request(session, req, priority);return 0;}req->transaction = transaction++;transaction %= 16;/* FIXME: Should we retry to send if the bufferwas not totally sent or in case of EINTR? */if (!avdtp_send(session, req->transaction, AVDTP_MSG_TYPE_COMMAND,req->signal_id, req->data, req->data_size)) {//avdtp_send就是主要的操作err = -EIO;goto failed;}

4、avdtp_send的实现
idh.code\external\bluetooth\hcidump\parser\avdtp.c

static gboolean avdtp_send(struct avdtp *session, uint8_t transaction,uint8_t message_type, uint8_t signal_id,void *data, size_t len){…………     /* Send the start packet */memset(&start, 0, sizeof(start));start.transaction = transaction;start.packet_type = AVDTP_PKT_TYPE_START;start.message_type = message_type;start.no_of_packets = cont_fragments + 1;start.signal_id = signal_id;memcpy(session->buf, &start, sizeof(start));memcpy(session->buf + sizeof(start), data,session->omtu - sizeof(start));if (!try_send(sock, session->buf, session->omtu))return FALSE;………………cont.message_type = message_type;memcpy(session->buf, &cont, sizeof(cont));memcpy(session->buf + sizeof(cont), data + sent, to_copy);if (!try_send(sock, session->buf, to_copy + sizeof(cont)))return FALSE;sent += to_copy;}return TRUE;} 

5、Try_sends函数的实现

static gboolean try_send(int sk, void *data, size_t len){int err;do {err = send(sk, data, len, 0);} while (err < 0 && errno == EINTR);if (err < 0) {error("send: %s (%d)", strerror(errno), errno);return FALSE;} else if ((size_t) err != len) {error("try_send: complete buffer not sent (%d/%zu bytes)",err, len);return FALSE;}return TRUE;}

(二)、AVDTP_DISCOVER的命令发送流程如上图所示;
avdtp是基于l2cap的,包括控制命令的发送和数据的发送都是l2cap的,所以建立一个发送控制命令的l2cap的socket,等这个socket建立起来以后,开始发送AVDPT_DISCOVER的请求;|
`AVDTP_DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:等一系列控制命令
建立了一个l2cap的连接,等有数据过来的时候,就开始触发逻辑,session_cb是一个非常重要的函数,这里控制了整个连接的流程,我们下面会讲,剩下的就是通过avdtp_send来发送一个AVDTP_DISCOVER的命令,这个命令的作用就是查看远程设备看它支持那些sep(stream end point),也就是说是否支持source,sink等;
四、AVDTP_GET_CAPABILITIES命令发送(其他代码流程比较类似)
如下图所示:

这个图在发送了avdtp discover命令以后,会被先前设立好的回调函数执行,里面会把远程设备的sep都加入到session的seps连边里面去,然后开始发送AVDTP_GET_CAPABILITIES命令了;
当收到远端设备的回复消息后触发调用下面的逻辑:

在系列初始化、状态设定之后,发送哦AVDTP_SET_CONFIGURATION
五、AVDTP_SET_CONFIGURATION命令发送

发送AVDTP_OPEN命令;
六、AVDTP_OPEN的处理流程
到这里就表示已经确立了sep和caps,开始打开AVDTP了,如下:

数stream_setup_complete里面会对先前的dbus消息进行回复;
七、AVDTP_START命令发送

这里发送AVDTP_START的命令,它的触发是由客户端引起的,比如aplay –Dbluetooth 2.wav的时候通过alsa提供的bluetooth的插件,daemonbluetoothd-service-audio通过socket(PF_LOCAL, SOCK_STREAM,0);建立起一个socket来监听客户端的接入,触发server_cb的执行,在这里accept客户端,并设置监听函数client_cb,当收到客户端的启动流播放命令的时候就开始调用avdtp_start函数来发送命令,注意这里设置了一个回调函数a2dp_resume_complete,后面会被调用;当bluetoothd-service-audio收到了这个命令AVDTP_START的响应消息时执行下面的逻辑:

进程间传递文件描述符,内核层里面的实现,通过socket发送这个文件描述符,在内核里面把struct file信息传递给socket的peer端,它再取得一个空的fd把它和struct file关联起来,于是就实现了文件描述符传递。







更多相关文章

  1. 箭头函数的基础使用
  2. Python技巧匿名函数、回调函数和高阶函数
  3. Android(安卓)命令行编译、打包生成apk文件
  4. Ionic 运行报错No resource identifier found for attribute 'ap
  5. Android架构分析之使用自定义硬件抽象层(HAL)模块
  6. Android(安卓)开发中的倒计时
  7. 使用sencha cmd创建android应用
  8. Android执行shell命令
  9. Android中OpenMax的适配层

随机推荐

  1. Android闹钟及相关组件
  2. Android(安卓)转场动画使用,所遇到的坑
  3. android OpenGLES开发第二课 为Polygon添
  4. Android百度地图开发(五)公交线路详情搜索
  5. Unable to resolve target 'android-XX'
  6. 利用 Android(安卓)Studio 阅读 Android(
  7. Android(安卓)中文 API (101) —— Async
  8. Android(安卓)Log介绍
  9. 谈谈Android中的SurfaceTexture
  10. 关于使用腾讯云腾讯(即时通讯SDK)遇到的问