Android bluetooth introduction (iv): a2dp connect process analysis, androida2dp
Key words: Bluetooth blueZ A2DP, SINK, sink_connect, sink_disconnect, sink_suspend, sink_resume,Sink_is_connected, sink_get_properties, AUDIO, AUDIO
Version: Based on bluez, Which is earlier than Android
Kernel:Linux/linux3.08
System:Android/android4.1.3.4
Author: xubin341719 (You are welcome to reprint it. Please indicate the author. Please respect copyright. Thank you)
Correct the mistakes and learn and make progress together !!
Android bluetooth introduction (1): Basic Concepts and hardware interfaces
Android bluetooth introduction (II): android bluetooth code architecture and the process from uart to rfcomm
Introduction to Android bluetooth (III): Analysis of bluetooth scan Devices
Android bluetooth introduction (iv): a2dp connect Process Analysis
I. A2DP_CONNECT upper-Layer Code Process
Ii. SlaveHCI logViewAVDTPCreation process
1,AVDTP l2capCreation process
2,AVDTPThe relevant signaling processing process is inHCIProcess in
DISCOVER \ GET_CAPABILITIES \ SET_CONFIGURATION \ OPEN \ START \ SUSPEND
Iii. audiosink function registration and command processing process
AVDTP_DISCOVER \ AVDTP_GET_CAPABILITIES \ AVDTP_SET_CONFIGURATION \ AVDTP_OPEN \ AVDTP_START: and other control commands
(1) process for creating sink_connect
The overall process is as follows:
1. idh. code \ external \ bluetooth \ bluez \ audio \ sink. c
Static DBusMessage * sink_connect (DBusConnection * conn, DBusMessage * msg, void * data ){............ If (! Sink-> session) // (1). If there is no AVDTP session, obtain the AVDTP connection status. 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) // if the connection is being established or disconnected, send the busy message; return btd_error_busy (msg); if (sink-> stream_state> = AVDTP_STATE_OPEN) // if the connection is enabled, send the connected message; return btd_error_already_connected (msg); if (! Sink_setup_stream (sink, NULL) // (2) create AVDTP stream; return btd_error_failed (msg, "Failed to create a stream"); dev-> auto_connect = FALSE; pending = sink-> connect; pending-> conn = dbus_connection_ref (conn); // (3) Save the client metadata information; pending-> msg = dbus_message_ref (msg ); DBG ("stream creation in progress"); return NULL ;}
(1) If there is no AVDTP session, obtain the AVDTP connection status;
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 );..................} Set session-> state in avdtp_get_internal, session-> state = AVDTP_SESSION_STATE_DISCONNECTED;
(2) create an AVDTP stream;
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); // automatic disconnection is not allowed; if (avdtp_discover (sink-> session, discovery_complete, sink) <0) // call avdtp_discover, discovery_complete is the callback function; 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); // send the AVDTP_DISCOVER command out 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); // This function is analyzed later ;}
(3) Save the token information of the client;
pending->conn = dbus_connection_ref(conn);pending->msg = dbus_message_ref(msg);
2. Create an L2CAP connection using send_req
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) {// if AVDTP is not connected, session-> io = l2cap_connect (session); // (1), create l2cap connection; 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) {// If AVDTP is not connected to queue_request (session, req, priority); // put the relevant parameters in the queue return 0; // return here, after AVDTP sock is created, this function will be called again;} req-> transaction = transaction ++; transaction % = 16;/* FIXME: shocould 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), send related commands err =-EIO; goto failed ;}............}
(1) Create an l2cap connection
The process of sink connect is essentially the process of establishing an avdtp connection. avdtp is based on l2cap, and control command sending and data sending are both l2cap, therefore, this drawing indicates creating an l2cap socket for sending control commands. After the socket is established, AVDPT_DISCOVER requests are sent;
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;}
Note two points in this function: 1), bt_io_connect; 2), avdtp_connect_cb callback function;
1) bt_io_connect
Idh. code \ external \ bluetooth \ bluez \ 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; // connection of different protocols, such as 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_c Onnect (sock, & opts. dst); break ;............ Connect_add (io, connect, user_data, destroy); return io ;}
Implementation of l2cap_connect in Btio:
Idh. code \ external \ bluetooth \ bluez \ 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); // create BTPROTO_L2CAPif (err <0 &&! (Errno = EAGAIN | errno = EINPROGRESS) return err; return 0 ;}
2) avdtp_connect_cb callback function
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) {// if the connection is in the status; DBG ("AVDTP imtu = % u, omtu = % u", session-> imtu, session-> omtu); session-> buf = g_malloc0 (session-> imtu); avdtp_set_state (session, AVDTP_SESSION_STATE_CONNECTED); // set the AVDTP status to connected; if (session-> io_id) g_source_remove (session-> io_id);/* This watch shocould be low priority since otherwise the * connect callback might be dispatched before Session * callback if the kernel wakes us up at the same time for * them. this cocould happen if a headset is very quick in * sending the Start command after connecting the stream * transport channel. */session-> io_id = require (chan, G_PRIORITY_LOW, G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL, (GIOFunc) session_cb, session, NULL );.................. Process_queue (session); // send DISCOVERreturn ;............}
3. process_queue (session) sends the DISCOVER command.
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);}
This function calls send_req. This function has been called before, but now the status of AVDTP is different. The first call of AVDTP_SESSION_STATE_DISCONNECTED status, the second call is
AVDTP_SESSION_STATE_CONNECTED status;
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) {// during the second call, this function session will not be taken away-> 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 | // The second call also crosses this function session-> req! = NULL) {queue_request (session, req, priority); return 0;} req-> transaction = transaction ++; transaction % = 16;/* FIXME: shocould 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 is the main operation err =-EIO; goto failed ;}
4. avdtp_send implementation
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. Implementation of the Try_sends Function
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;}
(2) shows the AVDTP_DISCOVER command sending process;
Avdtp is based on l2cap, including control command sending and data sending are both l2cap, so an l2cap socket for sending control commands is created. After this socket is established, start sending AVDPT_DISCOVER requests; |
'Avdtp _ DISCOVER\AVDTP_GET_CAPABILITIES\AVDTP_SET_CONFIGURATION\AVDTP_OPEN\AVDTP_START:And other control commands
Establish an l2cap connection and trigger the logic when data comes in. session_cb is a very important function. Here we control the entire connection process. We will talk about it below, the rest is to use avdtp_send to send an AVDTP_DISCOVER command. The purpose of this command is to check the remote device to see which sep (stream end point) it supports, that is, whether it supports source, sink, and so on;
4. Send the AVDTP_GET_CAPABILITIES command (other code flows are similar)
As shown in:
This figure will be executed by the previously established callback function after the avdtp discover command is sent, and the remote device sep will be added to the session seps connection edge, then begin to send the AVDTP_GET_CAPABILITIES command;
After receiving a reply message from a remote device, the following logic is called:
Send AVDTP_SET_CONFIGURATION after series initialization and status setting
5. Send the AVDTP_SET_CONFIGURATION command
Send the AVDTP_OPEN command;
6. AVDTP_OPEN Process
Here, sep and caps have been established and AVDTP is started, as shown below:
Stream_setup_complete replies to the previous response message;
7. Send the AVDTP_START command
The AVDTP_START command is sent here, which is triggered by the client. For example, when aplay-Dbluetooth 2.wav is used, the bluetooth plug-in provided by alsa and daemon1_thd-service-audio are used through socket (PF_LOCAL, SOCK_STREAM, 0); Establish a socket to listen to the access of the client and trigger the execution of server_cb. Here, accept client and set the listening function client_cb, when you receive the start stream Playback command from the client, you can call the avdtp_start function to send the command. Note that a callback function a2dp_resume_complete is set here and will be called later; when mongothd-service-audio receives the response message from this command AVDTP_START, execute the following logic:
The file descriptor is transmitted between processes. The implementation in the kernel layer sends this file descriptor through socket, and the struct file information is transmitted to the peer end of the socket in the kernel, it then acquires an empty fd and associates it with the struct file, so it implements file descriptor transmission.
Android 235 Bluetooth supports A2DP
The full name of A2DP is Advanced Audio Distribution Profile Bluetooth Audio transmission protocol. It has no direct relationship with the mobile phone system.
HTC G11 supports A2DP. We have tested it in practice.
What is Bluetooth 40 + EDR + A2DP supported?
EDR (Enhanced data rate) is the abbreviation of Enhanced rate in Bluetooth technology. It features a significant increase in the data transmission rate of Bluetooth technology, reaching 2.1 Mbps, which is three times the current bluetooth technology. Therefore, in addition to more stable audio stream transmission and lower power consumption, you can also take full advantage of the bandwidth advantage to connect multiple Bluetooth devices at the same time.
A2DP full name is Advanced Audio Distribution Profile Bluetooth Audio transmission model agreement! A2DP uses a chip in the headset to stack data to achieve high sound definition. A headset with A2DP is a Bluetooth stereo headset. The sound can reach 44.1 kHz, and the average earphone can only reach 8 kHz.