A2DP连接在安卓系统中的实现


A2DP连接在安卓系统中的实现

文章插图
 
在上篇《蓝牙音乐之A2DP》中简要介绍完了A2DP音频流建立过程的协议分析 , 该过程主要通过协商双方的编码方式信息并设置一个最优的编码方式 , 最后通过AVDTP_OPEN建立A2DP连接 。此连接最重要的是建立信令通道和数据通道这两条L2CAP链路 。这篇主要和大家分享下Android/ target=_blank class=infotextkey>安卓系统中如何实现A2DP的连接 。
安卓源码版本:Android-9(P版本)
A2DP连接涉及到应用层、蓝牙服务层、蓝牙协议栈及蓝牙芯片等各个模块 。由于蓝牙芯片因各厂家的实现方式不一致(主要是没有代码实现资源) , 暂不分析 , 接下来主要介绍下其他模块在蓝牙音乐之A2DP连接过程中的作用 。
应用层:控制连接的发起或结束、连接状态的监听 。
A2DP协议的两端SRC和SNK都可以发起A2DP的连接 , 所以根据自己蓝牙设备的角色正确选择安卓提供的接口函数 。
SRC 端通过BluetoothA2dp. connect()发起整个A2DP连接 , 注册如下系统广播监听连接状态的改变:
A2DP连接在安卓系统中的实现

文章插图
 
SNK 端通过BluetoothA2dpSink. connect() , 注册如下广播监听A2DP的连接状态:
A2DP连接在安卓系统中的实现

文章插图
 
蓝牙服务层:A2DP连接状态机的切换 , 状态机切换时向系统中发送A2DP协议连接状态改变的广播 。
蓝牙协议栈层:协议规定的主要工作都在这一层完成 , 是整个A2DP连接的关键 , 也是本篇文章的分析重点 。
老读者想必都发现了这么件事 , 每次分析蓝牙相关协议在安卓中的实现基本上都是以Client(Sink)端为分析点 , 其实和Server(Source)端没啥大的区别 。本次A2DP连接还是以SNK端的角色为分析点展开 , SRC端的分析大同小异 。
简要的A2DP连接时序图如下:
A2DP连接在安卓系统中的实现

文章插图
【A2DP连接在安卓系统中的实现】 
从以上时序图明显可以看出A2DP连接的大部分工作都是在协议栈中完成的 , 连接的过程环环相扣 , 缺一不可 。A2DP连接在协议栈具体怎么跑起来我这里就不做过多分析 , 感兴趣的同学可以按照上面的时序图分析 , 基本上也就明白其中的逻辑了 。但还是有几个关键点需要提出来方便大家的学习 。
A2DP的连接在协议栈的行为实际上是AVDTP协议的连接 , 所以发起连接前需要进行SDP服务 , 搜索发现对端是否支持A2DP协议以及AVDTP的版本号
A2DP连接在安卓系统中的实现

文章插图
 
AVDTP的连接包括信令通道和数据通道的建立 , 而信令通道主要包含如下四个步骤:
  1. AVDTP_DISCOVER
  2. AVDTP_GET_CAPABILITIES(依次获取Discovery发现的每种编码方式的能力 , 但是AVDTP的1.3版本新增一个功能:一次性获取所有编码方式的能力AVDTP_GET_ALL_CAPABILITIES , 更加方便)
  3. AVDTP_SET_CONFIGURATION
  4. AVDTP_OPEN

A2DP连接在安卓系统中的实现

文章插图
 
数据通道就是简单建立一条L2CAP链路 , 以后蓝牙音乐的音频数据就在上面传输 。
A2DP连接在安卓系统中的实现

文章插图
 
协议栈在 AVDTP_GET_CAPABILITIES 获取完所有对端支持的编码方式的能力后 , 选取双方都支持且最优的编码方式作为本次A2DP连接的编码方式 。并且通过函数 btif_a2dp_sink_update_decoder() 更新SNK端的音频解码器的配置 , 根据配置创建 AudioTrack , SNK端接收到的蓝牙音乐音频数据通过该AudioTrack发送到安卓音频系统中播放 。
最后A2DP连接成功上报蓝牙服务层A2DP的连接状态切换到Connected后 , 协议栈会判断本端的蓝牙设备是否为Sink端 , 如果是Sink端的话 , 协议栈主动发起AVRCP的连接 , 因此安卓系统中的蓝牙API中没有AVRCP的连接 , 这块的知识点等以后分享再做具体分析 。
本篇A2DP协议的连接在安卓系统中的实现就分享到这里 , 感兴趣的小伙伴欢迎私信留言一起讨论 , 共同学习 , 一起进步!


推荐阅读