蓝牙流程介绍

1 基础知识介绍

1.1 缩略语

BTIF: Bluetooth Interface 
BTU : Bluetooth Upper Layer 
BTM: Bluetooth Manager 
BTE: Bluetooth embedded system 
BTA :Blueetooth application layer 
CO: call out\CI: call in 
HF : Handsfree Profile 
HH: HID Host Profile 
HL: Health Device Profile 
AV:audio\vidio 
ag: audio gateway 
ar: audio/video registration 
gattc: GATT client 
BLE: Bluetooth Low Energy

1.2 android蓝牙结构

这里写图片描述
再把左边的部分展开来看: 
这里写图片描述
再把它摊开来看: 
这里写图片描述
代码分布: 
frameworks/base/core/java/Android/Bluetooth
frameworks/base/services/java/com/android/server/BluetoothManagerService.java
里面提供java层使用的一些类和一些aidl文件,供其他进程调用。打开和关闭蓝牙的公共接口也在这里面。 
packages/apps/Bluetooth:对应Bluetooth.apk,作为系统的蓝牙的核心进程而存在,com.android.bluetooth,调用framework的打开蓝牙接口后会启动该进程。其内部实现了多种上层蓝牙模式:opp,hfp,a2dp,hdp,hid等,并通过JNI调用与hal层完成联系。 
/hardware/libhardware/include/hardware/bluetooth.h: hal层接口头文件 
external/bluetooth/bluedroid:bluedroid官方协议栈 
packages/apps/Settings/src/com/android/settings/Bluetooth: setting中蓝牙部分,界面相关 
bluedroid蓝牙的调用方式:从apk到framework,framework再通过binder调用bluetooth应用,在通过应用层利用jni调用hal层实现蓝牙的各种请求。

1.3 协议简介

这里写图片描述

  • Core Specification(核心规范),用于规定蓝牙设备必须实现的通用功能和协议层次。它由软件和硬件模块组成,两个模块之间的信息和数据通过主机控制接口(HCI)的解释才能进行传递。
  • Profiles(蓝牙应用规范),它从应用场景的角度为蓝牙技术的使用制定了不同的规范。这也是和大众日常生活接触最多的一部分。蓝牙支持很多Profiles,下文将介绍几种使用最广泛的蓝牙应用规范

OPP:文件传输规范 
hfp:和电话相关,接听、挂断电话,以及连接sco通路 
a2dp:蓝牙立体声规范,其中包含avrcp规范,avrcp规范实现了听歌时暂停、上下歌曲选择等控制模式。目前蓝牙耳机一般都支持这两种规范。 
hid:人机交互规范,蓝牙鼠标键盘等 
phap:电话号码簿访问协议 
hdp:蓝牙医疗相关规范

2 打开蓝牙

2.1 文件路径

文件路径: 
frameworks/base/core/java/android/bluetooth/BluetoothAdapter.java 
frameworks/base/services/java/com/android/server/BluetoothManagerService.java 
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterService.java 
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/AdapterState.java 
packages/apps/Bluetooth/jni/com_android_bluetooth_btservice_AdapterService.cpp 
hardware/libhardware/include/hardware/bluetooth.h 
external/bluetooth/bluedroid/btif/src/bluetooth.c

2.2 上层打开流程

先从framework提供的公共接口开始: 
打开蓝牙使用BluetoothAdapter.java提供的方法getDefaultAdapter(),该方法中先获取BluetoothManagerService.java提供的binder对象,并且用该对象创建BluetoothAdapter实例。 
这里写图片描述
获取到BluetoothAdapter实例用,调用enable方法,BluetoothAdapter的enable方法会调用BluetoothManagerService.java的enable,直接看BluetoothManagerService.java,在其中enable方法会启动一个service,这个service就是AdapterService。在启动这个service之后,先不看这个service的具体动作,继续看BluetoothManagerService会做什么。 
这里写图片描述
在启动这个AdapterService之后,先不看这个AdapterService的具体动作,继续看BluetoothManagerService中的执行流程。在启动AdapterService,会回调mConnection的onServiceConnected()方法,在该方法中发送MESSAGE_BLUETOOTH_SERVICE_CONNECTED 消息,在处理该消息时,做了下面三件事: 
1、得到AdapterService返回的IBluetooth.aidl接口的实现类。 
mBluetooth = IBluetooth.Stub.asInterface(service) 
2.调用IBluetooth.aidl提供的接口注册AdapterService的回调方法。 
这样,不仅BluetoothManagerService可以通过aidl调用AdapterService中的方法,也实现了AdapterService可以回调BluetoothManagerService中的方法。回调方法如下所示,记住这个回调方法,后面会讲到。 
mBluetooth.registerCallback(mBluetoothCallback) 
这里写图片描述
3、第三件事就是调用IBluetooth.aidl提供的enable方法。下面就进入到AdapterService.java中。

下面就进入了package/apps下: 
首先看onBind方法,返回了一个对象mBinder。执行其中的enable方法, 
这里写图片描述
调用mAdapterStateMachine的sendMessage方法,发送USER_TURN_ON消息。。AdapterState.java是一个状态机,有以下几种状态: 
mOffState:关闭 
mPendingCommandState:活动,打开中 
mOnState:打开 
初始状态是mOffState。

发送消息后,在OffState状态下处理消息,首先切换状态到mPendingCommandState,然后调用adapterService.processStart()方法。 
这里写图片描述
继续回到adapterService。执行如下代码,启动手机支持的所有profile, 
这里写图片描述
手机支持的所有profile如下: 
这里写图片描述
挨个启动上面所有的服务。每个服务启动之后都会调用adapterService中的notifyProfileServiceStateChanged方法。该方法中发送MESSAGE_PROFILE_SERVICE_STATE_CHANGED消息。消息处理如下: 
这里写图片描述
如果所有服务都起来之后,给状态机AdapterState发送消息AdapterState.STARTED。 
根据以上流程,现在状态机在mPendingCommandState状态,在该状态下处理STARTED消息。代码如下: 
这里写图片描述
终于看到native方法。 
对应的native方法在com_android_bluetooth_btservice_AdapterService.cpp中。调用了sBluetoothInterface结构体中的enable方法。该结构体的定义在bluetooth.h中,具体实现在bluetooth.c中。后面的代码这里不做具体分析。 
这里写图片描述

2.3 底层回调流程

回调主要使用 
HAL_CBACK(bt_hal_cbacks, device_found_cb, num_properties, properties); HAL_CBACK就是一宏定义,就是调用结构体中对应的方法。 
这里写图片描述
这里重点看一下一个bt_callbacks_t结构体,定义在bluetooth.h中,如下 
这里写图片描述
初始化在bluetooth.c中,初始化的流程。

回到AdapterService.java,在服务启动时调用了initNative方法。看下jni中的initNative方法: 
这里写图片描述
还是sBluetoothInterface结构体,继续看bluetooth.c文件,可以看到init方法中将地址sBluetoothCallbacks赋值给bt_callbacks_t,也就是,回调结构体的地址指向了sBluetoothCallbacks,看下sBluetoothCallbacks的定义。任然在jni中定义。这样在启动AdapterService时将回调地址传入了hal层。 
这里写图片描述
最后,在驱动层完成蓝牙打开之后,会执行以下两个回调方法: 
adapter_properties_callback:返回手机蓝牙设备的地址、名称、UUID等。 
adapter_state_change_callback:更新AdapterProperties中蓝牙状态,发送广播通知蓝牙状态变化。

具体分析下adapter_state_change_callback。对应的jni方法为:adapter_state_change_callback,方法执行以下方法。 
callbackEnv->CallVoidMethod(sJniCallbacksObj, method_stateChangeCallback, (jint)status); 
也就是回调java层代码:JniCallback.java文件中stateChangeCallback方法。这时跳转到AdapterState.java中,执行stateChangeCallback()方法;发送了ENABLED_READY消息。根据以上分析,这时状态机还处于PendingCommandState,在该状态下处理ENABLED_READY消息, 
这里写图片描述
做下面两个动作:状态切换到mOnState;更新adapterProperties中的蓝牙状态信息;通知蓝牙状态变为打开。具体看下notifyAdapterStateChange方法。主要是调用了adapterService类的方法。 
adapterService.updateAdapterState(oldState, newState); 
来到adapterService类。 
这里写图片描述
可以看到,看是执行该服务中注册的回调方法。应该还能记得,之前在打开蓝牙操作初期,在BluetoothManagerService中注册了回调方法。因此又跳转到framework中,执行回调方法。在该回调方法中,发送广播通知蓝牙状态变化。

2.4 总结

蓝牙打开的流程到这里,蓝牙打开从framework公共接口开始调用enable方法,执行到bluetooth.apk中,在该应用中通过jni注册回调方法和调用hal层打开蓝牙方法,在驱动层完成蓝牙上电等操作后,通过hal-jni回调到应用层中,应用通过aidl回调通知framework蓝牙状态变化,framework发送广播通知大家蓝牙打开。

3 搜索蓝牙

3.1 新增代码路径

packages/apps/Bluetooth/src/com/android/bluetooth/btservice/RemoteDevices.java 
packages/apps/Settings/src/com/android/settings/Bluetooth/*

3.2 搜索过程

BluetoothSettings.java中的startScanning命令,开始搜索。 
如果a2dp没有在播放,就调用BluetoothAdapter.java中的startDiscovery()方法。 
然后又回到AdapterService中,调用startDiscovery方法。在该方法中直接进入到jni层。 
这里写图片描述
在jni层的函数中执行如下语句,依然是该结构体。 
int ret = sBluetoothInterface->start_discovery(); 
这里写图片描述
具体的底层搜索方法暂不往下追踪。驱动层完成搜索之后,返回状态,根据返回的消息进行处理。回调消息分如下几种:包括搜索结果返回消息、搜索完成消息、搜索取消等。 
BTA_DM_INQ_RES_EVT 
BTA_DM_INQ_CMPL_EVT 
BTA_DM_DISC_RES_EVT 
BTA_DM_DISC_BLE_RES_EVT 
BTA_DM_DISC_CMPL_EVT 
BTA_DM_DI_DISC_CMPL_EVT 
BTA_DM_SEARCH_CANCEL_CMPL_EVT

当收到搜索结果放回消息之后,会执行以下回调方法。 
这里写图片描述
其中后面两个参数:设备属性个数和设备属性。其中设备属性包括:地址、名称、信号强度等属性。根据上面所讲的回调方法初始化,直接找到jni层的对应回调方法,可以看到会先后调用以下两个方法。 
这里写图片描述
其中devicePropertyChangedCallback方法更新RemoteDevice类中的变量值。 
deviceFoundCallback方法发送BluetoothDevice.ACTION_FOUND广播,通知setting中要更新界面显示该设备。

4 蓝牙配对

4.1 新增代码路径

packages/apps/Settings/src/com/android/settings/Bluetooth/* 
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/ BondStateMachine.java 
packages/apps/Settings/src/com/android/settings/Bluetooth/BluetoothEventManager.java 
packages/apps/Settings/src/com/android/settings/Bluetooth/CachedBluetoothDevice.java

4.2 配对过程分析

在setting中点击设备之后开始进行配对。调用BluetoothDevice的createBond方法,走到AdapterService.java中的createBond方法。驱动BondStateMachine中的状态从StableState到 
PendingCommandState。然后调用jni层createBondNative方法。又来到hal层接口sBluetoothInterface: 
sBluetoothInterface->create_bond((bt_bdaddr_t *)addr); 
同样,在底层完成配对之后进行回调: 
这里写图片描述
找到对应的回调方法,JniCallback.java中的 
void bondStateChangeCallback(int status, byte[] address, int newState) { 
mBondStateMachine.bondStateChangeCallback(status, address, newState); 
}

5 Opp文件传输

只有opp的弹框是bluetooth里,其他都是setting中弹框。

5.1 发送文件

点击用蓝牙分享文件后,会调用BluetoothOppLauncherActivity.java,其中会判断当前蓝牙是否打开,如果没打开则进入BluetoothOppBtEnableActivity.java打开蓝牙,如果已经打开,则直接进入BluetoothDevicePicker.java选择传输文件设备选择。

6 Map消息访问profile

蓝牙开启后会启动BluetoothMapService.java 
在BluetoothMapService.java调用了BluetoothMapMasInstance.java的startRfcommSocketListener()方法,监听rfcomm端的连接请求,如果有连接请求,调用BluetoothMapService 的 onConnect()方法,在该方法里发送广播到setting中,setting弹框,用户确认后发送广播ACTION_CONNECTION_ACCESS_REPLY通知BluetoothMapService,BluetoothMapService收到广播后调用onConnectHandler方法,调用startObexServerSession方法,启动Obex传输通道。

7 AVRCP

  1. 手机侧主动改变状态。注册了一个RemoteControllerWeak(RemoteController),当手机端的播放器暂停、快进、快退、切歌等操作后,会调用远程控制接口更新播放状态、歌曲信息。mTrackChangedNT、mPlayStatusChangedNT、mPlayPosChangedNT三个变量的值决定是否会通知对端进行同步;这三个值有jni回调改变,即对端回调改变。
  2. 对端主动改变状态:1)快进、快退底层有回调handlePassthroughCmd,来通知avrcp实时更新进度条,并同步给对端。部分车载会上报快进、快退的keycode给手机端,播放器利用RemoteController更新状态。2)播放、暂停,对端上报keycode,播放器RemoteController控制avrcp中状态切换

8 Pbap

类似map,连接后弹框都是广播给setting弹框

9 功耗

之前的audiopath比较复杂,要到DSP里面去硬解码。现在比较简单了,从驱动角度看,收到AF写下来的PCM数据后,经协议栈SBC编码后经HCI接口写入到蓝牙芯片,然后就发出去了。 SBC编码是针对蓝牙设备的一种音频编码方式,压缩率中等,但是cpu消耗低。电话对应HFP profile,音乐对应Profile。电话使用的是同步链路SCO,音乐时异步链路ACL,使用A2DP profile。另外还有其它profile,传问题,获取电话本,短信同步等。 使用A2DP时,先是进行音乐的解码,然后再把解码后的PCM进行SBC编码。A2DP支持下行audio编码采样率范围为8k-48k。在高通的大多数方案里面,SBC编码是在DSP里做的。 HFP,有两个角色AG和HF,正常情况下手机作为AG,耳机作为HF。但是手机代码中也有HF部分的代码 手机蓝牙芯片是三合一的,包括BT,FM和WLAN,使用一根天线。蓝牙耳机和蓝牙模块之间通过AT命令进行通信,分为master和slave,谁先连对方谁就是master。蓝牙功耗待机1mA左右,传文件要100多mA,听音乐60左右,打电话30。