【Bluedroid】btif_a2dp_sink_init 全流程源码解析

本文以蓝牙 A2DP(Advanced Audio Distribution Profile)Sink 模块的初始化流程为核心,结合Android代码实现,详细解析模块状态管理、线程生命周期控制、线程安全队列创建及实时调度策略设置等关键步骤。重点分析原子状态变量、互斥锁、信号量队列及实时线程调度在多线程环境下的协同工作机制,揭示蓝牙音频模块初始化过程中确保实时性、可靠性和线程安全的核心设计逻辑。

一、概述

1.1 核心组件与初始化流程总览

蓝牙 A2DP Sink 模块的初始化由btif_a2dp_sink_init函数驱动,依赖以下核心组件协同工作:

  • 原子状态变量btif_a2dp_sink_state(线程安全的状态标记);

  • 控制块BtifA2dpSinkControlBlock(封装模块资源与状态);

  • 消息循环线程MessageLoopThread(处理异步任务的线程容器);

  • 固定队列fixed_queue_t(线程安全的音频数据缓存队列);

  • 实时调度EnableRealTimeScheduling(提升线程实时性的系统调用)。

1.2 关键步骤解析

蓝牙A2DP接收器负责接收并播放来自源设备的高质量音频流。初始化过程需确保线程安全、资源就绪和实时性保障。

(1) 状态检查与互斥锁保护

初始化前通过原子变量btif_a2dp_sink_state检查模块是否处于关闭状态(BTIF_A2DP_SINK_STATE_OFF),并使用互斥锁g_mutex确保多线程环境下初始化操作的原子性,避免重复初始化。

(2) 控制块重置与状态更新

调用BtifA2dpSinkControlBlock::Reset释放旧资源(如音频轨道、定时器),并重置成员变量。随后将状态更新为BTIF_A2DP_SINK_STATE_STARTING_UP,标记模块进入启动中状态。

(3) 消息循环线程启动

通过MessageLoopThread::StartUp创建并启动工作线程:

使用promise/future同步机制确保主线程等待子线程完成初始化(如消息循环对象创建、线程 ID 记录);

子线程通过Run函数进入事件循环(run_loop_->Run()),持续处理异步任务。

(4) 音频队列创建与实时调度设置

创建固定容量的音频接收队列(fixed_queue_new(SIZE_MAX)),用于缓存蓝牙接收的音频数据。随后调用EnableRealTimeScheduling为工作线程设置实时调度策略(SCHED_FIFO),提升音频解码等关键任务的实时性。

(5) 延迟初始化完成状态标记

通过DoInThreadbtif_a2dp_sink_init_delayed提交到工作线程执行,最终将状态更新为BTIF_A2DP_SINK_STATE_RUNNING,通知其他模块 “音频传输就绪”。

二、源码解析

btif_a2dp_sink_init

packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
// 定义一个静态的原子整数变量,用于在多线程环境下安全地记录蓝牙 A2DP 接收器的状态,并且初始状态为关闭
static std::atomic<int> btif_a2dp_sink_state{BTIF_A2DP_SINK_STATE_OFF};

using LockGuard = std::lock_guard<std::mutex>;

#  define SIZE_MAX      (18446744073709551615UL)

bool btif_a2dp_sink_init() {
  log::info("");
  // 使用互斥锁保护共享资源,确保在同一时间只有一个线程可以执行该函数内的关键代码段
  LockGuard lock(g_mutex);

  // 检查 A2DP 接收器的状态是否已经不是关闭状态
  if (btif_a2dp_sink_state != BTIF_A2DP_SINK_STATE_OFF) {
    // 如果不是关闭状态,返回 false 表示初始化失败
    log::error("A2DP Sink media task already running");
    return false;
  }

  // 重置 A2DP 接收器的回调函数结构体,清除之前可能存在的状态
  btif_a2dp_sink_cb.Reset();
  // 将 A2DP 接收器的状态设置为启动中
  btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_STARTING_UP;

  // 启动 A2DP 接收器的媒体任务线程
  btif_a2dp_sink_cb.worker_thread.StartUp();
  // 检查线程是否成功启动
  if (!btif_a2dp_sink_cb.worker_thread.IsRunning()) {
    // 如果线程未能成功启动,将接收器状态设置为关闭,并返回 false 表示初始化失败
    log::error("unable to start up media thread");
    btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_OFF;
    return false;
  }

  // 创建一个固定大小的音频接收队列,队列大小为 SIZE_MAX
  btif_a2dp_sink_cb.rx_audio_queue = fixed_queue_new(SIZE_MAX);

  // 尝试为媒体任务线程启用实时调度策略
  if (!btif_a2dp_sink_cb.worker_thread.EnableRealTimeScheduling()) {
    // 如果在 Android 平台上且启用实时调度失败,记录致命错误日志
#if defined(__ANDROID__)
    log::fatal("Failed to increase A2DP decoder thread priority");
#endif
  }
  // 安排在媒体任务线程中执行 btif_a2dp_sink_init_delayed 函数,完成后续的初始化操作
  btif_a2dp_sink_cb.worker_thread.DoInThread(
      FROM_HERE, base::BindOnce(btif_a2dp_sink_init_delayed));
  // 如果所有操作都成功,返回 true 表示初始化成功
  return true;
}

初始化蓝牙 A2DP接收器。检查接收器的状态,启动媒体任务线程,创建音频队列,设置线程调度策略,并安排后续的初始化操作。

btif_a2dp_sink_cb.Reset

/packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
static BtifA2dpSinkControlBlock btif_a2dp_sink_cb("bt_a2dp_sink_worker_thread"); // 使用指定的线程名称,调用该类的构造函数对其进行初始化

/* BTIF A2DP Sink control block */
class BtifA2dpSinkControlBlock {
 public:
  // 构造函数
  explicit BtifA2dpSinkControlBlock(const std::string& thread_name) // explicit 用于防止隐式类型转换,确保只能通过显式调用构造函数来创建 BtifA2dpSinkControlBlock对象
      : worker_thread(thread_name),
        rx_audio_queue(nullptr),
        rx_flush(false),
        decode_alarm(nullptr),
        sample_rate(0),
        channel_count(0),
        rx_focus_state(BTIF_A2DP_SINK_FOCUS_NOT_GRANTED),
        audio_track(nullptr),
        decoder_interface(nullptr) {}

  // Reset方法用于重置 BtifA2dpSinkControlBlock 对象的状态,释放已分配的资源,并将所有成员变量恢复到初始状态
  void Reset() {
    if (audio_track != nullptr) {
      BtifAvrcpAudioTrackStop(audio_track); // 停止音频轨道
      BtifAvrcpAudioTrackDelete(audio_track); // 删除音频轨道
    }
    audio_track = nullptr;
    fixed_queue_free(rx_audio_queue, nullptr);
    rx_audio_queue = nullptr;
    alarm_free(decode_alarm);
    decode_alarm = nullptr;
    // 状态重置
    rx_flush = false;
    rx_focus_state = BTIF_A2DP_SINK_FOCUS_NOT_GRANTED;
    sample_rate = 0;
    channel_count = 0;
    decoder_interface = nullptr;
  }

  // 成员变量
  MessageLoopThread worker_thread; // 用于处理与 A2DP 接收器相关的消息循环和线程操作
  fixed_queue_t* rx_audio_queue; // 用于存储接收到的音频数据
  bool rx_flush; /* discards any incoming data when true */
  alarm_t* decode_alarm; // 用于管理解码定时器
  tA2DP_SAMPLE_RATE sample_rate; // 音频采样率
  tA2DP_BITS_PER_SAMPLE bits_per_sample; // 每个样本的位数
  tA2DP_CHANNEL_COUNT channel_count; // 音频通道数
  btif_a2dp_sink_focus_state_t rx_focus_state; /* audio focus state */ //音频焦点状态
  void* audio_track; // 指向音频轨道的指针,用于播放音频数据
  const tA2DP_DECODER_INTERFACE* decoder_interface; // 用于调用音频解码器的接口
};

BtifA2dpSinkControlBlock 类是用于管理蓝牙 A2DP接收器控制块的类。通过构造函数初始化成员变量,并提供Reset 方法来重置对象的状态和释放资源。封装了与蓝牙 A2DP 接收器相关的各种状态和资源,方便对 A2DP 接收器进行管理和控制。

btif_a2dp_sink_cb.worker_thread.StartUp

packages/modules/Bluetooth/system/common/message_loop_thread.cc
void MessageLoopThread::StartUp() {
  // 1. 同步机制初始化
  // 用于在线程间传递同步信号。start_up_promise会在子线程启动完成后通过set_value()通知主线程
  std::promise<void> start_up_promise; 
  // 与promise绑定,主线程通过start_up_future.wait()阻塞等待子线程启动完成的信号
  std::future<void> start_up_future = start_up_promise.get_future();
  {
    // 2. 线程安全锁
    // 保证StartUp方法的线程安全性,防止多线程并发调用时出现竞态条件
    std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
    // 3. 重复启动检查
    if (thread_ != nullptr) {
      LOG(WARNING) << __func__ << ": thread " << *this << " is already started";

      return;
    }
    // 4. 创建并启动线程
    thread_ = new std::thread(&MessageLoopThread::RunThread, this,
                              std::move(start_up_promise));
  }
  // 5. 等待线程启动完成
  // 主线程阻塞等待,直到子线程中调用start_up_promise.set_value()(表示子线程已成功启动)
  start_up_future.wait(); 
}

安全、同步地启动一个消息循环线程,主要特点包括:

  • 线程安全:通过互斥锁api_mutex_防止多线程并发调用StartUp

  • 防重复启动:通过检查thread_指针避免重复创建线程。

  • 同步等待:使用promise/future机制确保主线程等待子线程完全启动后再返回。

递归互斥锁允许同一线程多次加锁(例如:线程在启动过程中可能递归调用其他需要加锁的接口),避免死锁

MessageLoopThread::RunThread

packages/modules/Bluetooth/system/common/message_loop_thread.cc
// 将线程执行逻辑转发给 Run 方法
// Non API method, should not be protected by API mutex
void MessageLoopThread::RunThread(MessageLoopThread* thread,
                                  std::promise<void> start_up_promise) {
  thread->Run(std::move(start_up_promise));
}

void MessageLoopThread::Run(std::promise<void> start_up_promise) {
  // 初始化阶段(加锁保护)
  {
    std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);

    LOG(INFO) << __func__ << ": message loop starting for thread "
              << thread_name_;
    base::PlatformThread::SetName(thread_name_); // 设置线程名称(调试友好)
    message_loop_ = new btbase::AbstractMessageLoop(); // 创建消息循环对象
    run_loop_ = new base::RunLoop(); // 创建运行循环对象(控制事件循环)
    thread_id_ = base::PlatformThread::CurrentId(); // 记录线程ID(跨平台)
    linux_tid_ = static_cast<pid_t>(syscall(SYS_gettid)); // 记录Linux特有的线程ID(TID)
    start_up_promise.set_value(); // 通知主线程:线程初始化完成
  }

  // 消息循环运行(阻塞阶段)
  // Blocking until ShutDown() is called
  run_loop_->Run(); // 启动事件循环并阻塞在此处,等待处理消息或任务

  //  清理阶段(加锁保护)
  {
    // 再次加锁 api_mutex_,确保清理过程中成员变量的修改是线程安全的
    std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
    thread_id_ = -1;  // 重置线程ID(标记为未运行)
    linux_tid_ = -1;
    delete message_loop_; // 释放消息循环对象
    message_loop_ = nullptr;
    delete run_loop_; // 释放运行循环对象
    run_loop_ = nullptr;
    LOG(INFO) << __func__ << ": message loop finished for thread "
              << thread_name_;
  }
}

结合 StartUpRunThreadRun 方法,线程的完整生命周期如下:

  1. 启动阶段(StartUp): 主线程创建 std::thread,绑定 RunThread 作为入口函数,并通过 promise/future 同步等待线程初始化完成。

  2. 初始化阶段(Run 函数前半部分): 子线程加锁保护,初始化消息循环、记录线程 ID,并通过 promise.set_value() 通知主线程 “已就绪”。

  3. 运行阶段(run_loop_->Run()): 子线程进入事件循环,持续处理消息和任务,直到 ShutDown 被调用(触发 run_loop_->Quit())。

  4. 清理阶段(Run 函数后半部分): 子线程加锁保护,释放消息循环资源,重置线程状态,并记录结束日志。

关键设计点

  • 线程安全:通过 api_mutex_ 递归互斥锁保护成员变量的修改(如初始化和清理阶段),避免多线程竞态条件。

  • 同步机制promise/future 确保主线程等待子线程完全初始化后再返回,避免 “线程未就绪就使用” 的问题。

  • 资源管理:显式释放 message_loop_run_loop_ 对象,防止内存泄漏。

