当前位置: 首页 > news >正文

【android bluetooth 框架分析 04】【bt-framework 层详解 2】【如何配置和启动蓝牙profile服务】

1. 问题

蓝牙协议栈在启动的时候,会启动很多协议服务, 例如 A2dpService、A2dpSinkService、AvrcpTargetService、AvrcpControllerService、BassClientService、BatteryService、CsipSetCoordinatorService、HeadsetService、HeadsetClientService …

你是否都清楚, 这些服务都是在哪里配置启动的?

如果不太清楚的话,今天我们就来梳理一下。

2. 如何在协议栈中配置支持的profile

当我们蓝牙服务进程被拉起时,会首先调用到

  • android/app/src/com/android/bluetooth/btservice/AdapterApp.java
package com.android.bluetooth.btservice;import android.app.Application;
import android.util.Log;public class AdapterApp extends Application {private static final String TAG = "BluetoothAdapterApp";private static final boolean DBG = AdapterService.DBG; //false;//For Debugging onlyprivate static int sRefCount = 0;static {if (DBG) {Log.d(TAG, "Loading JNI Library");}System.loadLibrary("bluetooth_jni"); // 1. 首先会加载我们 native 的库}public AdapterApp() {// 2. 创建 AdapterApp 对象super();if (DBG) {synchronized (AdapterApp.class) {sRefCount++;Log.d(TAG, "REFCOUNT: Constructed " + this + " Instance Count = " + sRefCount);}}}@Overridepublic void onCreate() {super.onCreate();if (DBG) {Log.d(TAG, "onCreate");}try {DataMigration.run(this);} catch (Exception e) {Log.e(TAG, "Migration failure: ", e);}// 3.调用 对应的配置初始化AdapterUtil.init(this);Config.init(this);}@Overrideprotected void finalize() {if (DBG) {synchronized (AdapterApp.class) {sRefCount--;Log.d(TAG, "REFCOUNT: Finalized: " + this + ", Instance Count = " + sRefCount);}}}
}
        AdapterUtil.init(this);Config.init(this);

1. AdapterUtil.init

// android/app/src/com/android/bluetooth/btservice/AdapterUtil.javapublic static void init(@NonNull Context context) {sContext = context;sDualBluetooth = SystemProperties.getBoolean("persist.vendor.bluetooth.dual_bt", false);// 根据当前 进程的名字,来区分 是bt0 还是 bt1 的服务进程sAdapterIndex = Application.getProcessName().equals(sContext.getPackageName()) ?ADAPTER_DEFAULT : ADAPTER_1;// 获取对应的 adaptersAdapter = getAdapter(sAdapterIndex);// 是否支持双蓝牙方案sDualAdapterMode = SystemProperties.getBoolean("persist.bluetooth.dual_adapter_mode", false);sFilterDevice = getFilterDeviceConfig();// 如果是双蓝牙方案, bt0 需要监测 bt1 服务的状态。if (isDualAdapterMode() && isAdapterDefault()) {// In dual adapter mode, default adapter needs to monitor// new adapter's state.AdapterExt.create(sContext);}// 获取系统属性,check 判断当前 bt0 是否为 a2dpsink 角色。如果没有设置,默认 bt0 为 a2dpsink 角色sIsA2dpSinkRole = SystemProperties.getBoolean("persist.bluetooth.adapter0.isA2dpSink", true);// Init profile supported in Bluetooth adaptersProfiles = new HashMap<Integer, ArrayList<Integer>>(ADAPTER_NUMBER);// 无论 bt0 是作为 a2dpsink 还是 a2dpsource , 将都支持 GATT, GATT_SERVER profile.ArrayList<Integer> profileArray = new ArrayList<Integer>(Arrays.asList(BluetoothProfile.GATT,BluetoothProfile.GATT_SERVER));if (sIsA2dpSinkRole) {// 如果是 a2dpsink 角色,默认支持这么多 profileprofileArray.add(BluetoothProfile.A2DP_SINK);profileArray.add(BluetoothProfile.AVRCP_CONTROLLER);profileArray.add(BluetoothProfile.HEADSET_CLIENT);profileArray.add(BluetoothProfile.PBAP_CLIENT);profileArray.add(BluetoothProfile.HID_HOST);profileArray.add(BluetoothProfile.MAP_CLIENT);profileArray.add(BluetoothProfile.PAN);profileArray.add(BluetoothProfile.HID_DEVICE);profileArray.add(BluetoothProfile.A2DP);profileArray.add(BluetoothProfile.AVRCP);profileArray.add(BluetoothProfile.OPP);} else {// 如果是 a2dp source : 默认支持如下profileArray.add(BluetoothProfile.A2DP);profileArray.add(BluetoothProfile.AVRCP);profileArray.add(BluetoothProfile.HID_HOST);}sProfiles.put(ADAPTER_DEFAULT, profileArray); // 将上述支持的协议数组,放置到 bt0 对应的 sProfiles 中// bt1 将默认支持如下协议sProfiles.put(ADAPTER_1, new ArrayList<Integer>(Arrays.asList(BluetoothProfile.A2DP,BluetoothProfile.AVRCP,BluetoothProfile.GATT,BluetoothProfile.GATT_SERVER,BluetoothProfile.HID_HOST,BluetoothProfile.BATTERY)));}public static boolean isProfileSupported(int profileId) {return sProfiles.get(sAdapterIndex).contains(profileId); // 获取 bt0 , bt1 支持的 profile}
  • AdapterUtil.init 主要是将 bt0/bt1 支持的协议,分别保存在 sProfiles 对应的数组中。
  • 在之后可以通过 AdapterUtil.isProfileSupported() 方法来获取 当前支持那些协议。

