在Bluedroid蓝牙协议栈中,A2DP(Advanced Audio Distribution Profile) Sink负责接收来自A2DP Source(如手机、音乐播放器等)的音频流,并将其播放到本地设备(如车载音响、蓝牙耳机等)上。A2DP Sink连接流程涉及多个步骤,包括服务启动、设备发现、连接建立、音频配置和数据传输等。
一、A2DP Sink连接流程
1.1. 初始发现与连接发起阶段
-
设备发现:在A2DP Sink连接之前,蓝牙设备需要处于可被发现的状态。用户通过蓝牙设置界面搜索附近的蓝牙设备,系统发送扫描请求,并更新设备列表。
-
发起
A2DP Sink
连接请求:基于发现的音频源设备信息,上层应用会通过合适的接口向Bluedroid
中的A2DP
相关模块发起连接请求,这一过程涉及到构建连接请求数据结构、设置相关连接参数等操作,告知A2DP
模块要去连接指定的音频源设备。
1.2. 协议交互与配置协商阶段
-
建立
L2CAP
(逻辑链路控制和适配协议)通道:-
A2DP
协议是基于L2CAP
协议之上的,所以首先要建立与音频源设备之间的L2CAP
通道,这个过程涉及到Bluedroid
中L2CAP
模块的相关操作。 -
L2CAP
模块会根据目标设备地址等信息,向蓝牙底层发送连接建立请求,经过蓝牙链路层的一系列交互(如链路建立、参数协商等操作,对应的源码中会有关于链路状态变化、消息发送与接收等相关函数和逻辑处理),最终成功建立起L2CAP
通道,为后续A2DP
协议的交互提供可靠的链路基础。
-
-
A2DP
连接建立与能力协商:-
在
L2CAP
通道建立好之后,A2DP
相关模块开始进行A2DP
连接的建立操作,会向音频源设备发送A2DP
连接请求消息(在源码中可以看到对应的消息结构体构建以及发送函数调用)。 -
对方设备收到请求后会进行响应,双方开始进行
A2DP
能力的协商,比如各自支持的音频编码格式(如SBC
、AAC
等)、声道数量、采样率范围等音频相关的能力信息会通过消息交互进行交换和协商(源码中有对这些能力参数的解析、比较以及选择合适参数的逻辑处理),确定最终在本次连接中使用的音频配置参数,这个协商过程是确保后续音频数据能够正确传输和播放的关键步骤,双方要达成一致的音频配置才能保证兼容性。
-
-
AVDTP
(音频 / 视频分发传输协议)配置与协商:-
A2DP
连接建立后,往往还需要通过AVDTP
协议来进一步配置和协商音频流相关的传输细节,例如音频数据的传输模式(是可靠传输还是尽力传输等模式)、缓冲区大小设置等。 -
在源码中可以看到
AVDTP
模块会发起相应的配置请求消息,与音频源设备进行来回的消息交互,根据对方的响应来调整和确定最终的AVDTP
配置参数,这些参数将直接影响音频数据在链路上的传输效率和稳定性,确保音频数据能够按照合适的规则进行传输,避免出现数据丢失、传输错误等问题。
-
1.3. 解码器初始化与音频轨道准备阶段
-
解码器更新与初始化:
-
一旦
A2DP
和AVDTP
相关的配置协商完成,A2DP Sink
端会接收到包含最终音频配置信息(如确定的音频编码格式等)的数据,此时会触发A2DP Sink
解码器的更新操作。 -
在源码中如
btif_a2dp_sink_update_decoder
这样的函数会被调用,它会根据接收到的编解码器信息来分配内存构建用于解码器更新的结构体,将信息复制进去并设置相应的事件标识; -
然后通过工作线程传递这个结构体去触发解码器的实际更新操作,包括获取解码器接口、进行解码器的初始化以及配置等步骤,确保解码器能够正确解析即将接收到的音频数据,依据协商好的音频编码格式进行解码工作。
-
-
音频轨道创建:
-
在解码器初始化完成后,接着会进行音频轨道的创建工作,像
BtifAvrcpAudioTrackCreate
函数会被调用,它基于协商确定的音频参数(如采样率、声道数等),利用安卓音频 API(如AAudio
)来配置并创建音频流,将音频流及其他相关参数信息封装到自定义的音频轨道结构体中。 -
例如设置音频轨道的缓冲区大小、音量增益初始值等,同时还可能涉及到一些可选的操作如打开用于调试的 PCM 数据输出文件等,最终创建好的音频轨道将用于后续音频数据的播放,为音频数据提供输出的途径。
-
1.4. 音频数据接收与播放阶段
-
音频数据接收与缓冲:
-
在前面的链路建立、配置协商以及解码器和音频轨道准备工作都完成后,
A2DP Sink
端就开始通过已建立的L2CAP
和AVDTP
链路接收来自音频源设备的音频数据了,接收的数据会先缓存在相应的接收缓冲区中(缓冲区的管理在源码中会有对应的分配、读写等操作逻辑以及相关的结构体来维护),等待解码器进行处理。 -
这个过程中要确保数据接收的完整性和顺序性,会有相应的校验和错误处理机制(例如对接收的数据进行校验和验证,若发现错误数据可能会请求重传等操作,这些在源码中都有对应的逻辑处理函数体现)。
-
-
音频数据解码与播放:
-
缓存中的音频数据会被传递给已初始化好的解码器进行解码操作,解码器会按照之前协商确定的音频编码格式将接收到的编码数据转换为可播放的音频样本数据(这一过程在解码器相关的函数实现中会有具体的解码算法和逻辑处理体现);
-
然后将解码后的音频数据写入到之前创建好的音频轨道的缓冲区中,音频轨道会按照设定的采样率、声道数等参数将音频数据输出到音频输出设备(如手机的扬声器或者连接的蓝牙音箱等)进行播放,期间还会涉及到对音频播放状态的监控、音量调节等相关操作(在源码中会有对应的状态更新函数、音量控制接口等相关代码逻辑),以保障音频能够正常、稳定地播放给用户。
-
1.5. 连接状态维护与异常处理阶段
-
连接状态监测与更新:
-
在整个
A2DP Sink
连接过程中,会有专门的模块或者函数(例如通过状态机相关的逻辑,不同状态下对应不同的事件处理函数来维护状态)持续监测连接的状态,比如链路是否正常、是否有数据传输中断、音频播放是否出现卡顿等情况。 -
根据监测到的状态变化情况,会在源码中触发相应的状态更新操作以及通知相关的上层模块或者回调函数(例如向上层应用反馈连接状态变化信息,以便应用能够展示给用户相应的连接提示等),确保整个连接状态在系统中能被准确掌握和及时处理。
-
-
异常处理机制:
-
如果在
A2DP Sink
连接的任何一个阶段出现异常情况,例如链路连接失败、协议协商超时、解码器初始化出错、音频数据接收错误等。 -
源码中都会有相应的异常处理机制启动,包括输出错误日志信息(便于调试查看问题所在,不同的错误情况会记录相应的关键错误信息,如出错的函数调用位置、相关参数等)、尝试重新发起连接或协商操作(根据具体的异常类型和可重试的条件决定是否进行重试以及重试的次数等限制)、向上层应用或用户反馈错误提示(例如在手机界面上显示蓝牙音频连接失败等提示信息,通过相应的接口函数将错误消息传递给上层进行展示)等,以保障整个系统在面对异常时能够尽量恢复正常或者给用户合理的提示反馈,维持系统的稳定性和用户体验。
-
Bluedroid
中A2DP Sink
连接流程源码涵盖了从设备发现到音频播放以及连接状态维护等多个环节的详细逻辑处理,各个环节紧密配合、相互依赖,通过一系列的协议交互、数据处理以及状态管理等操作来实现稳定的蓝牙音频接收和播放功能,并且具备完善的异常处理机制来应对可能出现的各种问题。
1.7. A2DP Sink连接流程中的关键组件
- A2dpSinkService:A2DP Sink服务的主要实现,负责处理连接请求、音频配置和数据传输等任务。
- L2CAP:负责建立逻辑链路控制层连接,为A2DP协议提供数据传输通道。
- AVDTP:负责音频/视频数据的分发和传输,是A2DP协议中用于音频数据传输的关键协议。
1.8. A2DP Sink连接流程中的常见问题及解决方案
A2DP Sink连接流程中,可能会遇到多种问题,这些问题通常与设备兼容性、蓝牙连接稳定性、音频配置以及驱动程序等方面有关。以下是一些常见问题及其解决方案。
1.8.1. 常见问题
-
连接失败:
-
A2DP Sink设备无法与A2DP Source设备成功建立连接。
-
连接过程中断或连接不稳定。
-
-
音频质量差:
-
接收到的音频流出现卡顿、延迟或音质下降。
-
播放音频时出现杂音或失真。
-
-
设备不兼容:
-
A2DP Sink设备与某些A2DP Source设备无法配对或连接。
-
设备间支持的蓝牙版本或编解码器不匹配。
-
-
驱动程序问题:
-
A2DP Sink设备的驱动程序过时或损坏,导致连接或音频传输问题。
-
驱动程序与操作系统或蓝牙协议栈不兼容。
-
-
配置错误:
-
音频配置参数设置不正确,导致音频传输失败或音质受损。
-
蓝牙设备未正确配置为A2DP Sink模式。
-
1.8.2. 解决方案
-
解决连接失败问题:
-
确保A2DP Sink设备和A2DP Source设备均处于可被发现的状态,并靠近彼此以减少信号干扰。
-
尝试重新配对设备,确保输入正确的PIN码或确认配对请求。
-
检查设备的蓝牙版本和编解码器支持情况,确保它们兼容。
-
更新设备的蓝牙驱动程序或固件,以修复可能存在的连接问题。
-
-
提高音频质量:
-
确保A2DP Sink设备和A2DP Source设备之间的蓝牙连接稳定。
-
尝试调整音频配置参数,如采样率、比特率和通道数,以优化音质。
-
检查是否有其他蓝牙设备在附近干扰连接,尝试在干扰较少的环境中使用设备。
-
如果可能,使用更高版本的蓝牙技术(如蓝牙5.4或更高版本)以提高音频传输的稳定性和质量。
-
-
解决设备不兼容问题:
-
查阅设备的兼容性列表或说明文档,确保A2DP Sink设备与A2DP Source设备兼容。
-
如果设备间存在蓝牙版本或编解码器不匹配的问题,尝试更新设备的固件或驱动程序以支持更广泛的兼容性。
-
考虑使用蓝牙适配器或转换器来桥接不兼容的设备。
-
-
更新驱动程序:
-
访问设备制造商的官方网站,下载并安装最新版本的蓝牙驱动程序。
-
在安装新驱动程序之前,请确保备份旧驱动程序以防万一。
-
如果驱动程序更新后问题仍未解决,请尝试卸载并重新安装驱动程序。
-
-
正确配置设备:
-
确保A2DP Sink设备已正确配置为接收音频的模式(A2DP Sink模式)。
-
检查设备的音频配置参数设置是否正确,并根据需要进行调整。
-
如果设备支持多种蓝牙配置文件(如A2DP、AVRCP等),请确保已启用A2DP配置文件以接收音频流。
-
A2DP Sink连接流程中的常见问题通常可以通过确保设备兼容性、更新驱动程序、正确配置设备以及优化蓝牙连接稳定性等方法来解决。
二、源码分析
在 Bluedroid协议栈中
,sink_connect_src
函数用于处理 A2DP Sink和A2DP Source
建立连接的相关操作。作为一个对外暴露的接口,为上层应用、蓝牙管理模块或者其他相关的调用者提供了便捷的方式来触发和控制 A2DP Sink
与音频源之间连接的建立流程。
Bluedroid协议栈的A2dp Sink的连接流程我们从sink_connect_src 函数开始分析。(说明:源码基于Android 14分析)。
sink_connect_src
packages/modules/Bluetooth/system/btif/src/btif_av.cc
static bt_status_t sink_connect_src(const RawAddress& peer_address) {
log::info("Peer {}", ADDRESS_TO_LOGGABLE_CSTR(peer_address));
if (!btif_av_sink.Enabled()) {
log::warn("BTIF AV Sink is not enabled");
return BT_STATUS_NOT_READY;
}
RawAddress peer_address_copy(peer_address);
return btif_queue_connect(UUID_SERVCLASS_AUDIO_SINK, &peer_address_copy,
connect_int);
}
sink_connect_src
函数主要是尝试与指定的蓝牙设备(源设备)建立音频接收(Sink)连接。通过检查A/V Sink功能是否启用,如果启用,则尝试与指定的蓝牙设备建立音频接收连接。并通过btif_queue_connect
函数发起连接请求。
btif_queue_connect
packages/modules/Bluetooth/system/btif/src/btif_profile_queue.cc
/*******************************************************************************
*
* Function btif_queue_connect
*
* Description Add a new connection to the queue and trigger the next
* scheduled connection.
*
* Returns BT_STATUS_SUCCESS if successful
*
******************************************************************************/
bt_status_t btif_queue_connect(uint16_t uuid, const RawAddress* bda,
btif_connect_cb_t connect_cb) {
return do_in_jni_thread(
FROM_HERE, base::BindOnce(&queue_int_add, uuid, *bda, connect_cb));
}
btif_queue_connect
用于将一个新的连接添加到连接队列中,并且触发下一个已计划好的连接操作。若操作成功,会返回 BT_STATUS_SUCCESS
,以此告知调用者此次添加连接到队列并触发相关后续操作的过程是顺利完成的。 通过借助 do_in_jni_thread
函数,将具体的添加连接到队列以及可能涉及的后续连接触发操作委托给了 queue_int_add
函数在JNI线程环境下去执行,
queue_int_add
packages/modules/Bluetooth/system/btif/src/btif_profile_queue.cc
/*******************************************************************************
* Queue helper functions
******************************************************************************/
static void queue_int_add(uint16_t uuid, const RawAddress& bda,
btif_connect_cb_t connect_cb) {
// Sanity check to make sure we're not leaking connection requests
CHECK(connect_queue.size() < MAX_REASONABLE_REQUESTS);
ConnectNode param(bda, uuid, connect_cb);
for (const auto& node : connect_queue) {
if (node.uuid() == param.uuid() && node.address() == param.address()) {
log::error("Dropping duplicate profile connection request:{}",
param.ToString());
return;
}
}
log::info("Queueing profile connection request:{}", param.ToString());
connect_queue.push_back(param);
btif_queue_connect_next();
}
queue_int_add
函数属于队列相关的辅助函数,负责将新的连接请求添加到队列中,并在添加之前进行合理性检查和重复请求检查。通过维护一个连接请求队列,并确保每个请求都是唯一的,该函数能够有效地管理多个并发或顺序的连接尝试。
btif_queue_connect_next
packages/modules/Bluetooth/system/btif/src/btif_profile_queue.cc
bt_status_t btif_queue_connect_next(void) {
// The call must be on the JNI thread, otherwise the access to connect_queue
// is not thread-safe.
CHECK(is_on_jni_thread());
if (connect_queue.empty()) return BT_STATUS_FAIL;
if (!stack_manager_get_interface()->get_stack_is_running())
return BT_STATUS_FAIL;
ConnectNode& head = connect_queue.front();
log::info("Executing profile connection request:{}", head.ToString());
bt_status_t b_status = head.connect();
if (b_status != BT_STATUS_SUCCESS) {
log::info("connect {} failed, advance to next scheduled connection.",
head.ToString());
btif_queue_advance();
}
return b_status;
}
btif_queue_connect_next
函数负责处理队列中的下一个连接请求。通过确保调用在JNI线程上进行、检查队列是否为空、检查蓝牙协议栈是否运行以及处理连接结果,能够有效地管理蓝牙连接请求。如果连接失败,它将自动推进队列并尝试下一个请求。
connect
packages/modules/Bluetooth/system/btif/src/btif_profile_queue.cc
/**
* Initiate the connection.
*
* @return BT_STATUS_SUCCESS on success, othewise the corresponding error
* code. Note: if a previous connect request hasn't been completed, the
* return value is BT_STATUS_SUCCESS.
*/
bt_status_t connect() {
if (busy_) return BT_STATUS_SUCCESS;
busy_ = true;
return connect_cb_(&address_, uuid_);
}
connect
方法是一个用于发起蓝牙连接请求的成员函数,它通过检查 busy_
变量来避免并发连接请求,并调用回调函数connect_cb_
来执行实际的连接操作。
connect_int
packages/modules/Bluetooth/system/btif/src/btif_av.cc
// Establishes the AV signalling channel with the remote headset
static bt_status_t connect_int(RawAddress* peer_address, uint16_t uuid) {
log::verbose("peer_address={} uuid=0x{:x}",
ADDRESS_TO_LOGGABLE_CSTR(*peer_address), uuid);
if (btif_av_both_enable()) {
const RawAddress tmp = *peer_address;
if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) {
btif_av_source_dispatch_sm_event(tmp, BTIF_AV_CONNECT_REQ_EVT);
} else if (uuid == UUID_SERVCLASS_AUDIO_SINK) {
btif_av_sink_dispatch_sm_event(tmp, BTIF_AV_CONNECT_REQ_EVT);
}
return BT_STATUS_SUCCESS;
}
auto connection_task = [](RawAddress* peer_address, uint16_t uuid) {
BtifAvPeer* peer = nullptr;
if (uuid == UUID_SERVCLASS_AUDIO_SOURCE) {
peer = btif_av_source.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
} else if (uuid == UUID_SERVCLASS_AUDIO_SINK) {
peer = btif_av_sink.FindOrCreatePeer(*peer_address, kBtaHandleUnknown);
}
if (peer == nullptr) {
btif_queue_advance();
return;
}
peer->StateMachine().ProcessEvent(BTIF_AV_CONNECT_REQ_EVT, nullptr);
};
bt_status_t status = do_in_main_thread(
FROM_HERE, base::BindOnce(connection_task, peer_address, uuid));
if (status != BT_STATUS_SUCCESS) {
log::error("can't post connection task to main_thread");
}
return status;
}
connect_int
函数用于与远程音频设备建立A/V信令通道。首先检查A/V功能是否启用,并根据UUID的类型分发连接请求事件。如果A/V功能未启用,则定义一个异步任务,在主线程中查找或创建 BtifAvPeer
对象,并向其状态机发送连接请求事件。函数返回操作的状态码,以指示连接请求是否成功发布到主线程。
检查A/V是否启用:调用
btif_av_both_enable()
检查A/V音频和视频功能是否都已启用。如果已启用,则根据UUID的类型(音频源或音频接收器),分别向相应的状态机(Source或Sink)分发连接请求事件(BTIF_AV_CONNECT_REQ_EVT
)。异步连接任务:如果A/V功能未启用,则定义一个
connection_task
的lambda函数,用于在主线程中执行连接任务。这个lambda函数会根据UUID查找或创建对应的BtifAvPeer
对象,并向其状态机发送连接请求事件。在主线程中执行任务:通过调用
do_in_main_thread
函数,将connection_task
lambda函数绑定到主线程执行。如果任务成功发布到主线程,函数返回BT_STATUS_SUCCESS
;否则,记录错误日志。
BtifAvStateMachine::StateIdle::ProcessEvent(BTIF_AV_CONNECT_REQ_EVT)
packages/modules/Bluetooth/system/btif/src/btif_av.cc
bool BtifAvStateMachine::StateIdle::ProcessEvent(uint32_t event, void* p_data) {
log::verbose("Peer {} : event={} flags={} active_peer={}",
ADDRESS_TO_LOGGABLE_CSTR(peer_.PeerAddress()),
BtifAvEvent::EventName(event), peer_.FlagsToString(),
logbool(peer_.IsActivePeer()));
switch (event) {
...
case BTIF_AV_CONNECT_REQ_EVT:
case BTA_AV_PENDING_EVT: {
bool can_connect = true;
peer_.SetSelfInitiatedConnection(event == BTIF_AV_CONNECT_REQ_EVT);
// Check whether connection is allowed
if (peer_.IsSink()) {
can_connect = btif_av_source.AllowedToConnect(peer_.PeerAddress());
if (!can_connect) src_disconnect_sink(peer_.PeerAddress());
} else if (peer_.IsSource()) {
can_connect = btif_av_sink.AllowedToConnect(peer_.PeerAddress());
if (!can_connect) sink_disconnect_src(peer_.PeerAddress());
}
if (!can_connect) {
log::error("Cannot connect to peer {}: too many connected peers",
ADDRESS_TO_LOGGABLE_CSTR(peer_.PeerAddress()));
if (peer_.SelfInitiatedConnection()) {
btif_queue_advance();
}
break;
}
btif_av_query_mandatory_codec_priority(peer_.PeerAddress());
BTA_AvOpen(peer_.PeerAddress(), peer_.BtaHandle(), true,
peer_.LocalUuidServiceClass());
peer_.StateMachine().TransitionTo(BtifAvStateMachine::kStateOpening);
if (event == BTIF_AV_CONNECT_REQ_EVT) {
DEVICE_IOT_CONFIG_ADDR_SET_INT(
peer_.PeerAddress(), IOT_CONF_KEY_A2DP_ROLE,
(peer_.LocalUuidServiceClass() == UUID_SERVCLASS_AUDIO_SOURCE)
? IOT_CONF_VAL_A2DP_ROLE_SINK
: IOT_CONF_VAL_A2DP_ROLE_SOURCE);
DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
IOT_CONF_KEY_A2DP_CONN_COUNT);
} else if (event == BTA_AV_PENDING_EVT) {
DEVICE_IOT_CONFIG_ADDR_INT_ADD_ONE(peer_.PeerAddress(),
IOT_CONF_KEY_A2DP_CONN_COUNT);
}
} break;
default:
log::warn("Peer {} : Unhandled event={}",
ADDRESS_TO_LOGGABLE_CSTR(peer_.PeerAddress()),
BtifAvEvent::EventName(event));
return false;
}
return true;
}
BtifAvStateMachine::StateIdle::ProcessEvent
是蓝牙A/V状态机在空闲状态下处理事件的核心函数。它根据事件类型(如连接请求或挂起事件)执行相应的处理逻辑,包括检查连接权限、尝试建立连接、更新状态机状态和IoT配置等。该方法通过返回 true
或 false
来指示事件是否已被成功处理。
事件处理:对于
BTIF_AV_CONNECT_REQ_EVT
和BTA_AV_PENDING_EVT
这两种事件,会进行以下处理:
BTIF_AV_CONNECT_REQ_EVT
:表示收到了A/V连接请求。
BTA_AV_PENDING_EVT
:表示有挂起的A/V连接事件(由蓝牙协议栈触发)。调用
peer_.SetSelfInitiatedConnection
来标记连接是否由本地设备主动发起。根据对端设备的角色(音频源或音频接收器),检查是否允许连接。通过调用
btif_av_source.AllowedToConnect
或btif_av_sink.AllowedToConnect
来实现的。如果不允许连接(例如,已连接的对等设备数量过多),则根据对等设备的角色调用
src_disconnect_sink
或sink_disconnect_src
来断开连接。如果允许连接,则查询必要的编解码器优先级,并调用
BTA_AvOpen
来尝试建立A/V连接。将状态机转移到
kStateOpening
状态,表示连接正在建立中。根据事件类型,更新IoT配置中与A2DP角色和连接计数相关的设置。
BTA_AvOpen
packages/modules/Bluetooth/system/bta/av/bta_av_api.cc
/*******************************************************************************
*
* Function BTA_AvOpen
*
* Description Opens an advanced audio/video connection to a peer device.
* When connection is open callback function is called
* with a BTA_AV_OPEN_EVT.
*
* Returns void
*
******************************************************************************/
void BTA_AvOpen(const RawAddress& bd_addr, tBTA_AV_HNDL handle, bool use_rc,
uint16_t uuid) {
log::info("peer {} bta_handle:0x{:x} use_rc={} uuid=0x{:x}",
ADDRESS_TO_LOGGABLE_CSTR(bd_addr), handle,
(use_rc) ? "true" : "false", uuid);
tBTA_AV_API_OPEN* p_buf =
(tBTA_AV_API_OPEN*)osi_malloc(sizeof(tBTA_AV_API_OPEN));
p_buf->hdr.event = BTA_AV_API_OPEN_EVT;
p_buf->hdr.layer_specific = handle;
p_buf->bd_addr = bd_addr;
p_buf->use_rc = use_rc;
p_buf->switch_res = BTA_AV_RS_NONE;
p_buf->uuid = uuid;
if (btif_av_src_sink_coexist_enabled()) {
if (p_buf->uuid == AVDT_TSEP_SRC) {
p_buf->uuid = UUID_SERVCLASS_AUDIO_SOURCE;
p_buf->incoming = TRUE;
} else if (p_buf->uuid == AVDT_TSEP_SNK) {
p_buf->uuid = UUID_SERVCLASS_AUDIO_SINK;
p_buf->incoming = TRUE;
} else {
p_buf->incoming = FALSE;
}
}
bta_sys_sendmsg(p_buf);
}
BTA_AvOpen
的主要功能是用于建立与一个对等设备(peer device)之间的高级音频 / 视频(advanced audio/video)连接。在连接成功打开后,会通过回调函数以 BTA_AV_OPEN_EVT
事件的形式进行通知。函数本身无返回值,主要完成构建和发送相关消息以发起连接请求的操作。
bta_sys_sendmsg
packages/modules/Bluetooth/system/bta/sys/bta_sys_main.cc
/*******************************************************************************
*
* Function bta_sys_sendmsg
*
* Description Send a GKI message to BTA. This function is designed to
* optimize sending of messages to BTA. It is called by BTA
* API functions and call-in functions.
*
* TODO (apanicke): Add location object as parameter for easier
* future debugging when doing alarm refactor
*
*
* Returns void
*
******************************************************************************/
void bta_sys_sendmsg(void* p_msg) {
if (do_in_main_thread(
FROM_HERE,
base::BindOnce(&bta_sys_event, static_cast<BT_HDR_RIGID*>(p_msg))) !=
BT_STATUS_SUCCESS) {
log::error("do_in_main_thread failed");
}
}
bta_sys_sendmsg
的主要功能是将一个 GKI
消息发送给 BTA
(Bluetooth Application Layer)。并且它会被 BTA
的应用程序接口(API
)函数以及回调函数(call-in functions
)调用,自身无返回值,主要是完成消息发送的相关操作并处理发送过程中可能出现的错误情况。
bta_sys_event
packages/modules/Bluetooth/system/bta/sys/bta_sys_main.cc
/*******************************************************************************
*
* Function bta_sys_event
*
* Description BTA event handler; called from task event handler.
*
*
* Returns void
*
******************************************************************************/
static void bta_sys_event(BT_HDR_RIGID* p_msg) {
bool freebuf = true;
log::verbose("Event 0x{:x}", p_msg->event);
/* get subsystem id from event */
uint8_t 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 {
log::info("Ignoring receipt of unregistered event id:{}[{}]",
BtaIdSysText(static_cast<tBTA_SYS_ID>(id)), id);
}
if (freebuf) {
osi_free(p_msg);
}
}
bta_sys_event
是 BTA事件处理的核心函数,被任务事件处理器调用以响应不同的蓝牙事件。这个函数负责解析传入的事件消息,根据事件类型调用相应的子系统事件处理器,并管理消息缓冲区的释放。
子系统 ID 提取:通过右移
p_msg->event
的高8位来获取子系统 ID(id
)。在蓝牙协议栈中,不同的事件通常通过不同的高位值来区分它们所属的子系统。事件处理器调用:
- 检查
id
是否在有效范围内(即小于BTA_ID_MAX
)。- 如果
id
有效,并且对应的子系统事件处理器(bta_sys_cb.reg[id]->evt_hdlr
)已注册(非空),则调用该处理器。- 事件处理器返回一个布尔值,指示是否应该释放消息缓冲区(
freebuf
)。未注册事件处理:如果
id
无效或对应的子系统事件处理器未注册,则记录一条信息日志,指出收到了未注册的事件 ID。缓冲区释放:如果
freebuf
为true
,则使用osi_free
释放p_msg
指向的消息缓冲区。通常是事件处理器处理完事件后应该做的,但也可能根据特定情况决定不释放缓冲区(例如,如果事件处理器将消息传递给另一个处理器进一步处理)。
bta_av_hdl_event
packages/modules/Bluetooth/system/bta/av/bta_av_main.cc
/*******************************************************************************
*
* Function bta_av_hdl_event
*
* Description Advanced audio/video main event handling function.
*
*
* Returns bool
*
******************************************************************************/
bool bta_av_hdl_event(const BT_HDR_RIGID* p_msg) {
if (p_msg->event > BTA_AV_LAST_EVT) {
return true; /* to free p_msg */
}
if (p_msg->event >= BTA_AV_FIRST_NSM_EVT) {
log::verbose("AV nsm event=0x{:x}({})", p_msg->event,
bta_av_evt_code(p_msg->event));
bta_av_non_state_machine_event(p_msg->event, (tBTA_AV_DATA*)p_msg);
} else if (p_msg->event >= BTA_AV_FIRST_SM_EVT &&
p_msg->event <= BTA_AV_LAST_SM_EVT) {
log::verbose("AV sm event=0x{:x}({})", p_msg->event,
bta_av_evt_code(p_msg->event));
/* state machine events */
bta_av_sm_execute(&bta_av_cb, p_msg->event, (tBTA_AV_DATA*)p_msg);
} else {
log::verbose("bta_handle=0x{:x}", p_msg->layer_specific);
/* stream state machine events */
bta_av_ssm_execute(bta_av_hndl_to_scb(p_msg->layer_specific), p_msg->event,
(tBTA_AV_DATA*)p_msg);
}
return true;
}
bta_av_hdl_event
函数负责处理来自蓝牙 A/V 子系统的各种事件。这些事件可能涉及非状态机事件、状态机事件或流状态机事件。函数通过解析传入的事件消息,并调用相应的处理函数来处理这些事件。
事件范围检查:检查事件标识符(
p_msg->event
)是否超过了BTA_AV_LAST_EVT
事件。如果是,函数立即返回true
,表示消息应该被释放。非状态机事件处理:如果事件标识符在
BTA_AV_FIRST_NSM_EVT
和BTA_AV_LAST_EVT
之间(包括BTA_AV_LAST_EVT
,但上面的检查已经排除了这种情况),则认为这是一个非状态机事件。函数记录事件信息,并调用bta_av_non_state_machine_event
来处理该事件。状态机事件处理:如果事件标识符在
BTA_AV_FIRST_SM_EVT
和