Android蓝牙开发【六】hfp连接

HFP (Hands-free Profile),让蓝牙设备(如蓝牙耳机)可以控制电话,如接听、挂断、拒接、语音拨号等,拒接、语音拨号要看蓝牙耳机及电话是否支持。

HFP定义了音频网关(AG)和免提组件(HF)两个角色: 
音频网关(AG) – 该设备为音频(特别是手机)的输入/输出网关。 
免提组件(HF) – 该设备作为音频网关的远程音频输入/输出机制,并可提供若干遥控功能。


 

2 手机音频连接

对于手机音频的使用,首先连接的蓝牙设备需要支持hfp协议,并且需要与该设备进行配对,如何进行蓝牙配对这里就不细说了,可以参照我的其他文章。主要分析下其连接过程。 
对于系统自带应用Settings中已配对的蓝牙设备界面(如下图所示), 
这里写图片描述
其对应文件路径: 
packages/apps/Settings/src/com/android/settings/bluetooth/DeviceProfilesSettings.java 
点击手机音频进行连接,调用onPreferenceChange。

 

 
  1. public boolean onPreferenceChange(Preference preference, Object newValue) {

  2. if (preference == mDeviceNamePref) { //重命名

  3. mCachedDevice.setName((String) newValue);

  4. } else if (preference instanceof CheckBoxPreference) {//check box

  5. LocalBluetoothProfile prof = getProfileOf(preference); //获取对应的profile

  6. onProfileClicked(prof, (CheckBoxPreference) preference);

  7. return false; // checkbox will update from onDeviceAttributesChanged() callback

  8. } else {

  9. return false;

  10. }

  11. return true;

  12. }

接着看onProfileClicked()函数处理

 
  1. private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) {

  2. BluetoothDevice device = mCachedDevice.getDevice(); //获取配对的蓝牙设备

  3. int status = profile.getConnectionStatus(device); //获取profile的连接状态

  4. boolean isConnected =

  5. status == BluetoothProfile.STATE_CONNECTED;

  6. if (isConnected) { //如果是连接状态则断开连接

  7. askDisconnect(getActivity(), profile);

  8. } else { //没有连接

  9. if (profile.isPreferred(device)) { //获取profile是否是首选

  10. // profile is preferred but not connected: disable auto-connect

  11. profile.setPreferred(device, false); //设置对应profile的PRIORITY 为off,防止自动连接

  12. refreshProfilePreference(profilePref, profile); //刷新check box状态

  13. } else {

  14. profile.setPreferred(device, true); //设置对应profile的PRIORITY 为on

  15. mCachedDevice.connectProfile(profile); //连接指定profile

  16. }

  17. }

  18. }

接着查看CachedBluetoothDevice中的connectProfile函数连接某一profile。

 
  1. void connectProfile(LocalBluetoothProfile profile) {

  2. mConnectAttempted = SystemClock.elapsedRealtime();

  3. // Reset the only-show-one-error-dialog tracking variable

  4. mIsConnectingErrorPossible = true;

  5. connectInt(profile); //连接profile

  6. refresh(); // 刷新ui

  7. }

  8.  
  9. synchronized void connectInt(LocalBluetoothProfile profile) {

  10. //查看是否配对,如果没有配对则进行配对,配对后进行连接,

  11. //如果配对则直接连接

  12. if (!ensurePaired()) {

  13. return;

  14. }

  15. if (profile.connect(mDevice)) {//连接

  16. return;

  17. }

  18. }

connectProfile() ——>connectInt() 
connectInt()函数中会先判断是否配对,如果没有配对则开始配对,配对成功后连接profile。 
如果已经配对则直接连接profile。 
对于profile.connect(mDevice)会根据profile调用各自对应的connect方法。(如手机音频则对应HeadsetProfile,媒体音频对应A2dpProfile)。这里查看手机音频的连接HeadsetProfile

 
  1. public boolean connect(BluetoothDevice device) {

  2. if (mService == null) return false;

  3. //获取连接hfp的设备

  4. List<BluetoothDevice> sinks = mService.getConnectedDevices();

  5. if (sinks != null) {

  6. for (BluetoothDevice sink : sinks) {

  7. mService.disconnect(sink); //断开连接

  8. }

  9. } //连接hfp。

  10. return mService.connect(device);

  11. }

HeadsetProfile.java中的connect()方法,mService是通过getProfileProxy获取的BluetoothHeadset代理对象,通过其进行hfp相关操作。 
mService.connect跳到Bluetooth应用中, 
代码路径:packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java 
先调用到内部类BluetoothHeadsetBinder的connect方法。

 
  1. public boolean connect(BluetoothDevice device) {

  2. HeadsetService service = getService();

  3. if (service == null) return false;

  4. return service.connect(device);

  5. }

该方法中很明显是去调用HeadsetService的connect方法。

 

 
  1. public boolean connect(BluetoothDevice device) {

  2. enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,

  3. "Need BLUETOOTH ADMIN permission");

  4. if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {

  5. return false; //检查priority

  6. }

  7. int connectionState = mStateMachine.getConnectionState(device);

  8. if (connectionState == BluetoothProfile.STATE_CONNECTED ||

  9. connectionState == BluetoothProfile.STATE_CONNECTING) {

  10. return false; //检查连接状态

  11. }

  12. mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);

  13. return true;

  14. }


HeadsetService的connect()函数会对priority和连接状态进行必要的检查,不符合条件则返回false。符合条件则向状态机发送消息HeadsetStateMachine.CONNECT。 
此时HeadsetStateMachine中状态应该是Disconnected,所以查看Disconnected state中的处理

 
  1. BluetoothDevice device = (BluetoothDevice) message.obj;

  2. //发送广播,正在连接hfp

  3. broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,

  4. BluetoothProfile.STATE_DISCONNECTED);

  5. //连接远端设备。

  6. if (!connectHfpNative(getByteAddress(device)) ) {

  7. //连接失败,向外发送连接失败广播

  8. broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,

  9. BluetoothProfile.STATE_CONNECTING);

  10. break;

  11. }

  12. synchronized (HeadsetStateMachine.this) {

  13. mTargetDevice = device;

  14. transitionTo(mPending); //切换到pending状态

  15. }

  16. sendMessageDelayed(CONNECT_TIMEOUT, 30000);

HeadsetStateMachine调用connectHfpNative()函数来进行手机音频的连接。connectHfpNative是native方法,跳转到com_android_bluetooth_hfp.cpp中,调用对应的方法connectHfpNative

 
  1. static jboolean connectHfpNative(JNIEnv *env, jobject object, jbyteArray address) {

  2. jbyte *addr;

  3. bt_status_t status;

  4. if (!sBluetoothHfpInterface) return JNI_FALSE;

  5. addr = env->GetByteArrayElements(address, NULL);

  6. if (!addr) {

  7. jniThrowIOException(env, EINVAL);

  8. return JNI_FALSE;

  9. }

  10. if ((status = sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {

  11. ALOGE("Failed HF connection, status: %d", status);

  12. }

  13. env->ReleaseByteArrayElements(address, addr, 0);

  14. return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;

  15. }

其中sBluetoothHfpInterface->connect会跳到蓝牙协议栈进行连接,协议栈就先不进行分析了。

 

3 连接状态

当协议栈连接状态改变会回调com_android_bluetooth_hfp.cpp中的方法connection_state_callback()。

 
  1. static void connection_state_callback(bthf_connection_state_t state, bt_bdaddr_t* bd_addr) {

  2. jbyteArray addr;

  3. CHECK_CALLBACK_ENV

  4. addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t));

  5. if (!addr) {

  6. checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

  7. return;

  8. }

  9. sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr);

  10. sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged,

  11. (jint) state, addr);

  12. checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__);

  13. sCallbackEnv->DeleteLocalRef(addr);

  14. }

在connection_state_callback方法中会从cpp层调用到java层,对应于HeadsetStateMachine中的onConnectionStateChanged函数

 
  1. private void onConnectionStateChanged(int state, byte[] address) {

  2. StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);

  3. event.valueInt = state;

  4. event.device = getDevice(address);

  5. sendMessage(STACK_EVENT, event);

  6. }


onConnectionStateChanged函数中发送消息STACK_EVENT(携带状态和蓝牙地址),此时是Pending state,收到该消息调用processConnectionEvent。 
正常连接成功应该会先收到HeadsetHalConstants.CONNECTION_STATE_CONNECTING状态,然后收到HeadsetHalConstants.CONNECTION_STATE_CONNECTED状态。

 
  1. //发送广播,连接成功

  2. broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,

  3. BluetoothProfile.STATE_CONNECTING);

  4. synchronized (HeadsetStateMachine.this) {

  5. mCurrentDevice = mTargetDevice; //mCurrentDevice表示已连接的设备

  6. mTargetDevice = null; //mTargetDevice表示要连接的设备

  7. transitionTo(mConnected); //切换到Connected状态

  8. }


收到HeadsetHalConstants.CONNECTION_STATE_CONNECTED状态,后向外发送连接成功的广播,状态机切换到Connected状态

 

 
  1. private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {

  2. /* Notifying the connection state change of the profile before sending the intent for

  3. connection state change, as it was causing a race condition, with the UI not being

  4. updated with the correct connection state. */

  5. mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET,newState, prevState);

  6. Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);

  7. intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);

  8. intent.putExtra(BluetoothProfile.EXTRA_STATE, newState);

  9. intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);

  10. mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);

  11. }

在mService.notifyProfileConnectionStateChanged中会将手机音频的proirty设置为auto_connect,并且向外发送BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED广播。

在其他应用中可以通过广播接收者注册BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED该广播,用来监听hfp的连接状态。


 

4 更新ui

当手机音频连接成功后,Settings应用中会更新ui界面。 
LocalBluetoothProfileManager中会对所有的profile进行管理,其将hfp的profile添加到BluetoothEventManager中,BluetoothEventManager会注册蓝牙状态改变、各profile状态改变等广播。 
当BluetoothEventManager收到BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED广播后,会根据action获取对应的handler,调用对应handler的onReceive方法。 
接收到该广播跳到LocalBluetoothProfileManager内部类StateChangedHandler.onReceive->CachedBluetoothDevice.onProfileStateChanged ->refresh ->dispatchAttributesChanged 
接着跳到DeviceProfilesSettings中的onDeviceAttributesChanged ->refresh.这里会对界面进行更新,显示其连接状态信息。

hfp连接过程已经分析完了,而断开连接到过程和连接整体相差不多,就不再细说了。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flybirding10011

谢谢支持啊999

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值