編輯:關於Android編程
關鍵詞:藍牙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上層代碼流程
二、從<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjxzdHJvbmc+SENJIGxvZzwvc3Ryb25nPjxzdHJvbmc+1tC/tDwvc3Ryb25nPjxzdHJvbmc+QVZEVFAgPC9zdHJvbmc+PHN0cm9uZz60tL2ouf2zzDxiciAvPjwvc3Ryb25nPjxzdHJvbmc+MTwvc3Ryb25nPjxzdHJvbmc+oaI8L3N0cm9uZz48c3Ryb25nPkFWRFRQIGwyY2FwPC9zdHJvbmc+PHN0cm9uZz69qMGiuf2zzDxiciAvPjxpbWcgc3JjPQ=="/uploadfile/Collfiles/20140818/2014081808491489.png" alt="\" />
2、AVDTP相關信令處理流程在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.c struct 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 buffer was 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、SCO case 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); else addr.l2_psm = htobs(psm); err = connect(sock, (struct sockaddr *) &addr, sizeof(addr));//建立BTPROTO_L2CAP if (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);//發送DISCOVER return; ………… }
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 buffer was 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.ViewPager簡單使用ViewPager是android擴展包android.support.v4 裡的一個繼承與ViewGroup組件,通過布局管理器可以實現左
寫在前面最近一直在做畢設項目的准備工作,考慮到可能要用到一個模糊的效果,所以就學習了一些高斯模糊效果的實現。比較有名的就是 FastBlur 以及它衍生的一些優化方案,還
Android中的數據存儲主要有以下幾種方式: 1、使用SharedPreferences:該存儲方式主要用於應用程序有少量的數據需要保存,而且這些數據的格
你的小米手機准備好迎接MIUI8的到來了嗎?今天小編搶先帶來小米MIUI 8系統更新升級教程,想要升級miui8系統的米粉們快來看看吧!MIUI8升級須知: