1. bt_jni_thread 职责介绍
bt_jni_thread 这个线程的作用是专门负责处理蓝牙 JNI 层的消息循环,也可以说是 C++ 层和 Java 层交互的桥梁线程。
1.1 什么是 JNI 层?为什么需要这个线程?
JNI(Java Native Interface)是 Android 用来让 Java 和 C/C++ 代码通信的机制。在蓝牙协议栈中:
- Java 层(APP 或框架)发送蓝牙指令。
- C++ 层(Native 蓝牙协议栈)负责底层逻辑,比如连接蓝牙设备、传输数据等。
- 中间就是 JNI 层,它用来做“翻译官” —— 把 Java 的指令转成 C++ 能理解的函数调用,反之也一样。
1.2 职责总结
-
消息分发器(Message Dispatcher)
所有 JNI 相关的调用(比如设备连接状态回调、扫描结果等),都通过这个线程发送回 Java 层。 -
避免阻塞主线程(Non-blocking)
蓝牙操作可能会耗时,比如搜索设备、建立连接等。如果不单独开线程处理,会拖慢系统,甚至 ANR(应用无响应)。 -
保证线程安全(Thread Safety)
蓝牙操作涉及很多共享资源(socket、状态等),用一个专门的线程可以避免多线程访问冲突。 -
事件循环(Message Loop)
这个线程运行的是一个“消息循环”,就是不断读取事件队列中的消息,然后按顺序处理。
1.3 为什么这样设计很重要?
-
安全性(Safe):
如果所有 JNI 回调都在主线程或者随便哪个线程跑,容易出现崩溃或者状态错乱。 -
效率(Efficient):
用单独线程做专门的事,可以提高系统响应速度,用户操作不会被蓝牙事件卡住。 -
解耦(Decoupled):
Java 层、JNI 层、Native 层之间职责分明,出了问题更容易排查和维护。
1.4 总结一句话:
bt_jni_thread 就像是蓝牙 JNI 层的“接线员”,负责有序、高效、安全地处理 Java 与 C++ 层的消息传递,是整个 Android 蓝牙系统稳定运行的重要一环。
2. 如何工作的?
既然 bt_jni_thread 职责已经很清晰了, 那我们就探索一下, bt_jni_thread 是如何启动 , 如何接收事件, 以及处理事件的?
- system/btif/src/btif_core.cc
2.1 线程何时启动
static MessageLoopThread jni_thread("bt_jni_thread");bt_status_t btif_init_bluetooth() {LOG_INFO("%s entered", __func__);exit_manager = new base::AtExitManager();jni_thread.StartUp(); // 创建 bt_jni_thread 线程invoke_thread_evt_cb(ASSOCIATE_JVM);LOG_INFO("%s finished", __func__);return BT_STATUS_SUCCESS;
}
调用流程
[stack_manager.cc:event_init_stack] ->[btif_init_bluetooth]
- 是在 调用 stack_manager 的 event_init_stack 函数阶段触发的, event_init_stack的调用流程, 请参考 之前的文章 介绍 bt_stack_manager_thread
2.2 如何下发任务到 该线程
bt_jni_thread 线程已经启动, 那我们如何下发任务给 该线程去处理呢?
答案是通过 do_in_jni_thread 函数
1. do_in_jni_thread
bt_status_t do_in_jni_thread(const base::Location& from_here,base::OnceClosure task) {if (!jni_thread.DoInThread(from_here, std::move(task))) {LOG(ERROR) << __func__ << ": Post task to task runner failed!";return BT_STATUS_FAIL;}return BT_STATUS_SUCCESS;
}
这个函数的目的是:
把你传进来的任务
task
安排到bt_jni_thread
线程里去执行。
参数解释:
from_here
:记录任务来源,用于调试(比如你从哪个文件、哪一行调用的这个函数)。task
:要在线程中执行的逻辑(闭包或 lambda 表达式)。
实现逻辑:
-
它调用了:
jni_thread.DoInThread(from_here, std::move(task));
-
让
bt_jni_thread
来执行这个任务。如果失败(比如线程未启动),就返回BT_STATUS_FAIL
。 -
那些场景会用到 do_in_jni_thread
-
从搜素结果来看, 凡是设计到 jni 交互的场景都 在使用。 这个也验证了 本章已开始就介绍的 bt_jni_thread 职责。
这个函数主要用途:
-
线程切换:你可能当前在蓝牙主线程、HAL 线程、APP 线程,但 JNI 的东西要在
bt_jni_thread
里跑,必须切过去。 -
线程安全:统一通过
bt_jni_thread
来操作 JNI,避免多线程同时调用 JNI 导致 crash。 -
任务封装:代码结构更清晰,异步任务都包成一个
Closure
任务发送。
2. btif_transfer_context
bt_status_t btif_transfer_context(tBTIF_CBACK* p_cback, uint16_t event,char* p_params, int param_len,tBTIF_COPY_CBACK* p_copy_cback) {tBTIF_CONTEXT_SWITCH_CBACK* p_msg = (tBTIF_CONTEXT_SWITCH_CBACK*)osi_malloc(sizeof(tBTIF_CONTEXT_SWITCH_CBACK) + param_len);BTIF_TRACE_VERBOSE("btif_transfer_context event %d, len %d", event,param_len);/* allocate and send message that will be executed in btif context */p_msg->hdr.event = BT_EVT_CONTEXT_SWITCH_EVT; /* internal event */p_msg->p_cb = p_cback;p_msg->event = event; /* callback event *//* check if caller has provided a copy callback to do the deep copy */if (p_copy_cback) {p_copy_cback(event, p_msg->p_param, p_params);} else if (p_params) {memcpy(p_msg->p_param, p_params, param_len); /* callback parameter data */}do_in_jni_thread(base::Bind(&bt_jni_msg_ready, p_msg));return BT_STATUS_SUCCESS;
}static void bt_jni_msg_ready(void* context) {tBTIF_CONTEXT_SWITCH_CBACK* p = (tBTIF_CONTEXT_SWITCH_CBACK*)context;if (p->p_cb) p->p_cb(p->event, p->p_param);osi_free(p);
}
btif_transfer_context(...)
,它的作用是:
把来自 HAL 或 Stack 的事件,转移(transfer)到 JNI 线程(bt_jni_thread)中执行,并调用对应的回调函数。
这个函数起到了核心的“线程桥梁”和“事件分发器”的作用。
函数入参解释
参数 | 类型 | 作用 |
---|---|---|
p_cback | tBTIF_CBACK* | 你希望在 bt_jni_thread 中被调用的回调函数 |
event | uint16_t | 表示是什么事件,通常用枚举,如 BTIF_DM_CB_DISCOVERY_STARTED |
p_params | char* | 回调函数要用的参数 |
param_len | int | 参数长度 |
p_copy_cback | tBTIF_COPY_CBACK* | 自定义的“深拷贝”函数,用于复杂结构的复制(可选) |
-
分配内存
tBTIF_CONTEXT_SWITCH_CBACK* p_msg = (tBTIF_CONTEXT_SWITCH_CBACK*)osi_malloc(...);
创建一个消息对象
p_msg
,用来封装要执行的任务。 -
设置元数据
p_msg->hdr.event = BT_EVT_CONTEXT_SWITCH_EVT; p_msg->p_cb = p_cback; p_msg->event = event;
标记这个是“上下文切换事件”,并把事件编号和要执行的回调函数绑定进来。
-
拷贝参数
如果参数非空,尝试用p_copy_cback()
深拷贝参数;否则直接用memcpy()
复制。 -
投递给 JNI 线程执行
do_in_jni_thread(base::Bind(&bt_jni_msg_ready, p_msg));
把这个封装好的消息送到
bt_jni_thread
中,由bt_jni_msg_ready()
函数去调度并执行真正的回调。 -
返回成功
return BT_STATUS_SUCCESS;
这样设计意义是什么?
设计点 | 意义 |
---|---|
线程安全 | 所有 JNI 回调都在同一个线程中执行,避免多线程问题 |
可扩展 | 不同事件、不同回调都能统一走这个机制 |
解耦 | HAL 层不用关心 JNI 层线程情况,只管调用 transfer |
支持复杂数据 | 可通过 p_copy_cback 自定义深拷贝参数结构体 |
总结
-
btif_transfer_context()
是一个线程切换和任务派发的机制。 -
它不是 Java 调 Native 的唯一通道,但在需要切换到
bt_jni_thread
执行的下行任务中非常关键。 -
它和
do_in_jni_thread()
一起构成了 Android 蓝牙协议栈中线程调度和事件派发的基础工具。
3. 使用举例
1. 上行 native -> java
01-10 06:48:39.160 2024 3120 I bluetooth: packages/modules/Bluetooth/system/bta/dm/bta_dm_main.cc:65 bta_dm_search_sm_execute: bta_dm_search_sm_execute state:1, event:0x20501-10 06:48:39.160 2024 3120 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: btif_dm_search_devices_evt event=BTA_DM_DISC_CMPL_EVT01-10 06:48:39.161 2024 2493 I bt_btif : packages/modules/Bluetooth/system/main/bte_logmsg.cc:198 LogMsg: operator(): HAL bt_hal_cbacks->discovery_state_changed_cb01-10 06:48:39.161 2024 2493 I AdapterProperties: Callback:discoveryStateChangeCallback with state:0
当我们发起扫描, 扫描结束后, 会上报一个状态给 java 层。
我们来梳理一下这个调用流程:
- system/btif/src/btif_dm.cc
static int start_discovery(void) {if (!interface_ready()) return BT_STATUS_NOT_READY;// 当我们触发 扫描时, 是跑在 main_thread 中的do_in_main_thread(FROM_HERE, base::BindOnce(btif_dm_start_discovery));return BT_STATUS_SUCCESS;
}void btif_dm_start_discovery(void) {BTIF_TRACE_EVENT("%s", __func__);/* no race here because we're guaranteed to be in the main thread */if (bta_dm_is_search_request_queued()) {LOG_INFO("%s skipping start discovery because a request is queued",__func__);return;}/* Will be enabled to true once inquiry busy level has been received */btif_dm_inquiry_in_progress = false;/* find nearby devices */BTA_DmSearch(btif_dm_search_devices_evt);
}
- app 侧发起扫描, 就会触发 btif_dm_start_discovery 调用,
- 在触发扫描时,我们注册了 btif_dm_search_devices_evt 回调函数
当芯片上报扫描 结束时,就会 回调 btif_dm_search_devices_evt 。 这个过程怎么回调到的,暂时不表, 后面有机会,专门单独表述,这种机制。
- system/btif/src/btif_dm.cc
static void btif_dm_search_devices_evt(tBTA_DM_SEARCH_EVT event,tBTA_DM_SEARCH* p_search_data) {// 此时发现收到了 扫描结束事件case BTA_DM_DISC_CMPL_EVT: {// 这里会触发invoke_discovery_state_changed_cb(BT_DISCOVERY_STOPPED);} break; }
-
触发调用 invoke_discovery_state_changed_cb
-
system/btif/src/bluetooth.cc
void invoke_discovery_state_changed_cb(bt_discovery_state_t state) {do_in_jni_thread(FROM_HERE, base::BindOnce([](bt_discovery_state_t state) {HAL_CBACK(bt_hal_cbacks,discovery_state_changed_cb,state);},state));
}
- HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb,…)
- 上述的 调用会打印如下log
- HAL bt_hal_cbacks->discovery_state_changed_cb
- 此时就会调用到 discovery_state_changed_cb
- 从这里开始 回调 hal 层, 将 discovery_state_changed_cb 回调放置到 bt_jni_thread 线程中去处理, 从这里往下都是跑在 bt_jni_thread 中。
- android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
static bt_callbacks_t sBluetoothCallbacks = {
...discovery_state_changed_callback, // 这里会被回调到...
};static void discovery_state_changed_callback(bt_discovery_state_t state) {CallbackEnv sCallbackEnv(__func__);if (!sCallbackEnv.valid()) return;ALOGV("%s: DiscoveryState:%d ", __func__, state);sCallbackEnv->CallVoidMethod(sJniCallbacksObj, method_discoveryStateChangeCallback, (jint)state);
}static void classInitNative(JNIEnv* env, jclass clazz) {
...method_discoveryStateChangeCallback = env->GetMethodID(jniCallbackClass, "discoveryStateChangeCallback", "(I)V");
}
-
此时回调到 discovery_state_changed_callback 他最终回调到 java 侧的 AdapterProperties::discoveryStateChangeCallback
-
android/app/src/com/android/bluetooth/btservice/AdapterProperties.java
void discoveryStateChangeCallback(int state) {infoLog("Callback:discoveryStateChangeCallback with state:" + state);synchronized (mObject) {Intent intent;if (state == AbstractionLayer.BT_DISCOVERY_STOPPED) {mDiscovering = false;mService.clearDiscoveringPackages();mDiscoveryEndMs = System.currentTimeMillis();intent = newIntent(BluetoothAdapter.ACTION_DISCOVERY_FINISHED,BluetoothAdapterExt.ACTION_DISCOVERY_FINISHED);mService.sendBroadcast(intent, BLUETOOTH_SCAN,Utils.getTempAllowlistBroadcastOptions());} else if (state == AbstractionLayer.BT_DISCOVERY_STARTED) {mDiscovering = true;mDiscoveryEndMs = System.currentTimeMillis() + DEFAULT_DISCOVERY_TIMEOUT_MS;intent = newIntent(BluetoothAdapter.ACTION_DISCOVERY_STARTED,BluetoothAdapterExt.ACTION_DISCOVERY_STARTED);mService.sendBroadcast(intent, BLUETOOTH_SCAN,Utils.getTempAllowlistBroadcastOptions());}}}
2. 下行 java -> native
在车机的蓝牙电话通话过程中, 我们可以随意在 车机上切换当前来电是在手机接听,还是车机接听。 也就是 hfp 中 SCO 的建立和断开。 这里我们看一下 主动去建立 SCO的流程。
- android/app/jni/com_android_bluetooth_hfpclient.cpp
static jboolean connectAudioNative(JNIEnv* env, jobject object,jbyteArray address) {...bt_status_t status =sBluetoothHfpClientInterface->connect_audio((const RawAddress*)addr);...
}
- system/btif/src/btif_hf_client.cc
static bt_status_t connect_audio(const RawAddress* bd_addr) {btif_hf_client_cb_t* cb = btif_hf_client_get_cb_by_bda(*bd_addr);if (cb == NULL || !is_connected(cb)) return BT_STATUS_FAIL;CHECK_BTHF_CLIENT_SLC_CONNECTED(cb);if ((get_default_hf_client_features() & BTA_HF_CLIENT_FEAT_CODEC) &&(cb->peer_feat & BTA_HF_CLIENT_PEER_CODEC)) {BTA_HfClientSendAT(cb->handle, BTA_HF_CLIENT_AT_CMD_BCC, 0, 0, NULL);} else {BTA_HfClientAudioOpen(cb->handle);}/* Inform the application that the audio connection has been initiated* successfully */// 之间加入到 bt_jni_thread 中去执行btif_transfer_context(btif_in_hf_client_generic_evt,BTIF_HF_CLIENT_CB_AUDIO_CONNECTING, (char*)bd_addr,sizeof(RawAddress), NULL);return BT_STATUS_SUCCESS;
}
- 这里通过 btif_transfer_context 将建立 sco 的操作放置到 bt_jni_thread 线程中去执行了。
2.3 线程何时终止
bt_status_t btif_cleanup_bluetooth() {LOG_INFO("%s entered", __func__);btif_dm_cleanup();invoke_thread_evt_cb(DISASSOCIATE_JVM);btif_queue_release();jni_thread.ShutDown();delete exit_manager;exit_manager = nullptr;btif_dut_mode = 0;LOG_INFO("%s finished", __func__);return BT_STATUS_SUCCESS;
}
- system/btif/src/stack_manager.cc
static void event_clean_up_stack(std::promise<void> promise) {...btif_cleanup_bluetooth();...
}
- 当我们点击关闭蓝牙时 bt_stack_manager_thread 线程会去触发 event_clean_up_stack 调用, 在这个里面,会去讲我们的 bt_jni_thread 线程终止的。
3. bt_hal_cbacks 介绍
3.1 HAL_CBACK
在上面 2.2.2.1 小结介绍上行 流时, 我们看到了 HAL_CBACK 的调用,本节就来看看 这部分是如何 做到的。
void invoke_discovery_state_changed_cb(bt_discovery_state_t state) {do_in_jni_thread(FROM_HERE, base::BindOnce([](bt_discovery_state_t state) {HAL_CBACK(bt_hal_cbacks,discovery_state_changed_cb,state);},state));
}
#define HAL_CBACK(P_CB, P_CBACK, ...) \do { \if ((P_CB) && (P_CB)->P_CBACK) { \BTIF_TRACE_API("%s: HAL %s->%s", __func__, #P_CB, #P_CBACK); \(P_CB)->P_CBACK(__VA_ARGS__); \} else { \ASSERTC(0, "Callback is NULL", 0); \} \} while (0)
- 这里是一个宏函数,
- 可以直接把 HAL_CBACK(bt_hal_cbacks, discovery_state_changed_cb 调用替换为:
- bt_hal_cbacks->discovery_state_changed_cb(state)
但是这不是重点, 重点是 bt_hal_cbacks 初始化以及 这些回调函数的含义
3.2 bt_callbacks_t
- system/btif/src/bluetooth.cc
static bt_callbacks_t* bt_hal_cbacks = NULL;
typedef struct {/** set to sizeof(bt_callbacks_t) */size_t size;adapter_state_changed_callback adapter_state_changed_cb;adapter_properties_callback adapter_properties_cb;remote_device_properties_callback remote_device_properties_cb;device_found_callback device_found_cb;discovery_state_changed_callback discovery_state_changed_cb; // 会调用到这里pin_request_callback pin_request_cb;ssp_request_callback ssp_request_cb;bond_state_changed_callback bond_state_changed_cb;acl_state_changed_callback acl_state_changed_cb;callback_thread_event thread_evt_cb;dut_mode_recv_callback dut_mode_recv_cb;le_test_mode_callback le_test_mode_cb;energy_info_callback energy_info_cb;
} bt_callbacks_t;
bt_callbacks_t
结构体是 Bluetooth HAL(硬件抽象层) 定义的一组回调接口,用于 Native 层通过回调与上层框架通信,比如 Java 层或者 JNI 层。
回调函数名 | 作用说明 | 触发时机 / 事件说明 |
---|---|---|
adapter_state_changed_cb | 通知适配器状态变化(开/关) | 当 BluetoothAdapter.enable() 或 disable() 被调用后,适配器状态变化时触发,如 BT_STATE_ON 或 BT_STATE_OFF |
adapter_properties_cb | 返回适配器属性(如名称、地址) | 当上层请求读取或设置蓝牙本地适配器属性,如调用 getAdapterProperty() 或 setAdapterProperty() |
remote_device_properties_cb | 返回远程设备的属性(如名称、class) | 上层请求远程设备属性,或发现设备时获取其属性后触发 |
device_found_cb | 通知发现了新的远程设备 | 调用 startDiscovery() 后,在扫描过程中每发现一个新设备就会触发 |
discovery_state_changed_cb | 扫描状态变化 | 调用 startDiscovery() 或 cancelDiscovery() 后,通知开始或结束扫描 |
pin_request_cb | 要求输入 PIN 码配对 | 当连接传统蓝牙设备(BR/EDR)时需要输入 PIN 码进行配对时触发 |
ssp_request_cb | 要求进行安全简单配对(SSP) | 设备配对时支持 SSP 模式,进行确认、比较、输入密钥等操作时触发 |
bond_state_changed_cb | 配对状态变化 | 设备配对成功或失败时调用,如从 BOND_BONDING → BOND_BONDED |
acl_state_changed_cb | ACL 连接状态变化 | 蓝牙链路层连接或断开时调用(所有设备连接都会有 ACL 层) |
thread_evt_cb | 通知线程附加或分离 | JNI 层使用,用于线程绑定和解绑当前线程到 JVM(Attach/Detach) |
dut_mode_recv_cb | DUT 模式接收数据 | 当设备处于 DUT 模式(测试模式)下收到测试数据时触发(调试使用) |
le_test_mode_cb | LE 测试模式回调 | BLE 专用的 TX/RX 测试事件的结果回调(蓝牙 SIG 测试场景) |
energy_info_cb | 蓝牙能耗信息回调 | 请求能耗信息时(比如上层调用 requestControllerEnergyInfo() )触发,回调耗电数据 |
3.3 bt_hal_cbacks 如何初始化的
- system/btif/src/bluetooth.cc
static bt_callbacks_t* bt_hal_cbacks = NULL;
那这里的 bt_hal_cbacks 是如何初始化的?
- android/app/jni/com_android_bluetooth_btservice_AdapterService.cpp
static bool initNative(JNIEnv* env, jobject obj, jboolean isGuest,jboolean isCommonCriteriaMode, int configCompareResult,jobjectArray initFlags, jboolean isAtvDevice,jstring userDataDirectory) {// 将 sBluetoothCallbacks 传递出去
int ret = sBluetoothInterface->init(&sBluetoothCallbacks, isGuest == JNI_TRUE ? 1 : 0,isCommonCriteriaMode == JNI_TRUE ? 1 : 0, configCompareResult, flags,isAtvDevice == JNI_TRUE ? 1 : 0, user_data_directory);}static bt_callbacks_t sBluetoothCallbacks = {sizeof(sBluetoothCallbacks),adapter_state_change_callback,adapter_properties_callback,remote_device_properties_callback,device_found_callback,discovery_state_changed_callback,pin_request_callback,ssp_request_callback,bond_state_changed_callback,address_consolidate_callback,le_address_associate_callback,acl_state_changed_callback,callback_thread_event,dut_mode_recv_callback,le_test_mode_recv_callback,energy_info_recv_callback,link_quality_report_callback,generate_local_oob_data_callback,switch_buffer_size_callback,switch_codec_callback};
-
当我们拉起我们的 蓝牙进程服务时, 将触发 initNative 调用
-
此时通过 sBluetoothInterface->init( &sBluetoothCallbacks
- 将我们的 sBluetoothCallbacks 传递出去。
-
system/btif/src/bluetooth.cc
static int init(bt_callbacks_t* callbacks, bool start_restricted,bool is_common_criteria_mode, int config_compare_result,const char** init_flags, bool is_atv,const char* user_data_directory) {set_hal_cbacks(callbacks); // 直接将 callbacks 给了 bt_hal_cbacks}void set_hal_cbacks(bt_callbacks_t* callbacks) { bt_hal_cbacks = callbacks; }
- 这里所有的 hal 回调都将回调到 sBluetoothCallbacks 中。
4. 小结
bt_jni_thread
是 AOSP 蓝牙系统中 native → Java 方向的专用线程桥梁,同时也承担部分 profile 层轻量控制任务的执行职责。它不是全能线程,但在 JNI 回调中不可或缺。
为什么要有 bt_jni_thread
原因 | 解释 |
---|---|
保证 JVM attach | 不同 native 回调线程不一定 attach 了 JVM |
避免跨线程调用 Java | Android 不允许非 JVM 线程直接访问 Java |
提高模块解耦 | 各个 profile 回调逻辑统一封装、集中调度 |
提升线程安全 | 所有 JNI 回调集中在一个线程处理,避免竞态 |
核心职责
类别 | 说明 |
---|---|
上行事件调度 | 负责 native → Java 的事件回调,例如设备发现、配对状态变化等 |
JVM 安全桥梁 | 由于 native 回调来自蓝牙堆栈中的多个线程,bt_jni_thread 保证在 JVM 附着线程中调用 Java |
profile 模块轻量任务 | 某些模块(如 HFP client、AVRCP、GATT)内部异步任务也在此线程中执行 |
使用场景举例
上行事件来源 | 对应 Java 回调函数 |
---|---|
adapter_state_changed_cb | onAdapterStateChanged() |
device_found_cb | onDeviceFound() |
bond_state_changed_cb | onBondStateChanged() |
acl_state_changed_cb | onConnectionStateChanged() |
btif_gatt_client_* 回调 | GATT 连接、通知、读写特征值等 |
hfp_client_callbacks | HFP 状态变化、音频连接事件 |
看到这里大家可以思考一下问题:
- native <-> java 上下行的事件, 一定都要 放在 bt_jni_thread 线程中执行吗?
- 答案