一、概述:
蓝牙协议的处理操作,主要涉及到使用SDK客户端的具体业务应用、Bluetooth服务端(蓝牙应用)、HAL层(HCI)以及蓝牙芯片(bluetooth chip)等不同的模块,本文通过分析HFP协议的连接过程,主要集中讲在Bluetooth应用中的代码。
二、从客户端的应用到Bluetooth服务端
通常我们操作连接HFP协议的客户端应用一般都是系统设置,使用Android SDK 中提供的BluetoothHeadsetClient这个类,其中的connect方法去连接具体的远程蓝牙设备,代码如下:
public boolean connect(BluetoothDevice device) {
IBluetoothHeadsetClient service = this.mService;
if (service != null && this.isEnabled() && isValidDevice(device)) {
try {
return service.connect(device); //调用服务端连接蓝牙
} catch (RemoteException var4) {
Log.e("BluetoothHeadsetClient", Log.getStackTraceString(new Throwable()));
return false;
}
}
.......
}
这里是一个典型的binder调用,对应的服务端就是位于Bluetooth蓝牙应用的HeadsetClientService,源码位置是
packages/apps/Bluetooth/src/com/android/bluetooth/hfpclient/HeadsetClientService.java
部分关键代码如下:
public class HeadsetClientService extends ProfileService {
private NativeInterface mNativeInterface = null; //JNI 接口,很多实际的功能都是通过此接口去调用native层的代码
//返回Binder接口
@Override
public IProfileServiceBinder initBinder() {
return new BluetoothHeadsetClientBinder(this);
}
/**
* binder本地实现
**/
private static class BluetoothHeadsetClientBinder extends IBluetoothHeadsetClient.Stub
implements IProfileServiceBinder {
private HeadsetClientService mService;
BluetoothHeadsetClientBinder(HeadsetClientService svc) {
mService = svc;
}
private HeadsetClientService getService() {
if (mService != null && mService.isAvailable()) {
return mService;
}
}
@Override
public boolean connect(BluetoothDevice device) {
HeadsetClientService service = getService();
if (service == null) {
return false;
}
return service.connect(device);
}
......
@Override
public boolean acceptCall(BluetoothDevice device, int flag) {
HeadsetClientService service = getService();
if (service == null) {
return false;
}
return service.acceptCall(device, flag);
}
}
/**
* 服务执行连接的方法,利用状态机去执行连接操作
**/
public boolean connect(BluetoothDevice device) {
HeadsetClientStateMachine sm = getStateMachine(device);
sm.sendMessage(HeadsetClientStateMachine.CONNECT, device);
return true;
}
//获取每个设备对应的状态机
private synchronized HeadsetClientStateMachine getStateMachine(BluetoothDevice device) {
HeadsetClientStateMachine sm = mStateMachineMap.get(device);
if (sm != null) {
return sm;
}
// Allocate a new SM
Log.d(TAG, "Creating a new state machine");
sm = mSmFactory.make(this, mSmThread, mNativeInterface);
mStateMachineMap.put(device, sm);
return sm;
}
// Handle messages from native (JNI) to java 这里是处理从native反馈回来的数据
public void messageFromNative(StackEvent stackEvent) {
HeadsetClientStateMachine sm = getStateMachine(stackEvent.device); //这里获取到对应远程设备的状态机
if (sm == null) {
Log.w(TAG, "No SM found for event " + stackEvent);
}
sm.sendMessage(StackEvent.STACK_EVENT, stackEvent);//信息交给StateMachine去处理handleMessage(Message msg)
}
}
-
BluetoothHeadsetClientBinder:
可以看到,服务初始化的时候新建了一个binder接口的本地实现–BluetoothHeadsetClientBinder,并且提供给客户端绑定,binder里面实现的connect方法会去调用HeadsetClientService的connect方法。 -
HeadsetClientStateMachine:
HeadsetClientService的connect这个方法里面,又使用了HeadsetClientStateMachine这个对象去处理发起连接的消息。我们不难猜到,这是一种状态机设计模式,对于连接的不同状态,状态机里面分别有对应的状态对象去处理业务逻辑(下面会单独介绍)。 -
NativeInterface:
这是JNI 接口,实际上对蓝牙的所有操作指令都要通过此接口去下发到HAL层,从而到达蓝牙芯片。同时,蓝牙芯片有信息反馈回来的时候,也是通过回调此接口中的方法来传递。
messageFromNative(StackEvent stackEvent):当蓝牙芯片有新的状态反馈的时候,会从chip —> HAL —> Bluetooth native —> NativeInterface 一路传上来,然后就会调用这个方法,把消息统一给到状态机去处理。
三、StateMachine 状态机
Bluetooth应用里面,大量使用了状态设计模式。所有的状态机都继承自StateMachine这个基类,内部通过一个Handler来实现消息处理。不管是客户端需要下发的操作指令(比如建立hfp连接),还是HAL层返回的蓝牙芯片的状态,都被当做一个个的消息放到了这个Handler里面去处理。而在Handler的内部,又存在一个由不同状态对象组成的栈,传递进来的消息会交给栈内对应的状态对象去处理。关键代码如下:
public class StateMachine {
//负责线程的转换,同时对消息做对应的分发
private static class SmHandler extends Handler {
/** The map of all of the states in the state machine */
private HashMap<State, StateInfo> mStateInfo = new HashMap<State, StateInfo>();
private StateInfo mStateStack[]; //对应此状态机所使用的一系列状态对象所形成的栈
private final void completeConstruction() {
mStateStack = new StateInfo[maxDepth];
mTempStateStack = new StateInfo[maxDepth];
setupInitialStateStack(); //HeadsetClientStateMachine初始的状态对象是Disconnected
/** Sending SM_INIT_CMD message to invoke enter methods asynchronously */
sendMessageAtFrontOfQueue(obtainMessage(SM_INIT_CMD, mSmHandlerObj));
}
public final void handleMessage(Message msg) {
/** State that processed the message */
State msgProcessedState = null;
msgProcessedState = processMsg(msg);
performTransitions(msgProcessedState, msg);
}
/**
* Process the message. If the current state doesn't handle
* it, call the states parent and so on. If it is never handled then
* call the state machines unhandledMessage method.
*调用状态栈里面的状态对象来处理消息,从栈顶的对象开始处理,
*如果当前状态对象处理不了,那就使用其父状态处理
* @return the state that processed the message
*/
private final State processMsg(Message msg) {
StateInfo curStateInfo = mStateStack[mStateStackTopIndex];
if (isQuit(msg)) {
transitionTo(mQuittingState);
} else {
while (!curStateInfo.state.processMessage(msg)) { //调用具体状态对象的实现方法
/**
* Not processed
*/
curStateInfo = curStateInfo.parentStateInfo;
if (curStateInfo == null) {
/**
* No parents left so it's not handled
*/
mSm.unhandledMessage(msg);
break;
}
if (mDbg) {
mSm.log("processMsg: " + curStateInfo.state.getName());
}
}
}
return (curStateInfo != null) ? curStateInfo.state : null;
}
}
}
四、HeadsetClientStateMachine状态机
HeadsetClientStateMachine是集成自StateMachine,并且它是负责对HFP协议的状态管理。在它的内部,定义了三种主要的状态类:Disconnected、Connecting、Connected,并且在HeadsetClientStateMachine初始化的时候,去实例化这三个状态类,并且把它们添加到栈里面,如下:
HeadsetClientStateMachine(HeadsetClientService context, Looper looper,
NativeInterface nativeInterface) {
mDisconnected = new Disconnected();
mConnecting = new Connecting();
mConnected = new Connected();
mAudioOn = new AudioOn();
addState(mDisconnected);
addState(mConnecting);
addState(mConnected);
addState(mAudioOn, mConnected);
setInitialState(mDisconnected);
}
还是继续说hfp连接的事,最开始的时候,状态机的当前状态为Disconnected,所以连接的请求会交给Disconnected去处理如下:
class Disconnected extends State {
public synchronized boolean processMessage(Message message) {
switch (message.what) {
case CONNECT:
BluetoothDevice device = (BluetoothDevice) message.obj;
if (!mNativeInterface.connect(getByteAddress(device))) {
// No state transition is involved, fire broadcast immediately
broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
BluetoothProfile.STATE_DISCONNECTED);
break;
}
mCurrentDevice = device;
transitionTo(mConnecting);
break;
}
果然是调用mNativeInterface的connect native方法,去做连接的动作。同时可以看到,状态也迁徙到了Connecting状态。
五、Bluetooth的Native方法
下面再来看看native方法,代码位于
packages/apps/Bluetooth/jni/com_android_bluetooth_hfpclient.cpp
下面来看看这个cpp文件的关键代码:
namespace android {
static bthf_client_interface_t* sBluetoothHfpClientInterface = NULL;
//初始化
static void initializeNative(JNIEnv* env, jobject object) {
//此方法是在com_android_bluetooth_btservice_AdapterService.cpp中实现,用以获取HAL层的接口
const bt_interface_t* btInf = getBluetoothInterface();
sBluetoothHfpClientInterface =
(bthf_client_interface_t*)btInf->get_profile_interface(
BT_PROFILE_HANDSFREE_CLIENT_ID);
bt_status_t status =
sBluetoothHfpClientInterface->init(&sBluetoothHfpClientCallbacks); //注册回调到HAL层
}
//根据MAC地址,执行连接的方法
static jboolean connectNative(JNIEnv* env, jobject object, jbyteArray address) {
if (!sBluetoothHfpClientInterface) return JNI_FALSE;
jbyte* addr = env->GetByteArrayElements(address, NULL);
if (!addr) {
jniThrowIOException(env, EINVAL);
return JNI_FALSE;
}
bt_status_t status = sBluetoothHfpClientInterface->connect((RawAddress*)addr); //调用HAL层发起连接
env->ReleaseByteArrayElements(address, addr, 0);
return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
}
......
//连接状态的回调接口
static void connection_state_cb(const RawAddress* bd_addr,
bthf_client_connection_state_t state,
unsigned int peer_feat,
unsigned int chld_feat) {
CallbackEnv sCallbackEnv(__func__);
if (!sCallbackEnv.valid()) return;
ScopedLocalRef<jbyteArray> addr(sCallbackEnv.get(), marshall_bda(bd_addr));
if (!addr.get()) return;
//这里调用java层的方法,实现连接状态的回调
sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,
(jint)state, (jint)peer_feat, (jint)chld_feat,
addr.get());
}
}
可以看到在初始化的时候,获取到了HAL层的接口指针,同时还向HAL层注册了一个回调接口。当HAL层有数据反馈的时候,例如一个远程设备连接HFP协议成功的消息,就会通过connection_state_cb这个方法传递上来,然后native里面继续把这个消息通过sCallbackEnv->CallVoidMethod调用method_onConnectionStateChanged方法,也就是调用到NativeInterface这个java文件里面的方法,把消息传递上来,如下:
public class NativeInterface {
//native层消息反馈调用的方法
private void onConnectionStateChanged(int state, int peerFeat, int chldFeat, byte[] address) {
StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
event.valueInt = state;
event.valueInt2 = peerFeat;
event.valueInt3 = chldFeat;
event.device = getDevice(address);
HeadsetClientService service = HeadsetClientService.getHeadsetClientService();
if (service != null) {
service.messageFromNative(event); //消息传递到HeadsetClientService
}
}
}
可以看到,HFP连接成功的消息又会传递给HeadsetClientService ,其实也就是传递给内部的HeadsetClientStateMachine状态机处理。
六、HeadsetClientStateMachine内部类Connecting和Connected
这个时候HeadsetClientStateMachine内部的状态是Connecting,看看它是怎么处理的:
class Connecting extends State {
@Override
public void enter() {
//进入Connecting状态之后,发送BluetoothProfile.STATE_CONNECTING广播
if (mPrevState == mDisconnected) {
broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTING,
BluetoothProfile.STATE_DISCONNECTED);
}
}
@Override
public synchronized boolean processMessage(Message message) {
switch (message.what) {
case StackEvent.STACK_EVENT:
switch (event.type) {
case StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED:
processConnectionEvent(event.valueInt, event.valueInt2, event.valueInt3,
event.device);
break;
}
break;
}
return HANDLED;
}
// in Connecting state
private void processConnectionEvent(int state, int peerFeat, int chldFeat,
BluetoothDevice device) {
switch (state) {
case HeadsetClientHalConstants.CONNECTION_STATE_SLC_CONNECTED:
//转到mConnected状态
transitionTo(mConnected);
break;
}
}
}
开始进入Connecting状态的时候,就会广播BluetoothProfile.STATE_CONNECTING,然后在收到连接成功的消息之后就会把自己的状态迁移到Connected状态。
下面再来看看进入Connected的逻辑处理:
class Connected extends State {
int mCommandedSpeakerVolume = -1;
@Override
public void enter() {
//广播连接成功的状态
if (mPrevState == mConnecting) {
broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
BluetoothProfile.STATE_CONNECTING);
MetricsLogger.logProfileConnectionEvent(
BluetoothMetricsProto.ProfileId.HEADSET_CLIENT);
}
mService.updateBatteryLevel();
}
@Override
public synchronized boolean processMessage(Message message) {
//处理连接另外一个设备
case CONNECT:
BluetoothDevice device = (BluetoothDevice) message.obj;
if (mCurrentDevice.equals(device)) {
// already connected to this device, do nothing
break;
}
mNativeInterface.connect(getByteAddress(device));
break;
//处理很多对电话的操作
case ACCEPT_CALL:
acceptCall(message.arg1);
break;
case REJECT_CALL:
rejectCall();
break;
case HOLD_CALL:
holdCall();
}
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
Intent intent = new Intent(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
.....
}
可以看到,在进入Connected状态之后,就会发送BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED广播,通知需要知道HFP连接状态的应用,对应的device设备已经连接上。
七、总结流程图
至此,一个完整的HFP连接流程就已经解析完毕,下面给出一个流程图来对整体的架构有一个更清晰的认识: