Bluetooth HFP连接过程分析

本文深入剖析了蓝牙协议HFP的连接过程,从Android客户端应用使用BluetoothHeadsetClient发起连接,经由Bluetooth服务端、HAL层到蓝牙芯片的交互。详细阐述了BluetoothHeadsetClientBinder、HeadsetClientStateMachine状态机及其内部状态(如Disconnected、Connecting、Connected)的工作原理,以及NativeInterface如何处理芯片反馈。整个流程涉及状态切换、消息传递和回调机制,揭示了蓝牙连接的底层逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述
蓝牙协议的处理操作,主要涉及到使用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)
    }

}
  1. BluetoothHeadsetClientBinder
    可以看到,服务初始化的时候新建了一个binder接口的本地实现–BluetoothHeadsetClientBinder,并且提供给客户端绑定,binder里面实现的connect方法会去调用HeadsetClientService的connect方法。

  2. HeadsetClientStateMachine
    HeadsetClientService的connect这个方法里面,又使用了HeadsetClientStateMachine这个对象去处理发起连接的消息。我们不难猜到,这是一种状态机设计模式,对于连接的不同状态,状态机里面分别有对应的状态对象去处理业务逻辑(下面会单独介绍)。

  3. 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连接流程就已经解析完毕,下面给出一个流程图来对整体的架构有一个更清晰的认识:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值