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 only
private 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);
}
}
}
@Override
public 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);
}
@Override
protected 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.java
public 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;
// 获取对应的 adapter
sAdapter = 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 adapter
sProfiles = 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 角色,默认支持这么多 profile
profileArray.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.java
public 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.java
public static boolean isEnabled() {
return BluetoothProperties.isProfileA2dpSourceEnabled().orElse(false);
}
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)}
*/
@Deprecated
int HEALTH = 3;
/**
* HID Host
*
* @hide
*/
@SystemApi
int HID_HOST = 4;
/**
* PAN Profile
*
* @hide
*/
@SystemApi
int PAN = 5;
/**
* PBAP
*
* @hide
*/
@SystemApi
int PBAP = 6;
/**
* GATT
*/
int GATT = 7;
/**
* GATT_SERVER
*/
int GATT_SERVER = 8;
/**
* MAP Profile
*
* @hide
*/
@SystemApi
int MAP = 9;
/*
* SAP Profile
* @hide
*/
int SAP = 10;
/**
* A2DP Sink Profile
*
* @hide
*/
@SystemApi
int A2DP_SINK = 11;
/**
* AVRCP Controller Profile
*
* @hide
*/
@SystemApi
int AVRCP_CONTROLLER = 12;
/**
* AVRCP Target Profile
*
* @hide
*/
int AVRCP = 13;
/**
* Headset Client - HFP HF Role
*
* @hide
*/
@SystemApi
int HEADSET_CLIENT = 16;
/**
* PBAP Client
*
* @hide
*/
@SystemApi
int PBAP_CLIENT = 17;
/**
* MAP Messaging Client Equipment (MCE)
*
* @hide
*/
@SystemApi
int MAP_CLIENT = 18;
/**
* HID Device
*/
int HID_DEVICE = 19;
/**
* Object Push Profile (OPP)
*
* @hide
*/
@SystemApi
int OPP = 20;
/**
* Hearing Aid Device
*
*/
int HEARING_AID = 21;
/**
* LE Audio Device
*
*/
int LE_AUDIO = 22;
/**
* Volume Control profile
*
* @hide
*/
@SystemApi
int 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
*/
@SystemApi
int 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
*/
@SystemApi
int 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 | 名称 | 功能说明 | 使用场景示例 |
---|---|---|---|
1 |
HEADSET | Headset Profile (HSP) & Hands-Free Profile (HFP)。支持音频通话传输、控制语音呼叫。 | 车载蓝牙通话、蓝牙耳机通话 |
2 |
A2DP | 高质量音频单向传输(Advanced Audio Distribution Profile)。 | 手机播放音乐到蓝牙音箱/耳机 |
3 |
HEALTH (已废弃) | 旧版医疗设备通信协议(HDP),基于 MCAP。 | 旧版健康设备,如血压计(不推荐) |
4 |
HID_HOST | 支持作为 HID Host 使用,如连接键盘/鼠标。 | 安卓设备接蓝牙键盘或鼠标 |
5 |
PAN | Personal Area Networking,蓝牙网络共享协议。 | 手机共享网络给车机或平板 |
6 |
PBAP | Phone Book Access Profile,提供联系人信息读取服务。 | 车机同步手机联系人 |
7 |
GATT | Generic Attribute Profile,用于 BLE 客户端角色。 | App 读取 BLE 设备的数据,如运动手环 |
8 |
GATT_SERVER | BLE GATT 服务端。 | 手环或传感器暴露服务给手机 |
9 |
MAP | Message Access Profile,支持消息通知和读取。 | 车机读取短信内容 |
10 |
SAP | SIM Access Profile,让蓝牙设备访问手机 SIM 卡。 | 车机通过手机 SIM 打电话 |
11 |
A2DP_SINK | 支持作为音频接收端(Sink)使用,接收 A2DP 音频流。 | 车机接收手机的音频 |
12 |
AVRCP_CONTROLLER | 控制设备上的媒体播放(AVRCP Controller Role)。 | 蓝牙耳机控制手机音乐播放 |
13 |
AVRCP | AVRCP Target Role,被控制设备的媒体控制接口。 | 手机被蓝牙耳机控制播放/暂停 |
16 |
HEADSET_CLIENT | 以 HFP HF 角色发起电话连接。 | 手机作为控制端连接车机 |
17 |
PBAP_CLIENT | PBAP 客户端,从服务器读取联系人。 | 手机从车机导入联系人(不常见) |
18 |
MAP_CLIENT | MAP 客户端,主动访问消息服务。 | 手机从车机读取短信 |
19 |
HID_DEVICE | 将本机作为蓝牙 HID 设备,例如模拟键盘、鼠标。 | 手机变身成蓝牙遥控器/键盘 |
20 |
OPP | Object Push Profile,用于文件传输(已较少使用)。 | 蓝牙发送联系人/图片 |
21 |
HEARING_AID | 支持助听器通信(低延迟、高同步性)。 | 手机连接助听器设备 |
22 |
LE_AUDIO | 支持 BLE Audio 传输(多路、广播、低功耗)。 | 多设备同步播放、语音辅助功能 |
23 |
VOLUME_CONTROL | 控制远端设备音量(LE Audio)。 | 手机调整 BLE 音箱音量 |
24 |
MCP_SERVER | Media Control Profile 服务端角色。 | 播放设备被远端控制(BLE) |
25 |
CSIP_SET_COORDINATOR | BLE 同步组协调器(Coordinated Set Identification)。 | 同步多只音箱播放 BLE 音频 |
26 |
LE_AUDIO_BROADCAST | BLE 音频广播源。 | 手机向公众广播音频 |
27 |
LE_CALL_CONTROL | BLE 电话控制服务(Call Control Profile)。 | 耳机控制手机拨打/挂断电话 |
28 |
HAP_CLIENT | Hearing Access Profile 客户端。 | 手机访问助听器服务 |
29 |
LE_AUDIO_BROADCAST_ASSISTANT | 辅助设备帮广播设备寻找接收者。 | 电视控制 BLE 音频广播目标 |
30 |
BATTERY | BLE 电量服务,报告设备电池电量。 | 耳机电量显示在手机通知栏 |
@SystemApi:表示该接口/ID 是系统内部使用,普通应用无法直接访问。
@hide:Android SDK 不对外暴露该接口。
@Deprecated:表示该 Profile 已不再推荐使用,可能未来会被移除。
多数 BLE 相关 Profile(如 GATT、LE_AUDIO)用于现代可穿戴/低功耗设备场景。
传统 Profile(如 HSP、A2DP、PBAP)仍广泛用于车载娱乐、耳机等领域。
3. 小结
如何在协议栈中配置支持的profile?
- 首先我们需要在 AdapterUtil.init 中,添加每个蓝牙支持的 profile
- 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 {
@Override
public 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: 12
01-02 04:40:09.048206 2259 2259 D A2dpSinkService: onCreate
01-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: 12
01-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: onCreate
01-02 04:40:09.238341 2259 2658 I AdapterState0: ON : entered
4. 总结
本节从两个角度 阐述了 蓝牙服务中 配置 和 启动 经典蓝牙 profile 服务的流程。