BT_ uuid & profile & service映射关系描述

基于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;
相关推荐
MinIO官方账号1 小时前
从 HDFS 迁移到 MinIO 企业对象存储
人工智能·分布式·postgresql·架构·开源
运维Z叔2 小时前
云安全 | AWS S3存储桶安全设计缺陷分析
android·网络·网络协议·tcp/ip·安全·云计算·aws
hai405872 小时前
Spring Boot中的响应与分层解耦架构
spring boot·后端·架构
Reese_Cool3 小时前
【C语言二级考试】循环结构设计
android·java·c语言·开发语言
平凡シンプル3 小时前
安卓 uniapp跨端开发
android·uni-app
elina80134 小时前
安卓实现导入Excel文件
android·excel
严文文-Chris4 小时前
【设计模式-享元】
android·java·设计模式
趋势大仙4 小时前
SQLiteDatabase insert or replace数据不生效
android·数据库
DS小龙哥4 小时前
QT For Android开发-打开PPT文件
android·qt·powerpoint
伯牙碎琴5 小时前
十一、SOA(SOA的具体设计模式)
架构