2. Config.init(this)

  • packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/Config.java
    static void init(Context ctx) {...ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length);// 这里会遍历 PROFILE_SERVICES_AND_FLAGS 数组中的每一个 ProfileConfig 对象for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {Log.i(TAG, "init: profile=" + config.mClass.getSimpleName() + ", enabled="+ config.mSupported);if (config.mSupported&& AdapterUtil.isProfileSupported(config.mProfileId)) {// 如果 PROFILE_SERVICES_AND_FLAGS 的配置和 ProfileConfig 的配置 都支持Log.i(TAG, "Add profile " + config.mClass.getSimpleName());// 会将该 profile 对应的 class 加入到 profiles 中profiles.add(config.mClass);}}// 最终将 支持的 profile 对应的 class 全部放入 sSupportedProfiles 中sSupportedProfiles = profiles.toArray(new Class[profiles.size()]);...}static Class[] getSupportedProfiles() {return sSupportedProfiles;}
  • Config.init: 将当前支持的 profile 对应的 class 全部放入 sSupportedProfiles 数组中
  • 可以通过 Config.getSupportedProfiles 获取到 支持的 profile 对应的 class.
private static final ProfileConfig[] PROFILE_SERVICES_AND_FLAGS = {new ProfileConfig(AdapterUtil.getA2dpServiceClass(), A2dpService.isEnabled(),BluetoothProfile.A2DP),new ProfileConfig(A2dpSinkService.class, A2dpSinkService.isEnabled(),BluetoothProfile.A2DP_SINK),new ProfileConfig(AdapterUtil.getAvrcpTargetServiceClass(), AvrcpTargetService.isEnabled(),BluetoothProfile.AVRCP),new ProfileConfig(AvrcpControllerService.class, AvrcpControllerService.isEnabled(),BluetoothProfile.AVRCP_CONTROLLER),new ProfileConfig(BassClientService.class, BassClientService.isEnabled(),BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT),new ProfileConfig(AdapterUtil.getBatteryServiceClass(), BatteryService.isEnabled(),BluetoothProfile.BATTERY),new ProfileConfig(CsipSetCoordinatorService.class,CsipSetCoordinatorService.isEnabled(),BluetoothProfile.CSIP_SET_COORDINATOR),new ProfileConfig(HapClientService.class, HapClientService.isEnabled(),BluetoothProfile.HAP_CLIENT),new ProfileConfig(HeadsetService.class, HeadsetService.isEnabled(),BluetoothProfile.HEADSET),new ProfileConfig(HeadsetClientService.class, HeadsetClientService.isEnabled(),BluetoothProfile.HEADSET_CLIENT),new ProfileConfig(HearingAidService.class, HearingAidService.isEnabled(),BluetoothProfile.HEARING_AID),new ProfileConfig(HidDeviceService.class, HidDeviceService.isEnabled(),BluetoothProfile.HID_DEVICE),new ProfileConfig(AdapterUtil.getHidHostServiceClass(), HidHostService.isEnabled(),BluetoothProfile.HID_HOST),new ProfileConfig(AdapterUtil.getGattServiceClass(), GattService.isEnabled(),BluetoothProfile.GATT),new ProfileConfig(LeAudioService.class, LeAudioService.isEnabled(),BluetoothProfile.LE_AUDIO),new ProfileConfig(TbsService.class, TbsService.isEnabled(),BluetoothProfile.LE_CALL_CONTROL),new ProfileConfig(BluetoothMapService.class, BluetoothMapService.isEnabled(),BluetoothProfile.MAP),new ProfileConfig(MapClientService.class, MapClientService.isEnabled(),BluetoothProfile.MAP_CLIENT),new ProfileConfig(McpService.class, McpService.isEnabled(),BluetoothProfile.MCP_SERVER),new ProfileConfig(BluetoothOppService.class, BluetoothOppService.isEnabled(),BluetoothProfile.OPP),new ProfileConfig(PanService.class, PanService.isEnabled(),BluetoothProfile.PAN),new ProfileConfig(BluetoothPbapService.class, BluetoothPbapService.isEnabled(),BluetoothProfile.PBAP),new ProfileConfig(PbapClientService.class, PbapClientService.isEnabled(),BluetoothProfile.PBAP_CLIENT),new ProfileConfig(SapService.class, SapService.isEnabled(),BluetoothProfile.SAP),new ProfileConfig(VolumeControlService.class, VolumeControlService.isEnabled(),BluetoothProfile.VOLUME_CONTROL),};

这里我们只分析一个例子:

new ProfileConfig(AdapterUtil.getA2dpServiceClass(), A2dpService.isEnabled(),BluetoothProfile.A2DP),
// android/app/src/com/android/bluetooth/btservice/AdapterUtil.javapublic static Class getA2dpServiceClass() {return isAdapter1() ? A2dpExtService.class : A2dpService.class;}

AdapterUtil.getA2dpServiceClass(): 如何是 bt0 就使用 A2dpService.class , 如果是bt1 就是 A2dpExtService.class

// android/app/src/com/android/bluetooth/a2dp/A2dpService.javapublic static boolean isEnabled() {return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false);}
  • 如何不明白请阅读【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

BluetoothProfile

framework/java/android/bluetooth/BluetoothProfile.java

/*** Headset and Handsfree profile*/int HEADSET = 1;/*** A2DP profile.*/int A2DP = 2;/*** Health Profile** @deprecated Health Device Profile (HDP) and MCAP protocol are no longer used. New* apps should use Bluetooth Low Energy based solutions such as {@link BluetoothGatt},* {@link BluetoothAdapter#listenUsingL2capChannel()}, or* {@link BluetoothDevice#createL2capChannel(int)}*/@Deprecatedint HEALTH = 3;/*** HID Host** @hide*/@SystemApiint HID_HOST = 4;/*** PAN Profile** @hide*/@SystemApiint PAN = 5;/*** PBAP** @hide*/@SystemApiint PBAP = 6;/*** GATT*/int GATT = 7;/*** GATT_SERVER*/int GATT_SERVER = 8;/*** MAP Profile** @hide*/@SystemApiint MAP = 9;/** SAP Profile* @hide*/int SAP = 10;/*** A2DP Sink Profile** @hide*/@SystemApiint A2DP_SINK = 11;/*** AVRCP Controller Profile** @hide*/@SystemApiint AVRCP_CONTROLLER = 12;/*** AVRCP Target Profile** @hide*/int AVRCP = 13;/*** Headset Client - HFP HF Role** @hide*/@SystemApiint HEADSET_CLIENT = 16;/*** PBAP Client** @hide*/@SystemApiint PBAP_CLIENT = 17;/*** MAP Messaging Client Equipment (MCE)** @hide*/@SystemApiint MAP_CLIENT = 18;/*** HID Device*/int HID_DEVICE = 19;/*** Object Push Profile (OPP)** @hide*/@SystemApiint OPP = 20;/*** Hearing Aid Device**/int HEARING_AID = 21;/*** LE Audio Device**/int LE_AUDIO = 22;/*** Volume Control profile** @hide*/@SystemApiint VOLUME_CONTROL = 23;/*** @hide* Media Control Profile server**/int MCP_SERVER = 24;/*** Coordinated Set Identification Profile set coordinator**/int CSIP_SET_COORDINATOR = 25;/*** LE Audio Broadcast Source** @hide*/@SystemApiint LE_AUDIO_BROADCAST = 26;/*** @hide* Telephone Bearer Service from Call Control Profile**/int LE_CALL_CONTROL = 27;/** Hearing Access Profile Client**/int HAP_CLIENT = 28;/*** LE Audio Broadcast Assistant** @hide*/@SystemApiint LE_AUDIO_BROADCAST_ASSISTANT = 29;/*** Battery Service** @hide*/int BATTERY = 30;/*** Max profile ID. This value should be updated whenever a new profile is added to match* the largest value assigned to a profile.** @hide*/int MAX_PROFILE_ID = 30;
Profile ID名称功能说明使用场景示例
1HEADSETHeadset Profile (HSP) & Hands-Free Profile (HFP)。支持音频通话传输、控制语音呼叫。车载蓝牙通话、蓝牙耳机通话
2A2DP高质量音频单向传输(Advanced Audio Distribution Profile)。手机播放音乐到蓝牙音箱/耳机
3HEALTH (已废弃)旧版医疗设备通信协议(HDP),基于 MCAP。旧版健康设备,如血压计(不推荐)
4HID_HOST支持作为 HID Host 使用,如连接键盘/鼠标。安卓设备接蓝牙键盘或鼠标
5PANPersonal Area Networking,蓝牙网络共享协议。手机共享网络给车机或平板
6PBAPPhone Book Access Profile,提供联系人信息读取服务。车机同步手机联系人
7GATTGeneric Attribute Profile,用于 BLE 客户端角色。App 读取 BLE 设备的数据,如运动手环
8GATT_SERVERBLE GATT 服务端。手环或传感器暴露服务给手机
9MAPMessage Access Profile,支持消息通知和读取。车机读取短信内容
10SAPSIM Access Profile,让蓝牙设备访问手机 SIM 卡。车机通过手机 SIM 打电话
11A2DP_SINK支持作为音频接收端(Sink)使用,接收 A2DP 音频流。车机接收手机的音频
12AVRCP_CONTROLLER控制设备上的媒体播放(AVRCP Controller Role)。蓝牙耳机控制手机音乐播放
13AVRCPAVRCP Target Role,被控制设备的媒体控制接口。手机被蓝牙耳机控制播放/暂停
16HEADSET_CLIENT以 HFP HF 角色发起电话连接。手机作为控制端连接车机
17PBAP_CLIENTPBAP 客户端,从服务器读取联系人。手机从车机导入联系人(不常见)
18MAP_CLIENTMAP 客户端,主动访问消息服务。手机从车机读取短信
19HID_DEVICE将本机作为蓝牙 HID 设备,例如模拟键盘、鼠标。手机变身成蓝牙遥控器/键盘
20OPPObject Push Profile,用于文件传输(已较少使用)。蓝牙发送联系人/图片
21HEARING_AID支持助听器通信(低延迟、高同步性)。手机连接助听器设备
22LE_AUDIO支持 BLE Audio 传输(多路、广播、低功耗)。多设备同步播放、语音辅助功能
23VOLUME_CONTROL控制远端设备音量(LE Audio)。手机调整 BLE 音箱音量
24MCP_SERVERMedia Control Profile 服务端角色。播放设备被远端控制(BLE)
25CSIP_SET_COORDINATORBLE 同步组协调器(Coordinated Set Identification)。同步多只音箱播放 BLE 音频
26LE_AUDIO_BROADCASTBLE 音频广播源。手机向公众广播音频
27LE_CALL_CONTROLBLE 电话控制服务(Call Control Profile)。耳机控制手机拨打/挂断电话
28HAP_CLIENTHearing Access Profile 客户端。手机访问助听器服务
29LE_AUDIO_BROADCAST_ASSISTANT辅助设备帮广播设备寻找接收者。电视控制 BLE 音频广播目标
30BATTERYBLE 电量服务,报告设备电池电量。耳机电量显示在手机通知栏
  • @SystemApi:表示该接口/ID 是系统内部使用,普通应用无法直接访问。

  • @hide:Android SDK 不对外暴露该接口。

  • @Deprecated:表示该 Profile 已不再推荐使用,可能未来会被移除。

  • 多数 BLE 相关 Profile(如 GATT、LE_AUDIO)用于现代可穿戴/低功耗设备场景。

  • 传统 Profile(如 HSP、A2DP、PBAP)仍广泛用于车载娱乐、耳机等领域。

