【android bluetooth 框架分析 01】【关键线程 2】【bt_stack_manager_thread线程介绍】
1. bt_stack_manager_thread
bt_stack_manager_thread
是蓝牙协议栈中的核心调度线程,负责串行化处理协议栈的生命周期事件,包括初始化、启动、关闭与清理操作。它确保这些状态切换在同一线程中按顺序执行,避免竞态和资源冲突。作为蓝牙栈的“总指挥”,它协调模块管理器、协议子系统(如 L2CAP、GATT、BTM)、以及 JNI 层之间的事件传递,是协议栈有序启动与安全退出的关键保障。
本节就来详细介绍一下 bt_stack_manager_thread 线程。
1.1 线程启动流程
static MessageLoopThread management_thread("bt_stack_manager_thread");static void ensure_manager_initialized() {if (management_thread.IsRunning()) return;management_thread.StartUp(); // 这里启动我们的 bt_stack_manager_thread 线程if (!management_thread.IsRunning()) {LOG_ERROR("%s unable to start stack management thread", __func__);return;}
}const stack_manager_t* stack_manager_get_interface() {ensure_manager_initialized();return &interface;
}
调用流程如下:
com_android_bluetooth_btservice_AdapterService.cpp
static void classInitNative(JNIEnv* env, jclass clazz) {if (hal_util_load_bt_library((bt_interface_t const**)&sBluetoothInterface)) {ALOGE("No Bluetooth Library found");}
}int hal_util_load_bt_library(const bt_interface_t** interface) {*interface = &bluetoothInterface;return 0;}
- 通过 hal_util_load_bt_library 加载 sBluetoothInterface
- 此时加载的接口是 bluetoothInterface , 这个接口是 bluetooth.cc 中的
- system/btif/src/bluetooth.cc
EXPORT_SYMBOL bt_interface_t bluetoothInterface = {sizeof(bluetoothInterface),init, // 这个里面 创建了 bt_stack_manager_thread 线程enable,disable,cleanup,get_adapter_properties,get_adapter_property,set_adapter_property,get_remote_device_properties,get_remote_device_property,set_remote_device_property,nullptr,get_remote_services,start_discovery,cancel_discovery,create_bond,create_bond_out_of_band,remove_bond,cancel_bond,get_connection_state,pin_reply,ssp_reply,get_profile_interface,dut_mode_configure,dut_mode_send,le_test_mode,set_os_callouts,read_energy_info,dump,dumpMetrics,config_clear,interop_database_clear,interop_database_add,get_avrcp_service,obfuscate_address,get_metric_id,set_dynamic_audio_buffer_size,generate_local_oob_data,allow_low_latency_audio,clear_event_filter,load_remote_oob_data,metadata_changed};
函数名 | 功能说明 |
---|---|
size | 接口结构体的大小,必须设置为 sizeof(bt_interface_t) |
init | 初始化 Bluetooth 接口,注册回调函数,is_atv 表示是否为 Android TV |
enable | 启用蓝牙,可指定是否为访客模式(guest_mode) |
disable | 禁用蓝牙 |
cleanup | 清理接口,释放资源 |
get_adapter_properties | 获取蓝牙适配器的所有属性 |
get_adapter_property | 获取指定类型的蓝牙适配器属性 |
set_adapter_property | 设置指定类型的蓝牙适配器属性(如地址、设备名等) |
get_remote_device_properties | 获取远程设备的所有属性 |
get_remote_device_property | 获取远程设备指定类型的属性 |
set_remote_device_property | 设置远程设备指定类型的属性 |
get_remote_service_record | 获取远程设备指定 UUID 的服务记录 |
get_remote_services | 执行 SDP 查询,获取远程设备提供的所有服务 |
start_discovery | 开始设备扫描(发现附近蓝牙设备) |
cancel_discovery | 取消设备扫描 |
create_bond | 建立配对连接(bonding) |
create_bond_out_of_band | 使用 OOB 数据建立配对连接 |
remove_bond | 移除设备配对信息 |
cancel_bond | 取消正在进行的配对 |
get_connection_state | 获取指定设备的连接状态(0=未连接,非0=已连接) |
pin_reply | 针对传统配对流程返回 PIN 码 |
ssp_reply | 针对 SSP(安全简单配对)流程返回结果,例如确认或输入 PIN |
get_profile_interface | 获取指定蓝牙 profile 的接口,例如 A2DP、HFP 等 |
dut_mode_configure | 设置 DUT(Device Under Test)模式,开启蓝牙测试模式 |
dut_mode_send | 向控制器发送 HCI 测试命令,仅限 DUT 模式下使用 |
le_test_mode | 发送 BLE 测试模式命令,如接收测试、发射测试等 |
set_os_callouts | 设置 OS 层接口,如警报、唤醒锁的调用方式 |
read_energy_info | 读取能量信息(控制器电量、耗电情况) |
dump | 用于 dumpsys 命令输出调试信息 |
config_clear | 清除 /data/misc/bt_config.conf 中的蓝牙配置信息 |
interop_database_clear | 清除设备兼容性数据库的动态部分 |
interop_database_add | 向兼容性数据库添加设备 workarounds 条目,用于特定厂商设备问题处理 |
- 在 init 中我们触发创建了 bt_stack_manager_thread 线程
- 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) {// 调用 init 函数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);}
- 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) {(void)user_data_directory;LOG_INFO("%s: start restricted = %d ; common criteria mode = %d, config compare ""result = %d",__func__, start_restricted, is_common_criteria_mode,config_compare_result);...// 这里调用了 stack_manager_get_interface()stack_manager_get_interface()->init_stack();return BT_STATUS_SUCCESS;
}
- system/btif/src/stack_manager.cc
const stack_manager_t* stack_manager_get_interface() {ensure_manager_initialized();return &interface;
}static void ensure_manager_initialized() {if (management_thread.IsRunning()) return;// 这里启动了 bt_stack_manager_thread 线程management_thread.StartUp();if (!management_thread.IsRunning()) {LOG_ERROR("%s unable to start stack management thread", __func__);return;}
}
1.2 如何工作的?
上面小节 介绍了 management_thread 是如何启动的,本节介绍是如何 工作的。
- system/btif/src/stack_manager.cc
static const stack_manager_t interface = {init_stack, start_up_stack_async,shut_down_stack_async, clean_up_stack,get_stack_is_running};const stack_manager_t* stack_manager_get_interface() {ensure_manager_initialized();return &interface;
}
- stack manager 暴露了如下接口
- init_stack -> event_init_stack
- start_up_stack_async -> event_start_up_stack
- shut_down_stack_async -> event_shut_down_stack
- clean_up_stack -> event_clean_up_stack
- get_stack_is_running -> return stack_is_running
static void init_stack() {semaphore_t* semaphore = semaphore_new(0);management_thread.DoInThread(FROM_HERE,base::Bind(event_init_stack, semaphore));semaphore_wait(semaphore);semaphore_free(semaphore);
}
- management_thread.DoInThread 将 event_init_stack 任务交给 management_thread 线程处理。
- 其他接口类似
咱们就来通俗地剖析一下 bt_stack_manager_thread
的工作流程,重点分析提到的几个事件:
-
event_init_stack
-
event_start_up_stack
-
event_shut_down_stack
-
event_clean_up_stack
bt_stack_manager_thread 是啥?
这是一个 专门用于管理蓝牙协议栈生命周期的线程,负责按顺序执行蓝牙栈的几个重要状态转换:
-
初始化蓝牙栈(Init)
-
启动蓝牙栈(StartUp)
-
关闭蓝牙栈(ShutDown)
-
清理资源(CleanUp)
每个状态对应一个事件(event),这些事件会被派发到 bt_stack_manager_thread
这个线程去处理,避免多线程并发操作蓝牙栈带来的崩溃或竞态问题。
1. event_init_stack
🕰️ 触发时机:当系统 拉起 AdapterService 服务时自动触发。
🔧 做了啥:
public class AdapterService extends Service {static {classInitNative(); // 会最终触发调用 event_init_stack}}
# 具体看 2.1 线程启动流程
[classInitNative] -> [sBluetoothInterface->init]-> [stack_manager_get_interface()->init_stack()]
👉 具体做了:
// `context`: 上层传入的信号量指针(semaphore),用于同步等待(也可能为 NULL)// 函数是异步派发到 `bt_stack_manager_thread` 中执行的,避免主线程卡顿static void event_init_stack(void* context) {// 如果 context 有值,表示调用方需要知道初始化是否完成(用信号量机制通知)semaphore_t* semaphore = (semaphore_t*)context;LOG_INFO("is initializing the stack");// 避免重复初始化,保证蓝牙栈只初始化一次// 在热启动或某些异常恢复流程中非常重要if (stack_is_initialized) {LOG_INFO("found the stack already in initialized state");} else {/*初始化内部的模块管理系统,比如创建 module 列表、管理模块状态等。所有后续 `module_init()`、`module_start_up()` 都依赖它。*/module_management_start();// 初始化 操作系统抽象层(OSI),提供 log、alarm、semaphore、thread 管理等基础功能。module_init(get_local_module(OSI_MODULE));// 蓝牙通用工具模块,如设备地址工具、设备信息解析、通用接口辅助。module_init(get_local_module(BT_UTILS_MODULE));// 负责启动 [GD(Gabeldorsche)](https://source.android.com/docs/core/connect/bluetooth/gd) 框架中的 idle 模块,为新架构蓝牙做准备(Android 11+ 引入)。module_start_up(get_local_module(GD_IDLE_MODULE));// 负责加载并管理配置文件(`bt_config.conf`),如配对记录、配对密钥等。module_init(get_local_module(BTIF_CONFIG_MODULE));/*Bluetooth Interface(BTIF)层初始化,它是 JNI 和 native stack 之间的桥梁 例如处理 Java 层调用 enable()、disable() 等动作*/btif_init_bluetooth();/*初始化设备兼容性工作模块,用于处理某些设备的特定 workaround(如音响连接问题)*/module_init(get_local_module(INTEROP_MODULE));/*这是真正启动 classic Bluetooth stack 的核心入口:初始化 HCI 模块启动 GAP、SDP、BNEP、RFCOMM 等协议层注册 internal 回调机制*/bte_main_init();// 负责加载栈的启动参数配置,比如 Feature Flag、调试开关、协议开关等module_init(get_local_module(STACK_CONFIG_MODULE));// stack init is synchronous, so no waiting necessary here// 标记栈初始化完成stack_is_initialized = true;}LOG_INFO("finished");// 发信号(通知等待线程)if (semaphore) semaphore_post(semaphore);
}
- 调用
init_stack()
:
步骤 | 模块 | 含义 |
---|---|---|
1 | module_management_start() | 启动模块系统 |
2 | OSI_MODULE | 基础操作系统抽象 |
3 | BT_UTILS_MODULE | 蓝牙通用工具 |
4 | GD_IDLE_MODULE | 新架构蓝牙 IDLE |
5 | BTIF_CONFIG_MODULE | 配对信息读取模块 |
6 | btif_init_bluetooth() | 初始化 JNI 层接口 |
7 | INTEROP_MODULE | 兼容性模块 |
8 | bte_main_init() | 启动 classic 蓝牙协议栈 |
9 | STACK_CONFIG_MODULE | 加载栈配置项 |
-
event_init_stack()
负责蓝牙栈中最基础的模块初始化,所有后续功能都依赖它的正确执行。 -
如果这一步挂了,蓝牙后续一切功能都不会工作。
-
常见触发点:应用层
enable()
、蓝牙重启、系统冷启动。拉起蓝牙服务时,就会触发
举例:你点开手机蓝牙开关,就会触发这个流程
2. event_start_up_stack
触发时机:当系统调用 开启蓝牙后, 各个profile 服务启动时,就会触发, 这里拿 GattService 举例,
01-10 01:47:11.513188 2008 2008 D GattService: doStart()01-10 01:47:11.552258 2008 2008 D BluetoothAdapterService: processProfileServiceStateChanged: GattService, state: 1201-10 01:47:11.552428 2008 2539 I bt_stack_manager: packages/modules/Bluetooth/system/btif/src/stack_manager.cc:283 event_start_up_stack: event_start_up_stack is bringing up the stack
static int enable() {if (!interface_ready()) return BT_STATUS_NOT_READY;stack_manager_get_interface()->start_up_stack_async();return BT_STATUS_SUCCESS;
}
调用流程:
[ProfileService:doStart] -> [mAdapterService.onProfileServiceStateChanged(this, BluetoothAdapter.STATE_ON);] ->[processProfileServiceStateChanged((ProfileService) msg.obj, msg.arg1);] ->[enableNative();] ->[sBluetoothInterface->enable();] ->[stack_manager_get_interface()->start_up_stack_async();]
- 上层 profile server 都是继承 ProfileService 。
event_start_up_stack()
逻辑:
是 Bluetooth Stack 启动的第二阶段:event_start_up_stack()
,它是在 event_init_stack()
成功执行之后调用的,用于 启动协议栈功能层模块(包括 L2CAP、SDP、GATT、A2DP、RFCOMM、HID 等),完成蓝牙的“上电”启动。
// Synchronous function to start up the stack
static void event_start_up_stack(UNUSED_ATTR void* context) {// 防重复启动if (stack_is_running) {LOG_INFO("%s stack already brought up", __func__);return;}// 如果 event_init_stack() 没跑,就跑一次,确保一切基础组件就绪ensure_stack_is_initialized();LOG_INFO("%s is bringing up the stack", __func__);// 创建 future,用于异步等待// 用来等待整个启动过程中某些异步操作完成,比如 controller 模块初始化future_t* local_hack_future = future_new();hack_future = local_hack_future;// 清理旧模块 + 启动新模块LOG_INFO("%s Gd shim module enabled", __func__);module_shut_down(get_local_module(GD_IDLE_MODULE)); // 关闭 GD_IDLE_MODULE(空模块)get_btm_client_interface().lifecycle.btm_init(); // 初始化 BTM 模块(底层管理模块)module_start_up(get_local_module(GD_SHIM_MODULE)); // 启动 GD_SHIM_MODULE:GD 是新蓝牙架构模块(Android 11+)module_start_up(get_local_module(BTIF_CONFIG_MODULE)); // BTIF_CONFIG 再次启动,以支持后续读写 // 初始化协议栈模块(重点)l2c_init(); // L2CAP 层sdp_init(); // 服务发现协议gatt_init(); // GATT 层(BLE)SMP_Init(); // 安全配对协议(BLE 安全)get_btm_client_interface().lifecycle.btm_ble_init(); // BLE BTM 层RFCOMM_Init(); // 串口仿真层BNEP_Init(); // 蓝牙以太网仿真协议PAN_Init(); // 个人局域网协议A2DP_Init(); // 高质量音频传输AVRC_Init(); // 遥控控制协议GAP_Init(); // 通用访问协议HID_HostInit(); // 人机交互设备(鼠标、键盘)bta_sys_init(); // 初始化 BTA 系统核心调度bta_ar_init(); // 注册音频相关 profile(如 A2DP + AVRCP)// 启用调试日志收集,便于排查协议过程中的问题module_init(get_local_module(BTE_LOGMSG_MODULE));// 启动主线程 bt_main_thread// 主线程处理主状态机事件(如连接、断开、认证请求)main_thread_start_up();// JNI Bridge 初始化完毕,通知上层btif_init_ok(); // JNI 层回调标志:Bluetooth Native 已准备好// 启动核心状态管理模块 BTA_dm_init(); // Device Manager 初始化bta_dm_enable(bte_dm_evt); // 启动 DM 并注册事件处理器// 控制器初始化 & 状态同步bta_set_forward_hw_failures(true); // 设置硬件错误转发btm_acl_device_down(); // 清除旧 ACL 连接CHECK(module_start_up(get_local_module(GD_CONTROLLER_MODULE))); // 控制器启动BTM_reset_complete(); // 标记芯片初始化完成// 通知上层 “蓝牙已上电”BTA_dm_on_hw_on(); // 通知 BTA 层 “controller 就绪”// 等待异步 future 完成if (future_await(local_hack_future) != FUTURE_SUCCESS) {LOG_ERROR("%s failed to start up the stack", __func__);stack_is_running = true; // So stack shutdown actually happensevent_shut_down_stack(nullptr); // 自动触发关闭// 如果 controller 初始化失败,则强制回滚,触发 `event_shut_down_stack()`return;}// 启动成功,通知 JNI 层// 通知 Android Framework 蓝牙栈已准备完成,可以对外服务!stack_is_running = true;LOG_INFO("%s finished", __func__);do_in_jni_thread(FROM_HERE, base::Bind(event_signal_stack_up, nullptr));
}
-
启动模块(有顺序):
-
shim
,Gki
,Bte_main
,BTIF_CONFIG_MODULE
,BTIF_CONFIG_INTERFACE
-
启动 HCI 模块(就是控制器通信模块)
-
启动 profile,如 A2DP、HFP 等
-
-
设置 HAL 状态为
BT_STATE_ON
-
回调通知上层 “蓝牙启动成功”
功能 | 内容 |
---|---|
协议层初始化 | L2CAP、SDP、GATT、SMP |
Profile 初始化 | A2DP、AVRCP、HID、PAN、RFCOMM |
Controller 初始化 | 启动蓝牙芯片控制器通信能力 |
上报系统状态 | 通知 JNI “蓝牙栈已准备” |
错误回滚 | 如果启动失败,立即关闭蓝牙栈防止残留 |
举例:你点开蓝牙开关之后,系统蓝牙图标亮起,表示可以连接设备了
3. event_shut_down_stack
触发时机:调用 disable()
或系统关机前触发
event_shut_down_stack()
做的事:
这是 Bluetooth 栈生命周期中第三阶段:event_shut_down_stack()
。它负责在关闭蓝牙或失败回滚时,干净、安全地关闭所有协议模块、资源清理、通知上层状态变更。
static void event_shut_down_stack(UNUSED_ATTR void* context) {// 防止重复关闭if (!stack_is_running) {LOG_INFO("%s stack is already brought down", __func__);return;}LOG_INFO("%s is bringing down the stack", __func__);// 创建异步 future + 设置状态// 后续异步流程依然使用 `future` 来等待任务结束future_t* local_hack_future = future_new();hack_future = local_hack_future;stack_is_running = false;// 清理 BLE 功能, 这些操作是异步的,在 `main_thread` 中执行do_in_main_thread(FROM_HERE, base::Bind(&btm_ble_multi_adv_cleanup)); // 清理 BLE 广播(BLE Advertising)do_in_main_thread(FROM_HERE, base::Bind(&btm_ble_scanner_cleanup)); // 清理 BLE 扫描器(BLE Scanner)// 清理 Profile 层, 将所有对外连接/监听都关闭btif_dm_on_disable(); // 关闭设备管理模块btif_sock_cleanup(); // 清除 socket serverbtif_pan_cleanup(); // PAN profile 清理// 停止设备管理// `bta_dm_disable()` 停止连接管理、bonding、搜索等do_in_main_thread(FROM_HERE, base::Bind(bta_dm_disable));// 等待任务完成,确保状态稳定future_await(local_hack_future);// 协议核心关闭local_hack_future = future_new();hack_future = local_hack_future;bta_sys_disable(); // 停止 BTA 系统任务调度bta_set_forward_hw_failures(false); // 关闭错误转发BTA_dm_on_hw_off(); // 通知 BTA 硬件已断电// 模块卸载module_shut_down(get_local_module(BTIF_CONFIG_MODULE));future_await(local_hack_future); // 等待卸载完成main_thread_shut_down(); // 主线程停止任务调度// 内存释放:GATT/SDP/L2CAP/BTM// 完全释放底层协议分配的资源和内存module_clean_up(get_local_module(BTE_LOGMSG_MODULE)); // 日志系统清理gatt_free();sdp_free();l2c_free();get_btm_client_interface().lifecycle.btm_ble_free(); // BLE BTM 释放LOG_INFO("%s Gd shim module disabled", __func__);// 关闭 GD 模块,恢复 Idle 状态module_shut_down(get_local_module(GD_SHIM_MODULE)); // GD 模块关闭get_btm_client_interface().lifecycle.btm_free(); // BTM 模块释放module_start_up(get_local_module(GD_IDLE_MODULE)); // 回归 idle 模块状态// 通知 JNI:蓝牙栈已关闭// 通知 Android Framework,**蓝牙状态 = 已关闭**hack_future = future_new();do_in_jni_thread(FROM_HERE, base::Bind(event_signal_stack_down, nullptr));future_await(hack_future);LOG_INFO("%s finished", __func__);
}
步骤 | 动作 |
---|---|
异步清理 BLE 功能模块 | 多播、扫描器 |
清理 Profile 服务 | socket、PAN |
停止设备管理任务 | bta_dm_disable |
关闭主线程 | 清理主事件调度系统 |
清除协议栈内存 | gatt, sdp, l2cap 等 |
切换模块状态 | GD_SHIM → GD_IDLE |
通知 Android 上层 | 蓝牙栈关闭完成 |
-
停用所有模块(reverse 顺序)
-
先断开 Profile(如 A2DP、HFP)
-
再停 HCI、BTIF、Main、Gki、Shim 等
-
-
设置 HAL 状态为
BT_STATE_OFF
-
回调通知上层 “蓝牙已关闭”
举例:你点关蓝牙,会先触发这个阶段
4. event_clean_up_stack
触发时机:shut_down_stack()
执行完后触发
``event_clean_up_stack() 做的事:
static void event_clean_up_stack(std::promise<void> promise) {if (!stack_is_initialized) {LOG_INFO("%s found the stack already in a clean state", __func__);goto cleanup;}ensure_stack_is_not_running();LOG_INFO("%s is cleaning up the stack", __func__);stack_is_initialized = false;btif_cleanup_bluetooth();module_clean_up(get_local_module(STACK_CONFIG_MODULE));module_clean_up(get_local_module(INTEROP_MODULE));module_clean_up(get_local_module(BTIF_CONFIG_MODULE));module_clean_up(get_local_module(BT_UTILS_MODULE));module_clean_up(get_local_module(OSI_MODULE));module_shut_down(get_local_module(GD_IDLE_MODULE));module_management_stop();LOG_INFO("%s finished", __func__);cleanup:;promise.set_value();
}
-
完全清理模块管理器
module_clean_up(get_module_manager())
-
释放 alarm、timer、logging 等资源
-
蓝牙模块生命周期彻底结束,等待下一次 init
举例:系统重启中、蓝牙长时间不用需要彻底释放资源
1.3 Bluetooth Stack 生命周期概览
阶段 | 关键函数 | 功能 | 核心动作 |
---|---|---|---|
初始化 | event_init_stack() | 准备 Stack 的基本模块 | 初始化模块、配置、btif_init_bluetooth() 等 |
启动 | event_start_up_stack() | 启动完整蓝牙协议栈 | 启动 L2CAP/GATT/A2DP 等,初始化 BTM、BTA |
关闭 | event_shut_down_stack() | 停止蓝牙协议栈 | 停止 BTA、释放协议栈模块 |
清理 | event_clean_up_stack() | 完全清理所有模块 | 清除 BTIF/UTILS/OSI 模块,停止管理器 |
event_init_stack()
-
触发时机:
bt_interface_t.init()
时,Bluetooth HAL 初始化蓝牙栈。 -
作用:初始化蓝牙相关基础模块。
-
关键逻辑:
-
检查是否已初始化(
stack_is_initialized
) -
启动模块管理器
-
初始化:
OSI_MODULE
,BT_UTILS_MODULE
,INTEROP_MODULE
,STACK_CONFIG_MODULE
-
初始化蓝牙框架层:
btif_init_bluetooth()
,bte_main_init()
-
设置标志:
stack_is_initialized = true
-
event_start_up_stack()
-
作用:启动所有核心蓝牙协议。
-
关键逻辑:
-
启动 BTIF_CONFIG 模块、关闭 GD_IDLE 模块,初始化 BLE 和 GATT 协议等
-
启动协议层模块:L2CAP、RFCOMM、A2DP、HID、GAP、AVRCP、BNEP、PAN 等
-
启动 BTA(Bluetooth Application layer)
-
通知 JNI 层:
event_signal_stack_up
-
event_shut_down_stack()
-
作用:优雅地关闭所有协议模块,释放资源。
-
关键逻辑:
-
调用 cleanup 方法:
btif_dm_on_disable
,btif_sock_cleanup
等 -
关闭:BTA、GATT、L2CAP、GAP、BNEP、SHIM、BTM
-
通知 JNI:
event_signal_stack_down
-
event_clean_up_stack()
-
作用:彻底清理蓝牙栈模块和资源。
-
关键逻辑:
-
只有在
stack_is_initialized
为 true 且 stack 已停止时执行 -
清理:BTIF_CONFIG, INTEROP, STACK_CONFIG, OSI, BT_UTILS
-
停止模块管理器
-
相关文章:
【android bluetooth 框架分析 01】【关键线程 2】【bt_stack_manager_thread线程介绍】
1. bt_stack_manager_thread bt_stack_manager_thread 是蓝牙协议栈中的核心调度线程,负责串行化处理协议栈的生命周期事件,包括初始化、启动、关闭与清理操作。它确保这些状态切换在同一线程中按顺序执行,避免竞态和资源冲突。作为蓝牙栈的…...
GEO, TCGA 等将被禁用?!这40个公开数据库可能要小心使用了
GEO, TCGA 等将被禁用?!这40个公开数据库可能要小心使用了 最近NIH公共数据库开始对中国禁用的消息闹得风风火火: 你认为研究者上传到 GEO 数据库上的数据会被禁用吗? 单选 会,毕竟占用存储资源 不会,不…...
matlab安装python API 出现Invalid version: ‘R2022a‘,
打开 setup.py 文件,找到设置版本号的部分 将 versionR2022a 修改为符合 Python 版本号规范的格式,例如 version2022.1 保存 setup.py 文件...
【ROS 通信】Services 服务通信
【ROS】Service 服务通信 前言前置操作创建一个 tutorial 功能包定义服务接口修改 CMakeLists.txt 文件修改 find_package修改 add_service_files修改 generate_messages修改 catkin_packagefind_package 和 catkin_package 修改 package.xml 文件构建 服务通信的 Python 实现服…...
25.4.8学习总结
javaFX实现倒计时 核心概念 Timeline: Timeline 是JavaFX动画API的核心类,用于创建动画。它可以按照指定的时间间隔(Duration)触发事件(KeyFrame)。 可以将其视为一个定时器,每隔一段时间执行一些操作。 …...
Android audio(6)-audiopolicyservice介绍
AudioPolicyService 是策略的制定者,比如某种 Stream 类型不同设备的音量(index/DB)是多少、某种 Stream 类型的音频数据流对应什么设备等等。而 AudioFlinger 则是策略的执行者,例如具体如何与音频设备通信,维护现有系…...
【区块链安全 | 第三十八篇】合约审计之获取私有数据(二)
文章目录 前言漏洞代码代码审计攻击步骤修复/开发建议审计思路 前言 在【区块链安全 | 第三十七篇】合约审计之获取私有数据(一)中,介绍了私有数据、访问私有数据实例、Solidity 中的数据存储方式等知识,本文通过分析具体合约代码…...
muduo:运行起来
Muduo 概述 Muduo 是一个用 C 编写的高性能网络库,由陈硕开发,主要用于开发 Linux 环境下的高性能网络应用程序。以下从几个方面对其进行详细介绍: 特点 事件驱动与非阻塞 I/O:Muduo 基于 Reactor 模式实现,使用了 …...
算法篇(八)【递归】
一、了解递归 1. 什么是递归? 递归就是自己调用自己 递归的概念解释起来就短短的几句话,但是写起来总是无从下手 ,但是首先要相信,在学过了数据结构 -- 树 之后 , 其实就已经具备了一定的递归思想,接下来的…...
Linux 学习笔记(4):cd 与 pwd 命令的深度解析与实战应用(期末、期中复习必备)
前言 一、cd 命令:切换工作目录的利器 1.命令来源与基本语法 2.命令使用示例 3.相对路径与绝对路径的使用 二、pwd 命令:清晰定位当前工作目录 1.命令来源与基本语法 2.命令使用示例 三、结语 前言 在 Linux 系统的操作中,对工作目录的…...
眨眼睛查看密码工具类
“眨眼睛查看密码”工具类实现思路: 一、核心功能 实现点击眼睛图标切换密码明文/星号显示,提升表单输入体验。包含以下关键功能: • 初始状态:密码框显示为星号,闭眼图标可见。 • 点击闭眼图标:切换为明…...
【嵌入式系统设计师】知识点:第9章 嵌入式系统安全性基础知识
提示:“软考通关秘籍” 专栏围绕软考展开,全面涵盖了如嵌入式系统设计师、数据库系统工程师、信息系统管理工程师等多个软考方向的知识点。从计算机体系结构、存储系统等基础知识,到程序语言概述、算法、数据库技术(包括关系数据库、非关系型数据库、SQL 语言、数据仓库等)…...
find指令中使用正则表达式
linux查找命令能结合正则表达式吗 find命令要使用正则表达式需要结合-regex参数 另,-type参数可以指定查找类型(f为文件,d为文件夹) rootlocalhost:~/regular_expression# ls -alh 总计 8.0K drwxr-xr-x. 5 root root 66 4月 8日 16:26 . dr-xr-…...
【RH124】第六章 管理本地用户和组
系列文章目录 第一章 红帽企业Linux入门 第二章 访问命令行 第三章 从命令行管理文件 第五章 创建、查看文本文件 第六章 管理本地用户和组 文章目录 系列文章目录前言一、用户和组1、用户2、组 二、获取超级用户访问权限1、root用户2、切换用户账户3、sudo配置 三、管理本地用…...
Linux学习笔记——中断
中断 硬中断和软中断的定义与区别硬中断(Hardware Interrupt)软中断(Software Interrupt) 硬中断与软中断的区别总结上半部和下半部机制详解为什么要分为上半部和下半部?上半部下半部 下半部的三种实现机制Linux中断响…...
Linux 进程间通信:信号机制
Linux 进程间通信:信号机制 在多进程操作系统中,进程之间的通信至关重要,尤其是在Linux系统中,信号(Signal)作为一种特殊的进程间通信方式,广泛用于进程之间的协调和控制。信号可以看作是操作系…...
计算机控制系统:arduino控制无源滤波器播放音乐
1 电脑播放简单音节 播放哆瑞咪发嗦啦西 在音频处理领域,声音合成是通过计算机算法模拟人类声音的一种技术。具体来说,模拟哆瑞咪发嗦啦西音节需要声卡先接收基本音符的信号,然后通过数字信号处理技术,将该信号转换为模拟声音输出…...
FTP协议和win server2022安装ftp
FTP协议简介 FTP(File Transfer Protocol,文件传输协议)是一种用于在网络上的计算机之间传输文件的标准网络协议。它被广泛应用于服务器与客户端之间的文件上传、下载以及管理操作。FTP支持多种文件类型和结构,并提供了相对简单的…...
UNet 改进(4):融合Ghost Module的轻量化分割网络
引言 在计算机视觉领域,U-Net因其优秀的性能在图像分割任务中广受欢迎。 随着模型复杂度的增加,计算资源和内存消耗也大幅上升。 本文将介绍一种改进的U-Net架构,通过引入Ghost Module来实现模型的轻量化,同时保持分割性能。 代码概述 这个实现构建了一个基于U-Net架构…...
香港VPS服务器如何优化CPU和内存使用率?
# 香港VPS服务器CPU与内存优化全攻略 在香港VPS服务器上优化CPU和内存使用率是提升性能、降低成本的关键。以下是经过验证的优化策略,涵盖从系统配置到应用层调优的全方位方案。 ## 一、系统级优化 ### 1. 内核参数调优 **调整swappiness值**(减少交…...
简单-快速-高效——模块化解析controlnet网络结构
资源 ControlNet论文:Adding Conditional Control to Text-to-Image Diffusion Models 官方项目:lllyasviel/ControlNet: Let us control diffusion models ControlNet 1.1项目地址:lllyasviel/ControlNet-v1-1-nightly diffusers框架的Co…...
C语言:字符串处理函数strstr分析
在 C 语言中,strstr 函数用于查找一个字符串中是否存在另一个字符串。它的主要功能是搜索指定的子字符串,并返回该子字符串在目标字符串中第一次出现的位置的指针。如果没有找到子字符串,则返回 NULL。 详细说明: 头文件…...
跨境企业应对美国加税:策略突围与破局之道
在全球经济一体化的浪潮中,跨境企业的业务发展与国际关系、贸易政策紧密相连。美国作为全球重要的经济体,其加税行为犹如一场突如其来的风暴,给众多跨境企业的运营带来了巨大冲击与挑战。面对这一严峻形势,跨境企业若想在波涛汹涌…...
Rust 是如何层层防错的
一、Rust 的多层防错机制 🧱 第一层:Rust语言自带的“编译时护盾” —— 错误连运行都跑不起来 错误类型Rust 怎么发现的?工具/机制举个例子✅ 语法缺陷写错了代码格式或语法Rust Analyzer(智能补全)少写了分号、括号…...
一种反激变换器的设计思路(01)
反激式转换器具有低成本且易于构建的优势,常被用作功率较低设备和电器的主要电源。其中固定开关频率(FF)和可变开关频率(QR)是两种基本的操作开关模式。本案例中,输入电压(Vin)为17V…...
Streamlit性能优化:缓存与状态管理实战
目录 📌 核心特性 📌 运行原理 (1)全脚本执行 (2)差异更新 📌 缓存机制 ❓为什么使用缓存? 使用st.cache_data的优化方案 缓存适用场景 使用st.session_state的优化方案 &…...
楼宇自控系统凭何成为建筑稳定、高效、安全运行的关键
在现代建筑领域,随着建筑规模的不断扩大和功能的日益复杂,建筑的稳定、高效、安全运行成为了至关重要的课题。楼宇自控系统犹如建筑的“智慧大脑”,凭借其卓越的功能和技术,在这三个关键方面发挥着不可替代的作用,成为…...
【学习自用】配置文件中的配置项
server.port服务器端口,常被用于指定应用程序运行时所监听的端口号spring.datasource.url用于配置数据源的数据库连接URLspring.datasource.username用于指定连接数据库的用户名spring.datasource.password用于配置数据源时设置数据库连接密码的属性mybatis.mapper-…...
《解码 C/C++ 关键字:科技编程的核心指令集》
序号语言关键字原型实现原理功能返回值类型使用示例注意事项应用场景1Cautoauto 数据类型 变量名;函数调用时在栈上分配内存,函数结束自动释放声明自动变量,变量生命周期限于函数执行期间无c<br>void func() {<br> auto int num 10;<br&…...
Linux 性能调优之CPU调优认知
写在前面 博文内容为《性能之巅 系统、企业与云可观测性(第2版)》CPU 章节课后习题答案整理内容涉及: CPU 术语,指标认知CPU 性能问题分析解决CPU 资源负载特征分析应用程序用户态CPU用量分析理解不足小伙伴帮忙指正对每个人而言,真正的职责只有一个:找到自我。然后在心中…...
《P2660 zzc 种田》
题目背景 可能以后 zzc 就去种田了。 题目描述 田地是一个巨大的矩形,然而 zzc 每次只能种一个正方形,而每种一个正方形时 zzc 所花的体力值是正方形的周长,种过的田不可以再种,zzc 很懒还要节约体力去泡妹子,想花最少的体力值…...
Model Context Protocol(MCP)介绍
“Model Context Protocol(MCP)”是近年来在多模态大模型或可扩展智能系统中出现的一个概念,其主要目标是为大模型提供结构化的上下文管理和动态记忆机制。它解决的是在长时间对话、多轮交互、任务切换等复杂情境中,模型如何理解“…...
解决使用PendingIntent.getBroadcast时出现java.lang.IllegalArgumentException异常的问题
当app为targetSdk31及以上,并且在Android12及以上系统中调用PendingIntent.getBroadcast(context, 0, intent, 0)接口时会抛出异常: java.lang.IllegalArgumentException: com.haier.uhome.uplus.seasia: Targeting S (version 31 and above) requires …...
创建一个简单的HTML游戏站
创建一个简单的HTML游戏站涉及多个步骤,包括规划网站结构、设计用户界面、编写游戏逻辑以及测试和部署。下面是一个详细的步骤指南: 1. 规划网站结构 确定目标受众:了解你的目标用户群体。选择游戏类型:决定你要开发的游戏类型&…...
AIDD-人工智能药物设计-TCMP-12个公开的中药数据库
12个公开的中药数据库 数据库是中药网络药理学研究不可或缺的数据来源之一。目前已经建立了若干中药数据库,提供有关中药的各方面信息,包括疾病、方剂、草药或天然产物、生物活性成分和靶点。这些数据库成为中医药与现代生物医学之间的桥梁,…...
基于大模型的阵发性室上性心动过速风险预测与治疗方案研究
目录 一、引言 1.1 研究背景与意义 1.2 研究目的与目标 1.3 研究方法与数据来源 二、阵发性室上性心动过速概述 2.1 定义与分类 2.2 发病机制与流行病学 2.3 临床表现与诊断方法 三、大模型在阵发性室上性心动过速预测中的应用 3.1 大模型技术原理与特点 3.2 模型构…...
基于金字塔视觉变换的类引导网络高分辨率遥感图像高效语义分割
Class-Guidance Network Based on the Pyramid Vision Transformer for Efficient Semantic Segmentation of High-Resolution Remote Sensing Images 摘要 多分类语义分割中类之间的小差异和类内的大变化是全卷积神经网络的“编码器-解码器”结构没有完全解决的问题&#…...
高级:数据库面试题全攻略
一、引言 数据库是软件开发中不可或缺的组件,面试官通过相关问题,考察候选人对数据库核心概念的理解、实际应用能力以及在复杂场景下的问题解决能力。本文将深入解读数据库的索引、事务、锁机制等常见面试问题,结合实际开发场景,…...
如何避免Python爬虫重复抓取相同页面?
在网络爬虫开发过程中,重复抓取相同页面是一个常见但必须解决的问题。重复抓取不仅会浪费网络带宽和计算资源,降低爬虫效率,还可能导致目标网站服务器过载,甚至触发反爬机制。本文将深入探讨Python爬虫中避免重复抓取的多种技术方…...
LeetCode.02.04.分割链表
分割链表 给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你不需要 保留 每个分区中各节点的初始相对位置。 示例 1: 输入:head [1,4,3,2,5,2], x …...
鸿蒙开发_ARKTS快速入门_语法说明_渲染控制---纯血鸿蒙HarmonyOS5.0工作笔记012
然后我们再来看渲染控制 首先看条件渲染,其实就是根据不同的状态,渲染不同的UI界面 比如下面这个暂停 开启播放的 可以看到就是通过if 这种条件语句 修改状态变量的值 然后我们再来看这个, 下面点击哪个,上面横线就让让他显示哪个 去看一下代码 可以看到,有两个状态变量opt…...
MOP数据库中的EXPLAIN用法
EXPLAIN 是 SQL 中的一个非常有用的工具,主要用于分析查询语句的执行计划。执行计划能展示数据库在执行查询时的具体操作步骤,像表的读取顺序、使用的索引情况、数据的访问方式等,这有助于我们对查询性能进行优化。 语法 不同的数据库系统&…...
软考 系统架构设计师系列知识点 —— 设计模式之抽象工厂模式
本文内容参考: 软考 系统架构设计师系列知识点之设计模式(2)_系统架构设计师中考设计模式吗-CSDN博客 https://baike.baidu.com/item/%E6%8A%BD%E8%B1%A1%E5%B7%A5%E5%8E%82%E6%A8%A1%E5%BC%8F/2361182 特此致谢! Abstract Fac…...
告别水下模糊!SU-YOLO:轻量化+尖峰神经网络,用“类脑计算”实现水下目标毫秒级识别
目录 一、摘要 二、引言 三、相关工作 SNN 物体检测 水下物体探测 水下图像去噪 归一化 四、方法 水下尖峰YOLO 尖峰干扰器 SU-Block SpikeSPP 编码器和检测头 分批归一化 五、Coovally AI模型训练与应用平台 六、实验结果 数据集和实施细节 数据集 实施细节…...
Three.js 系列专题 8:实战项目 - 构建一个小型 3D 游戏
内容概述 本专题将通过一个实战项目展示 Three.js 的综合应用。游戏包含迷宫生成、角色移动、相机控制和简单的物理碰撞检测(可选)。这将帮助你将之前学到的知识融会贯通。 学习目标 整合几何体、光照、动画和交互知识。实现基本的游戏逻辑和用户控制。可选:使用 Cannon.j…...
嵌入式笔试(一)
C语言和嵌入式软件 面试题(共10题 时间30分钟) 1. 请写出下面声明的含义。 int(*s[10])(int);定义了一个数组为s包含十个元素,每个元素都是函数指针,函数的参数为一个int类型,返回值也是int类型2. 选择题 设有一台计算机,它有一条加法指令,每次可计算三个数的和。如果要…...
spark 的流量统计案例
创建一个目录为data...
局域网访问 Redis 方法
局域网访问 Redis 方法 默认情况下,Redis 只允许本机 (127.0.0.1) 访问。如果你想让局域网中的其他设备访问 Redis,需要 修改 Redis 配置,并确保 防火墙放行端口。 方法 1:修改 Redis 配置 1. 修改 redis.conf(或 me…...
LeetCode题五:合并两个有序链表
基本思路其实就是:先建立一个空链表,然后将尾节点放在头结点上; 如果第一个链表节点值较小,那么先将list1插入新链表中,然后将尾节点后移;相同的,第二个也需要比较;移动新链表的指针…...
深入探索 `malloc`:内存分配失败的原因及正确使用规范
文章目录 一、malloc 内存分配失败的常见原因1. 内存不足2. 内存越界访问3. 内存碎片化4. 系统限制5. 错误的使用方式 二、如何正确使用 malloc1. 检查返回值2. 释放内存3. 避免内存越界4. 优化内存使用5. 调整系统参数6. 使用高效的内存分配器 三、总结 在 C 语言中࿰…...