基于Android P版本分析
BT UUID
我们针对device蓝牙连接过程中的一些流程进行分析,我们不针对常见的开启、扫描、绑定这3个操作进行分析,主要围绕这3个过程中所执行的一些逻辑,例如创建了哪些实例,实例的作用是什么等一些信息进行表述;
CachedBluetoothDevice
CachedBluetoothDevice 创建
我们知道,在BT扫描阶段发现了可见蓝牙设备之后,就会发送ACTION_FOUND广播来通知应用层,一般为Settings进程,Settings进程在接收到广播之后,就会触发SettingsLib中的BluetoothEventManager中的广播接收器,然后将消息发送给DeviceFoundHandler;
在接收到ACTION_FOUND广播中,同时会将扫描到的BluetoothDevice对象进行回调上报,在DeviceFoundHandler中会将BluetoothDevice封装成CachedBluetoothDevice实例;
CachedBluetoothDevice 详解
ini
CachedBluetoothDevice(Context context,
LocalBluetoothAdapter adapter,
LocalBluetoothProfileManager profileManager,
BluetoothDevice device) {
mContext = context;
mLocalAdapter = adapter;
mProfileManager = profileManager;
mAudioManager = context.getSystemService(AudioManager.class);
mDevice = device;
mProfileConnectionState = new HashMap<LocalBluetoothProfile, Integer>();
fillData();
mHiSyncId = BluetoothHearingAid.HI_SYNC_ID_INVALID;
}
CachedBluetoothDevice是BluetoothDevice在framework层中的一个缓存管理,用于描述BluetoothDevice的一些信息状态和操作方法;
在CachedBluetoothDevice构造方法中,持有了LocalBluetoothAdapter、LocalBluetoothProfileManager和AudioManager的实例引用;在该类中,大多数属性和方法都和BluetoothDevice实例有很大的关系;
mProfiles 服务集合
其中在该类中有一个比较主要的变量:mProfiles;
这个集合主要是用于维护BluetoothDevice中支持的服务对应的配置信息,每一个服务对应一个Profile,在framework中体现为LocalBluetoothProfile;
在连接device的时候,即对应CachedBluetoothDevice时,需要将CachedBluetoothDevice中维护的device对应的服务集合遍历进行启动并开启对应的状态机进行状态维护;
那这样就带来一个问题,Device支持的服务是如何定义的,手机端连接车机蓝牙时,如何得知车机具备哪些服务;
BT Profile_Service
首先,我们先看一下CachedBluetoothDevice中Profile集合的填充流程,通过流程,我们分析其中涉及到的一些概念;
Profile 加载
我们以车机设备为例,在开启蓝牙设备的时候,主要是进行AdapterService的初始化、连接操作以及其他各个类型的ProfileService的启动;
在Service启动之前,首先Bluedroid会上报蓝牙设备的UUID,确认该设备支持的Server集合;
然后紧接着才是各个Service的启动过程;我们以其中的一个Service为例,说明Service的启动、加载流程;
在A2dpSinkService连接过程中,在最后的逻辑中,A2dpSinkStateMachine的状态机进入了Pending状态,等待Bluedroid层的回调结果;
到了这一步,说明A2dpSinkService已经连接成功,状态机已经处于了Connected状态了。
当所有的Service(支持的ProfileService)都启动成功之后,Bluetooth状态机会进入BluetoothAdapter.OnState状态,之后就会进行Profile的加载保存,执行setBluetoothStateOn方法;
UUID
上述我们提及到了UUID的概念,UUID:Universally Unique Identifier,通用唯一识别码,由三部分组成,当前的日期和时间、时钟序列、全球唯一的IEEE设备识别号,UUID是根据一定算法,计算得到的一长串数字,这个数字的产生使用了多种元素,所以使得这串数字不会重复,每次生成都会产生不一样的序列,所以可以用来作为唯一标识;
对于蓝牙设备,每一个服务都有一个与之对应的UUID,在frameworks层中的定义在BluetoothUuid.java中,下面是我们常使用到的一些server对应的uuid的映射关系表;
Type | UUID |
---|---|
AudioSink | 0000110B |
AudioSource | 0000110A |
AdvAudioDist | 0000110D |
HSP | 00001108 |
HSP_AG | 00001112 |
Handsfree | 0000111E |
Handsfree_AG | 0000111F |
AvrcpController | 0000110E |
AvrcpTarget | 0000110C |
ObexObjectPush | 00001105 |
Hid | 00001124 |
Hogp | 00001812 |
PANU | 00001115 |
NAP | 00001116 |
BNEP | 0000000f |
PBAP_PCE | 0000112e |
PBAP_PSE | 0000112f |
MAP | 00001134 |
MNS | 00001133 |
MAS | 00001132 |
SAP | 0000112D |
HearingAid | 0000FDF0 |
DIP | 00001200 |
BASE_UUID | 00000000 |
蓝牙技术联盟SIG定义了UUID共用了一个基本的UUID:0000xxxx-0000-1000-8000-00805F9B34FB,共128位,为了进一步简化基本UUID,蓝牙技术联盟定义了一个16位的UUID,便于记忆和操作,其中xxxx部分不固定的,上述UUID后4位数字对应了该部分的值,也就是简化后的UUID;
RemoteDevice UUID
我们这边以RemoteDevice侧的UUID来分析一下,如何通过UUID来确定该RemoteDevice支持哪些Service;
测试设备:华为P40;
yaml
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: Class: 5a020c
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: UUID:
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 0000110a-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 00001105-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 00001115-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 00001116-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 0000112f-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 00001112-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 0000111f-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 00001132-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 00000000-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 00000000-0000-1000-8000-00805f9b34fb
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 11c8b310-80e4-4276-afc0-f81590b2177f
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 8ce255c0-200a-11e0-ac64-0800200c9a66
05-09 15:30:41.802 4764 4764 V CachedBluetoothDevice: 9664aa26-d76c-43ad-9775-d310f253a408
05-09 15:30:41.802 1297 1297 E CachedBluetoothDevice: updating profiles for dupz
- class:BluetoothClass,指定了BluetoothDevice;
- UUID:BluetoothDevice的唯一标识;
- dupz:BluetoothDevice name;
上述的3个参数是关于Device基本信息或概念的描述;
UUID Server 映射关系
- 0000110a -0000-1000-8000-00805f9b34fb:AudioSource;
- 00001105 -0000-1000-8000-00805f9b34fb:ObexObjectPush;
- 00001115 -0000-1000-8000-00805f9b34fb:PANU
- 00001116 -0000-1000-8000-00805f9b34fb:NAP
- 0000112f -0000-1000-8000-00805f9b34fb:PBAP_PSE(Phonebook Access Server)
- 00001112 -0000-1000-8000-00805f9b34fb:HSP_AG(Headset AG)
- 0000111f -0000-1000-8000-00805f9b34fb:Handsfree_AG(Handsfree Audio Gateway)
- 00001132 -0000-1000-8000-00805f9b34fb:MAS(Message Access Server)
- 00000000 -0000-1000-8000-00805f9b34fb:BASE_UUID
- 11c8b310-80e4-4276-afc0-f81590b2177f:Vendor specific
- 8ce255c0-200a-11e0-ac64-0800200c9a66:BluetoothChat,google提供的一个特定的UUID,属于Vendor specific
- 9664aa26-d76c-43ad-9775-d310f253a408:Vendor specific
UUID Profile 映射关系
这个映射关系定义在LocalBluetoothProfileManager的updateProfiles方法中;
在这个方法中涉及到了两组UUID:RemoteDevice UUID和LocalDevice UUID,即手机和车机的UUID;
因为有一些服务连接中,存在C/S模式,那就需要保证两侧满足该模式,只要有一侧不支持或者两侧都不支持,那在连接过程中,该服务就不需要启动运行;
- 绿色:BluetoothUuid常量值;
- 蓝色:BluetoothUuid的父节点,只有在该父节点下的所有的子节点(BluetoothUuid)都满足(两侧)的情况下,才可以加载指定的Profile;
- 红色:代表Profile与BluetoothUuid无关;
上述描述了Profile和UUid的映射关系;
LocalBluetoothProfile & BluetoothProfile 映射关系
LocalBluetoothProfile是一个接口,各种协议的封装者实现了该接口;
BluetoothProfile表示蓝牙配置文件的接口。蓝牙配置文件是适用于设备间蓝牙通信的无线接口规范。
LocalBluetoothProfile的实现依托于BluetoothProfile实现的;
LocalBluetoothProfile | BluetoothProfile(Service) | UUIDs |
---|---|---|
OppProfile | - | - |
PbapServerProfile | BluetoothPbap | BluetoothUuid.HSP BluetoothUuid.Handsfree BluetoothUuid.PBAP_PCE |
PanProfile | BluetoothPan | - |
HidDeviceProfile | BluetoothHidDevice | - |
HidProfile | BluetoothHidHost | - |
A2dpSinkProfile | BluetoothA2dpSink | BluetoothUuid.AudioSource BluetoothUuid.AdvAudioDist |
MapProfile | BluetoothMap | BluetoothUuid.MAP BluetoothUuid.MNS BluetoothUuid.MAS |
HfpClientProfile | BluetoothHeadsetClient | BluetoothUuid.HSP_AG BluetoothUuid.Handsfree_AG |
MapClientProfile | BluetoothMapClient | BluetoothUuid.MAP BluetoothUuid.MNS BluetoothUuid.MAS |
PbapClientProfile | BluetoothPbapClient | BluetoothUuid.PBAP_PSE |
SapProfile | BluetoothSap | BluetoothUuid.SAP |
Service 集合确认
蓝牙模块定义了很多ProfileService,例如A2dpService、A2dpSinkService、HfpService等等;
但是蓝牙设备并不是需要支持全部的Service,例如蓝牙音响,只需要支持播放音频就可以,但是手机则需要支持文件传输功能、远程通话功能,这些Service的确定,依赖于UUID;
我们首先回顾之前的逻辑,在开启蓝牙之后,我们就会调用setAllProfileServiceStates方法去加载所有的service,这里的逻辑就是用于加载所有该蓝牙设备支持的service集合;
核心逻辑:getSupportedProfiles,这个方法返回了车机设备Bluetooth模块定义的支持的Service的集合;
Service | profile | google_value | overlay_value | value |
---|---|---|---|---|
A2dpService | profile_supported_a2dp | false | false | false |
A2dpSinkService | profile_supported_a2dp_sink | true | true | true |
HealthService | profile_supported_hdp | false | false | false |
HeadsetService | profile_supported_hs_hfp | false | false | false |
HeadsetClientService | profile_supported_hfpclient | true | true | true |
HidHostService | profile_supported_hid_host | true | true | true |
BluetoothOppService | profile_supported_opp | false | true | true |
PanService | profile_supported_pan | false | false | false |
BluetoothPbapService | profile_supported_pbap | false | false | false |
GattService | profile_supported_gatt | true | true | |
pbap_include_photos_in_vcard | true | true | ||
pbap_use_profile_for_owner_vcard | true | true | ||
BluetoothMapService | profile_supported_map | false | false | false |
AvrcpTargetService | profile_supported_avrcp_target | false | false | |
AvrcpControllerService | profile_supported_avrcp_controller | true | true | true |
SapService | profile_supported_sap | false | false | |
PbapClientService | profile_supported_pbapclient | true | true | true |
MapClientService | profile_supported_mapmce | false | false | false |
HidDeviceService | profile_supported_hid_device | false | false | |
HearingAidService | profile_supported_hearing_aid | false | false | |
enable_phone_policy | false | false | ||
hfp_client_connection_service_enabled | true | true |
上述的profile这些定义,都是在Bluetooth模块的AndroidManifest.xml中使用;
ini
<service
android:process="@string/process"
android:name=".mapclient.MapClientService"
android:enabled="@bool/profile_supported_mapmce" >
<intent-filter>
<action android:name="android.bluetooth.IBluetoothMapClient" />
</intent-filter>
</service>
例如MapClientService的标签中,属性enabled使用到了上述的定义,判断这个service是否能被系统实例化,如果能则为true,否则为false,默认为true;
同时也会在Config中使用,用于判断哪些Service是Bluetooth支持的;
scss
private static final ProfileConfig[] PROFILE_SERVICES_AND_FLAGS = {
new ProfileConfig(HeadsetService.class, R.bool.profile_supported_hs_hfp,
(1 << BluetoothProfile.HEADSET)),
new ProfileConfig(A2dpService.class, R.bool.profile_supported_a2dp,
(1 << BluetoothProfile.A2DP)),
new ProfileConfig(A2dpSinkService.class, R.bool.profile_supported_a2dp_sink,
(1 << BluetoothProfile.A2DP_SINK)),
new ProfileConfig(HidHostService.class, R.bool.profile_supported_hid_host,
(1 << BluetoothProfile.HID_HOST)),
new ProfileConfig(HealthService.class, R.bool.profile_supported_hdp,
(1 << BluetoothProfile.HEALTH)),
new ProfileConfig(PanService.class, R.bool.profile_supported_pan,
(1 << BluetoothProfile.PAN)),
new ProfileConfig(GattService.class, R.bool.profile_supported_gatt,
(1 << BluetoothProfile.GATT)),
new ProfileConfig(BluetoothMapService.class, R.bool.profile_supported_map,
(1 << BluetoothProfile.MAP)),
new ProfileConfig(HeadsetClientService.class, R.bool.profile_supported_hfpclient,
(1 << BluetoothProfile.HEADSET_CLIENT)),
new ProfileConfig(AvrcpTargetService.class, R.bool.profile_supported_avrcp_target,
(1 << BluetoothProfile.AVRCP)),
new ProfileConfig(AvrcpControllerService.class,
R.bool.profile_supported_avrcp_controller,
(1 << BluetoothProfile.AVRCP_CONTROLLER)),
new ProfileConfig(SapService.class, R.bool.profile_supported_sap,
(1 << BluetoothProfile.SAP)),
new ProfileConfig(PbapClientService.class, R.bool.profile_supported_pbapclient,
(1 << BluetoothProfile.PBAP_CLIENT)),
new ProfileConfig(MapClientService.class, R.bool.profile_supported_mapmce,
(1 << BluetoothProfile.MAP_CLIENT)),
new ProfileConfig(HidDeviceService.class, R.bool.profile_supported_hid_device,
(1 << BluetoothProfile.HID_DEVICE)),
new ProfileConfig(BluetoothOppService.class, R.bool.profile_supported_opp,
(1 << BluetoothProfile.OPP)),
new ProfileConfig(BluetoothPbapService.class, R.bool.profile_supported_pbap,
(1 << BluetoothProfile.PBAP)),
new ProfileConfig(HearingAidService.class, R.bool.profile_supported_hearing_aid,
(1 << BluetoothProfile.HEARING_AID))
};
判断条件:
ini
static void init(Context ctx) {
if (ctx == null) {
return;
}
Resources resources = ctx.getResources();
if (resources == null) {
return;
}
ArrayList<Class> profiles = new ArrayList<>(PROFILE_SERVICES_AND_FLAGS.length);
for (ProfileConfig config : PROFILE_SERVICES_AND_FLAGS) {
boolean supported = resources.getBoolean(config.mSupported);
if (supported && !isProfileDisabled(ctx, config.mMask)) {
Log.v(TAG, "Adding " + config.mClass.getSimpleName());
profiles.add(config.mClass);
}
sSupportedProfiles = profiles.toArray(new Class[profiles.size()]);
}
mIsUserBuild = "user".equalsIgnoreCase(SystemProperties.get("ro.build.type","user"));
}
private static boolean isProfileDisabled(Context context, long profileMask) {
final ContentResolver resolver = context.getContentResolver();
final long disabledProfilesBitMask =
Settings.Global.getLong(resolver, Settings.Global.BLUETOOTH_DISABLED_PROFILES, 0);
return (disabledProfilesBitMask & profileMask) != 0;
}
其中根据profile的supported配置以及BLUETOOTH_DISABLED_PROFILES属性共同决定蓝牙设置是否支持该Service,最后通过startService的方法将sSupportedProfiles变量中保存的Service全部启动即可;
log分析:
yaml
05-09 15:30:18.648 17508 17508 V AdapterServiceConfig: Adding A2dpSinkService
05-09 15:30:18.648 17508 17508 V AdapterServiceConfig: Adding HidHostService
05-09 15:30:18.648 17508 17508 V AdapterServiceConfig: Adding GattService
05-09 15:30:18.648 17508 17508 V AdapterServiceConfig: Adding HeadsetClientService
05-09 15:30:18.649 17508 17508 V AdapterServiceConfig: Adding AvrcpControllerService
05-09 15:30:18.649 17508 17508 V AdapterServiceConfig: Adding SapService
05-09 15:30:18.649 17508 17508 V AdapterServiceConfig: Adding PbapClientService
05-09 15:30:18.649 17508 17508 V AdapterServiceConfig: Adding BluetoothOppService
基本上和上面红色字体表述的Service对应,SapService没有确认为什么会启动,后续再分析一下;
Profile 集合确认
需要启动的Service集合确认完成,这个代表了车机侧需要启动的Service,但是并不代表启动的Service都需要运行,因为蓝牙功能的正常运行需要C/S的配合,即需要判断RemoteDevice支持哪些协议,client和Server两侧的Profile交叉重叠的才是真正需要运行的协议或者是功能;
我们通过从手机上抓取log可知车机蓝牙UUID:
less
05-11 19:12:06.400 31145 32227 I bt_btif_dm: btif_dm_search_services_evt index:0 uuid:0000111e-0000-1000-8000-00805f9b34fb
05-11 19:12:06.400 31145 32227 I bt_btif_dm: btif_dm_search_services_evt index:1 uuid:00001105-0000-1000-8000-00805f9b34fb
05-11 19:12:06.400 31145 32227 I bt_btif_dm: btif_dm_search_services_evt index:2 uuid:0000112d-0000-1000-8000-00805f9b34fb
05-11 19:12:06.400 31145 32227 I bt_btif_dm: btif_dm_search_services_evt index:3 uuid:0000110b-0000-1000-8000-00805f9b34fb
05-11 19:12:06.400 31145 32227 I bt_btif_dm: btif_dm_search_services_evt index:4 uuid:0000110e-0000-1000-8000-00805f9b34fb
车机蓝牙UUID:
- 0000111e:Handsfree
- 00001105:ObexObjectPush
- 0000112d:SAP
- 0000110b:AdvAudioDist
- 0000110e:AvrcpController
手机蓝牙UUID:
- 0000110a -0000-1000-8000-00805f9b34fb:AudioSource;
- 00001105 -0000-1000-8000-00805f9b34fb:ObexObjectPush;
- 00001115 -0000-1000-8000-00805f9b34fb:PANU
- 00001116 -0000-1000-8000-00805f9b34fb:NAP
- 0000112f -0000-1000-8000-00805f9b34fb:PBAP_PSE(Phonebook Access Server)
- 00001112 -0000-1000-8000-00805f9b34fb:HSP_AG(Headset AG)
- 0000111f -0000-1000-8000-00805f9b34fb:Handsfree_AG(Handsfree Audio Gateway)
- 00001132 -0000-1000-8000-00805f9b34fb:MAS(Message Access Server)
- 00000000 -0000-1000-8000-00805f9b34fb:BASE_UUID
- 11c8b310-80e4-4276-afc0-f81590b2177f:Vendor specific
- 8ce255c0-200a-11e0-ac64-0800200c9a66:BluetoothChat,google提供的一个特定的UUID,属于Vendor specific
- 9664aa26-d76c-43ad-9775-d310f253a408:Vendor specific
我们知道,在之前描述UUID和Profile映射关系的时候,有些Profile的确定是需要两侧(手机侧和车机侧)共同持有匹配的UUID组合,Profile才能被加载;
我们根据上述的两侧的UUID,来确定一下最终需要被加载的Profile;
Phone UUID | Car UUID | Local Profile | Profile |
---|---|---|---|
0000111e(Handsfree) | 0000111f(Handsfree_AG) | HfpClientProfile | BluetoothHeadsetClient |
00001105(ObexObjectPush) | OppProfile | - | |
0000112d(SAP) | SapProfile | BluetoothSap | |
0000110a(AudioSource) | 0000110b(AdvAudioDist) | A2dpSinkProfile | BluetoothA2dpSink |
0000110e(AvrcpController) | BluetoothAvrcpController |
从log中提示,车机侧的Profile集合为:
arduino
New Profiles[HEADSET_CLIENT, A2DPSink, PAN, MAP Client, PbapClient]
这个是根据UUID匹配出来的Profile,但是实际加载的Profile却又是另一回事儿;
我们从图中可以看出,通过UUID匹配出来的是可支持的部分的Profiles,还不是真正的全部的Profile,因为有一部分Profile的加载不依赖BluetoothUUID,例如PbapClientProfile(电话簿),和真正开启的Profile是有本质区别的;
如上图,我们可以看出,提供可选择的开关:
- 通话音频 -- HfpClientProfile&PbapClientProfile;
- 媒体音频 -- AudioSink;