3. 小结

如何在协议栈中配置支持的profile?

  1. 首先我们需要在 AdapterUtil.init 中,添加每个蓝牙支持的 profile
  2. Config.init 中,需要根据 系统属性 将支持的 profile 对应的 class 加入到 sSupportedProfiles 中。其他类可以通过 Config.getSupportedProfiles()来获取 对应支持的 profile class.
        AdapterUtil.init(this);Config.init(this);

3. 子协议服务启动

前面我们已经知道了协议栈将我们要启动的 profile 对应的class 都已经加入到 sSupportedProfiles 中了。那对应的 profile 服务是在什么实际启动的呢?

  • android/app/src/com/android/bluetooth/btservice/AdapterState.java
private class TurningOnState extends BaseAdapterState {@Overridepublic void enter() {super.enter();sendMessageDelayed(BREDR_START_TIMEOUT, BREDR_START_TIMEOUT_DELAY);mAdapterService.startProfileServices(); // 开启启动 经典蓝牙的 各个 子 profile.}
  • 当 蓝牙服务进入 启动 经典蓝牙状态时: 会触发调用 startProfileServices

  • packages/modules/Bluetooth/android/app/src/com/android/bluetooth/btservice/AdapterService.java

void startProfileServices() {debugLog("startCoreServices()");Class[] supportedProfileServices = Config.getSupportedProfiles(); // 获取 支持的 profiles if ((supportedProfileServices.length == 1)&& isGattService(supportedProfileServices[0].getSimpleName())) {...} else {// 挨个启动 profile 对应的服务setAllProfileServiceStates(supportedProfileServices, BluetoothAdapter.STATE_ON);}}public void setProfileServiceState(Class service, int state) {...Log.d(TAG, "setProfileServiceState: " + service + ", state: " + state);Intent intent = new Intent(this, service);intent.putExtra(EXTRA_ACTION, ACTION_SERVICE_STATE_CHANGED);intent.putExtra(BluetoothAdapter.EXTRA_STATE, state);startService(intent); // 通过 系统函数将  profile 对应的服务拉起来}
01-02 04:40:07.206918  2259  2658 I AdapterState0: OFF : entered 
01-02 04:40:07.348232  2259  2658 I AdapterState0: BLE_TURNING_ON : entered 
01-02 04:40:09.013738  2259  2658 I AdapterState0: BLE_ON : entered // 进入 TURNING_ON 开始启动各个子协议
01-02 04:40:09.042387  2259  2658 I AdapterState0: TURNING_ON : entered // 开始逐个启动 经典蓝牙的 各个子 profile
01-02 04:40:09.043423  2259  2658 D BluetoothAdapterService: setProfileServiceState: class com.android.bluetooth.a2dpsink.A2dpSinkService, state: 12
01-02 04:40:09.045134  2259  2658 D BluetoothAdapterService: setProfileServiceState: class com.android.bluetooth.avrcpcontroller.AvrcpControllerService, state: 12
01-02 04:40:09.046304  2259  2658 D BluetoothAdapterService: setProfileServiceState: class com.android.bluetooth.hfpclient.HeadsetClientService, state: 1201-02 04:40:09.048206  2259  2259 D A2dpSinkService: onCreate01-02 04:40:09.048721  2259  2658 D BluetoothAdapterService: setProfileServiceState: class com.android.bluetooth.hid.HidDeviceService, state: 12
01-02 04:40:09.053122  2259  2658 D BluetoothAdapterService: setProfileServiceState: class com.android.bluetooth.hid.HidHostService, state: 12
01-02 04:40:09.054909  2259  2658 D BluetoothAdapterService: setProfileServiceState: class com.android.bluetooth.pbapclient.PbapClientService, state: 1201-02 04:40:09.075082  2259  2259 D AvrcpControllerService: onCreate
01-02 04:40:09.107154  2259  2259 D HeadsetClientService: onCreate
01-02 04:40:09.181937  2259  2259 D HidDeviceService: onCreate
01-02 04:40:09.187428  2259  2259 D HidHostService: onCreate
01-02 04:40:09.210880  2259  2259 D PbapClientService: onCreate
01-02 04:40:09.215510  2259  2259 D BluetoothMediaBrowserService: onCreate
01-02 04:40:09.228497  2259  2259 D HfpClientConnService: onCreate01-02 04:40:09.238341  2259  2658 I AdapterState0: ON : entered 

4. 总结

本节从两个角度 阐述了 蓝牙服务中 配置 和 启动 经典蓝牙 profile 服务的流程。

http://www.xdnf.cn/news/975817.html

相关文章:

  • 【多线程初阶】详解线程池(下)
  • PROFINET主站(M580)通过网关访问CANopen从站(NJ系列)的技术解析
  • 深度强化学习 | 详细推导随机/确定性策略梯度定理
  • Flutter setState() 状态管理详细使用指南
  • 使用 C/C++、OpenCV 和 Libevent 构建联网人脸识别考勤系统 [特殊字符]‍[特殊字符]
  • 电机控制基础,小白入门篇
  • 第三章支线六 ·数据幻域 · 状态管理与数据流
  • Android 默认第三方app运行权限(android11-13)
  • 小程序 UI 设计,怎样在方寸间实现高效交互
  • Fastapi + vue3 自动化测试平台(6):AI + Web UI的完美结合
  • 把下载的ippicv.tgz放入<opencv_build_dir>/3rdparty/ippicv/download/中cmake依然无法识别
  • 快速了解JVM的GC历史
  • 【Lua热更新知识】学习三 XLua学习
  • 【AI 时代,食品科技远未触及天花板,新一轮颠覆性突破正在酝酿】
  • 神舟笔记本Control Center无法打开风扇设置
  • Web 架构之服务网格(Service Mesh)实战解析
  • 机器视觉开发-边缘提取
  • Python爬虫(54)Python数据治理全攻略:从爬虫清洗到NLP情感分析的实战演进
  • 2025-6-9Vue3快速上手
  • ubuntu22 arm 编译安装input leap
  • 数据的聚合
  • 审计效率升级!Word一键批量给数字添加千位分隔符
  • 传统机器学习与大模型 + Prompt 的对比示例
  • eureka如何绕过 LVS 的虚拟 IP(VIP),直接注册服务实例的本机真实 IP
  • SpringMVC异步处理Servlet
  • Wyn 商业智能与 3D 大屏的深度融合应用
  • 在ARM 架构的 Mac 上 更新Navicat到17后连接Oracle时报错:未加载 Oracle 库。
  • 高频面试之6Hive
  • 机器学习算法——集成学习
  • 电路图识图基础知识-变频器控制电动机系统解析(二十四)