syscall(SYS_gettid)syscall 是 Linux 提供的系统调用接口函数,用于直接调用内核功能。SYS_gettid 是一个预定义的宏(通常在 <unistd.h><sys/syscall.h> 中定义),对应内核中获取线程 ID 的系统调用号(例如,在 x86_64 架构下,SYS_gettid 的值为 186)。 调用 syscall(SYS_gettid) 会触发内核执行获取当前线程 TID 的操作,并返回该 TID(类型为 long)。

btif_a2dp_sink_cb.worker_thread.StartUp

packages/modules/Bluetooth/system/common/message_loop_thread.cc
bool MessageLoopThread::IsRunning() const {
  std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);
  return thread_id_ != -1;
}

通过检查成员变量 thread_id_ 是否为 -1,来判断线程是否处于运行状态。用于查询当前消息循环线程是否活跃,避免向已停止的线程发送任务或执行依赖操作。

fixed_queue_new

packages/modules/Bluetooth/system/osi/src/fixed_queue.cc
typedef struct fixed_queue_t {
  list_t* list;
  semaphore_t* enqueue_sem;
  semaphore_t* dequeue_sem;
  std::mutex* mutex;
  size_t capacity;

  reactor_object_t* dequeue_object;
  fixed_queue_cb dequeue_ready;
  void* dequeue_context;
} fixed_queue_t;

static void internal_dequeue_ready(void* context);

fixed_queue_t* fixed_queue_new(size_t capacity) {
  // 1. 分配队列结构体内存
  fixed_queue_t* ret =
      static_cast<fixed_queue_t*>(osi_calloc(sizeof(fixed_queue_t)));

  // 2. 初始化互斥锁和容量
  ret->mutex = new std::mutex;
  ret->capacity = capacity;

  // 3. 初始化底层存储链表
  ret->list = list_new(NULL);
  if (!ret->list) goto error;

  // 4. 初始化信号量
  ret->enqueue_sem = semaphore_new(capacity);
  if (!ret->enqueue_sem) goto error;

  ret->dequeue_sem = semaphore_new(0);
  if (!ret->dequeue_sem) goto error;

  return ret;

error:
  fixed_queue_free(ret, NULL);
  return NULL;
}

创建并初始化一个固定容量的队列。通过 信号量 + 互斥锁 实现了线程安全的固定容量队列:

  • 容量控制enqueue_sem 的初始值为 capacity,确保入队操作不会超过队列容量(当队列满时,enqueue_sem 为 0,新的入队操作阻塞)。

  • 空队列保护dequeue_sem 的初始值为 0,确保出队操作在队列无元素时阻塞(当队列有元素时,dequeue_sem 被递增,出队操作解除阻塞)。

  • 线程安全mutex 互斥锁保证对链表 list 的修改(如入队时添加元素、出队时删除元素)是原子的,避免多线程并发操作导致的数据不一致。

这种固定容量的线程安全队列常用于生产者 - 消费者模型,例如:

  • 蓝牙模块中,上层应用(生产者)向队列发送指令,底层驱动(消费者)从队列取出指令执行。

  • 队列的固定容量避免内存无限增长,信号量机制自动协调生产者和消费者的速度差异。

fixed_queue_t 是队列的核心结构体,包含队列运行所需的所有状态和资源:

成员变量类型作用
listlist_t*存储队列元素的链表(底层容器,具体实现类似双向链表
enqueue_semsemaphore_t*入队信号量:控制可入队的元素数量(初始值为队列容量 capacity)
dequeue_semsemaphore_t*出队信号量:控制可出队的元素数量(初始值为 0,无元素时阻塞出队)
mutexstd::mutex*互斥锁:保证对队列的修改(如入队、出队)是线程安全的
capacitysize_t队列的最大容量(固定不变)
dequeue_objectreactor_object_t*反应堆对象(可能用于异步事件通知,如出队就绪时触发回调)
dequeue_readyfixed_queue_cb出队就绪回调函数(当队列有元素时触发)
dequeue_contextvoid*回调函数的上下文参数(传递用户自定义数据)

btif_a2dp_sink_cb.worker_thread.EnableRealTimeScheduling

packages/modules/Bluetooth/system/common/message_loop_thread.cc
// 在 Linux 系统中,实时调度策略(如 SCHED_FIFO、SCHED_RR)的优先级范围是 1~99(数值越大优先级越高)
// 设置为 1 表示较低的实时优先级(避免过高优先级影响系统其他任务)
static constexpr int kRealTimeFifoSchedulingPriority = 1;

bool MessageLoopThread::EnableRealTimeScheduling() {
  std::lock_guard<std::recursive_mutex> api_lock(api_mutex_);

  if (!IsRunning()) {
    LOG(ERROR) << __func__ << ": thread " << *this << " is not running";
    return false;
  }

  // 配置实时调度参数
  struct sched_param rt_params = {.sched_priority =
                                      kRealTimeFifoSchedulingPriority};
  //  调用系统调用设置实时调度策略
  int rc = sched_setscheduler(linux_tid_, SCHED_FIFO, &rt_params);
  if (rc != 0) {
    LOG(ERROR) << __func__ << ": unable to set SCHED_FIFO priority "
               << kRealTimeFifoSchedulingPriority << " for linux_tid "
               << std::to_string(linux_tid_) << ", thread " << *this
               << ", error: " << strerror(errno);
    return false;
  }
  return true;
}

通过 Linux 系统调用为运行中的蓝牙消息循环线程设置实时调度策略(SCHED_FIFO)和指定优先级,旨在提升线程的实时性能,确保关键任务的低延迟执行。其核心逻辑包括线程状态检查、调度参数配置、系统调用执行及错误处理,是蓝牙模块中保证实时性的重要机制。

  • SCHED_FIFO:调度策略,代表 “先入先出(First-In-First-Out)” 实时调度。该策略下,线程会一直运行直到主动阻塞(如等待 I/O)或被更高优先级线程抢占。

  • 权限要求:普通用户进程调用 sched_setscheduler 设置实时调度策略时,通常会失败(需要 root 权限或 CAP_SYS_NICE 能力)。实际使用中,需要通过 setcap 命令为程序授予权限(如 setcap cap_sys_nice+ep /path/to/executable)。

  • 优先级影响:实时优先级过高可能导致其他线程(如用户交互线程)无法获得 CPU 时间,需根据业务需求谨慎选择优先级( 1 是较低的实时优先级)。

注意事项

  • 实时调度策略(如SCHED_FIFO)通常用于需要精确控制时间的应用,如音频、视频处理等。但是,它们也可能对系统的其他部分产生负面影响。

  • 只有在具有适当权限(如root权限)的情况下,才能成功将线程设置为实时调度策略。

  • std::recursive_mutex允许同一个线程多次锁定互斥锁而不会导致死锁。在复杂的场景下可能是有用的,但也需要小心使用,以避免不必要的复杂性或性能问题。

btif_a2dp_sink_init_delayed

packages/modules/Bluetooth/system/btif/src/btif_a2dp_sink.cc
// 保证对变量的读 / 写操作是原子的(不可分割),无需显式加锁即可在多线程环境下安全访问
static std::atomic<int> btif_a2dp_sink_state{BTIF_A2DP_SINK_STATE_OFF};

static void btif_a2dp_sink_init_delayed() {
  log::info("");
  btif_a2dp_sink_state = BTIF_A2DP_SINK_STATE_RUNNING;
}

A2DP Sink 模块初始化流程的 “最后一步”,通过延迟执行确保依赖资源就绪后,将模块状态标记为运行中,其他模块可据此状态安全地开始音频数据传输等操作。其核心作用是协调初始化流程的异步性,保证模块状态与实际运行能力的一致性。

原子性的意义:

在多线程场景中(如蓝牙协议栈的主线程、IO 线程、事件处理线程),多个线程可能同时读取或修改 btif_a2dp_sink_state。若使用普通 int 类型,可能因编译器优化(如指令重排)或 CPU 缓存不一致导致状态读取错误(竞态条件)。而原子类型通过硬件指令(如 x86 的 LOCK 前缀)保证操作的原子性,确保所有线程看到的状态是一致的。

启动流程:

logcat:

三、流程图

蓝牙 A2DP Sink 模块的初始化通过原子状态管理、线程安全控制、实时调度优化三大核心机制,确保了多线程环境下的可靠性与实时性:

  • 线程安全:原子变量btif_a2dp_sink_state与互斥锁g_mutex协同,避免状态竞争;

  • 资源管理:控制块Reset方法显式释放旧资源,固定队列fixed_queue_t通过信号量限制容量,防止内存泄漏;

  • 实时性保证SCHED_FIFO调度策略结合低实时优先级(1),在提升音频任务响应速度的同时避免系统资源抢占;

  • 异步协调promise/future同步与延迟初始化机制,解决了初始化依赖(如线程就绪、资源准备)的异步性问题。


评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byte轻骑兵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值