Android蓝牙开发【六】hfp连接
来源:互联网 发布:vb 时间函数 编辑:程序博客网 时间:2024/06/07 19:08
本文主要内容是蓝牙手机音频的连接、断开流程分析,对应蓝牙HFP profile。
该文章是基于Android源码4.3的
1 hfp简单介绍
HFP (Hands-free Profile),让蓝牙设备(如蓝牙耳机)可以控制电话,如接听、挂断、拒接、语音拨号等,拒接、语音拨号要看蓝牙耳机及电话是否支持。
HFP定义了音频网关(AG)和免提组件(HF)两个角色:
音频网关(AG) – 该设备为音频(特别是手机)的输入/输出网关。
免提组件(HF) – 该设备作为音频网关的远程音频输入/输出机制,并可提供若干遥控功能。
2 手机音频连接
对于手机音频的使用,首先连接的蓝牙设备需要支持hfp协议,并且需要与该设备进行配对,如何进行蓝牙配对这里就不细说了,可以参照我的其他文章。主要分析下其连接过程。
对于系统自带应用Settings中已配对的蓝牙设备界面(如下图所示),
其对应文件路径:
packages/apps/Settings/src/com/android/settings/bluetooth/DeviceProfilesSettings.java
点击手机音频进行连接,调用onPreferenceChange。
public boolean onPreferenceChange(Preference preference, Object newValue) { if (preference == mDeviceNamePref) { //重命名 mCachedDevice.setName((String) newValue); } else if (preference instanceof CheckBoxPreference) {//check box LocalBluetoothProfile prof = getProfileOf(preference); //获取对应的profile onProfileClicked(prof, (CheckBoxPreference) preference); return false; // checkbox will update from onDeviceAttributesChanged() callback } else { return false; } return true;}
接着看onProfileClicked()函数处理
private void onProfileClicked(LocalBluetoothProfile profile, CheckBoxPreference profilePref) { BluetoothDevice device = mCachedDevice.getDevice(); //获取配对的蓝牙设备 int status = profile.getConnectionStatus(device); //获取profile的连接状态 boolean isConnected = status == BluetoothProfile.STATE_CONNECTED; if (isConnected) { //如果是连接状态则断开连接 askDisconnect(getActivity(), profile); } else { //没有连接 if (profile.isPreferred(device)) { //获取profile是否是首选 // profile is preferred but not connected: disable auto-connect profile.setPreferred(device, false); //设置对应profile的PRIORITY 为off,防止自动连接 refreshProfilePreference(profilePref, profile); //刷新check box状态 } else { profile.setPreferred(device, true); //设置对应profile的PRIORITY 为on mCachedDevice.connectProfile(profile); //连接指定profile } }}
接着查看CachedBluetoothDevice中的connectProfile函数连接某一profile。
void connectProfile(LocalBluetoothProfile profile) { mConnectAttempted = SystemClock.elapsedRealtime(); // Reset the only-show-one-error-dialog tracking variable mIsConnectingErrorPossible = true; connectInt(profile); //连接profile refresh(); // 刷新ui}synchronized void connectInt(LocalBluetoothProfile profile) { //查看是否配对,如果没有配对则进行配对,配对后进行连接, //如果配对则直接连接 if (!ensurePaired()) { return; } if (profile.connect(mDevice)) {//连接 return; }}connectProfile() ——>connectInt()
connectInt()函数中会先判断是否配对,如果没有配对则开始配对,配对成功后连接profile。
如果已经配对则直接连接profile。
对于profile.connect(mDevice)会根据profile调用各自对应的connect方法。(如手机音频则对应HeadsetProfile,媒体音频对应A2dpProfile)。这里查看手机音频的连接HeadsetProfile。
public boolean connect(BluetoothDevice device) { if (mService == null) return false; //获取连接hfp的设备 List<BluetoothDevice> sinks = mService.getConnectedDevices(); if (sinks != null) { for (BluetoothDevice sink : sinks) { mService.disconnect(sink); //断开连接 } } //连接hfp。 return mService.connect(device);}HeadsetProfile.java中的connect()方法,mService是通过getProfileProxy获取的BluetoothHeadset代理对象,通过其进行hfp相关操作。
mService.connect跳到Bluetooth应用中,
代码路径:packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
先调用到内部类BluetoothHeadsetBinder的connect方法。
public boolean connect(BluetoothDevice device) { HeadsetService service = getService(); if (service == null) return false; return service.connect(device);}
该方法中很明显是去调用HeadsetService的connect方法。
public boolean connect(BluetoothDevice device) { enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH ADMIN permission"); if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) { return false; //检查priority } int connectionState = mStateMachine.getConnectionState(device); if (connectionState == BluetoothProfile.STATE_CONNECTED || connectionState == BluetoothProfile.STATE_CONNECTING) { return false; //检查连接状态 } mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device); return true;}
HeadsetService的connect()函数会对priority和连接状态进行必要的检查,不符合条件则返回false。符合条件则向状态机发送消息HeadsetStateMachine.CONNECT。
此时HeadsetStateMachine中状态应该是Disconnected,所以查看Disconnected state中的处理
BluetoothDevice device = (BluetoothDevice) message.obj;//发送广播,正在连接hfpbroadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);//连接远端设备。if (!connectHfpNative(getByteAddress(device)) ) { //连接失败,向外发送连接失败广播 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING); break;}synchronized (HeadsetStateMachine.this) { mTargetDevice = device; transitionTo(mPending); //切换到pending状态}sendMessageDelayed(CONNECT_TIMEOUT, 30000);HeadsetStateMachine调用connectHfpNative()函数来进行手机音频的连接。connectHfpNative是native方法,跳转到com_android_bluetooth_hfp.cpp中,调用对应的方法connectHfpNative
static jboolean connectHfpNative(JNIEnv *env, jobject object, jbyteArray address) { jbyte *addr; bt_status_t status; if (!sBluetoothHfpInterface) return JNI_FALSE; addr = env->GetByteArrayElements(address, NULL); if (!addr) { jniThrowIOException(env, EINVAL); return JNI_FALSE; } if ((status = sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) { ALOGE("Failed HF connection, status: %d", status); } env->ReleaseByteArrayElements(address, addr, 0); return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;}
其中sBluetoothHfpInterface->connect会跳到蓝牙协议栈进行连接,协议栈就先不进行分析了。
3 连接状态
当协议栈连接状态改变会回调com_android_bluetooth_hfp.cpp中的方法connection_state_callback()。
static void connection_state_callback(bthf_connection_state_t state, bt_bdaddr_t* bd_addr) { jbyteArray addr; CHECK_CALLBACK_ENV addr = sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)); if (!addr) { checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); return; } sCallbackEnv->SetByteArrayRegion(addr, 0, sizeof(bt_bdaddr_t), (jbyte*) bd_addr); sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onConnectionStateChanged, (jint) state, addr); checkAndClearExceptionFromCallback(sCallbackEnv, __FUNCTION__); sCallbackEnv->DeleteLocalRef(addr);}在connection_state_callback方法中会从cpp层调用到java层,对应于HeadsetStateMachine中的onConnectionStateChanged函数
private void onConnectionStateChanged(int state, byte[] address) { StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); event.valueInt = state; event.device = getDevice(address); sendMessage(STACK_EVENT, event);}
onConnectionStateChanged函数中发送消息STACK_EVENT(携带状态和蓝牙地址),此时是Pending state,收到该消息调用processConnectionEvent。
正常连接成功应该会先收到HeadsetHalConstants.CONNECTION_STATE_CONNECTING状态,然后收到HeadsetHalConstants.CONNECTION_STATE_CONNECTED状态。
//发送广播,连接成功broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);synchronized (HeadsetStateMachine.this) { mCurrentDevice = mTargetDevice; //mCurrentDevice表示已连接的设备 mTargetDevice = null; //mTargetDevice表示要连接的设备 transitionTo(mConnected); //切换到Connected状态}
收到HeadsetHalConstants.CONNECTION_STATE_CONNECTED状态,后向外发送连接成功的广播,状态机切换到Connected状态
private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { /* Notifying the connection state change of the profile before sending the intent for connection state change, as it was causing a race condition, with the UI not being updated with the correct connection state. */ mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.HEADSET,newState, prevState); Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mService.sendBroadcast(intent, HeadsetService.BLUETOOTH_PERM);}
在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连接过程已经分析完了,而断开连接到过程和连接整体相差不多,就不再细说了。
- Android 蓝牙开发(六)hfp连接
- Android蓝牙开发【六】hfp连接
- Android蓝牙开发【七】hfp音频连接
- Android 蓝牙开发(七)hfp音频连接
- android 蓝牙 HFP 总结
- Android 蓝牙开发(八)hfp接听、挂断电话
- Android蓝牙开发【八】hfp接听、挂断电话
- android 蓝牙hfp client实现简介
- android 蓝牙hfp server实现简介
- android蓝牙hfp client使用例
- 蓝牙的HFP协议
- 蓝牙 HFP AT集
- 蓝牙之八-HFP
- android蓝牙协议名词解释 OPP HFP HDP A2DP PAN
- 蓝牙的HFP协议笔记
- 蓝牙之十四 HFP profile
- android蓝牙开发 蓝牙设备的查找和连接
- android蓝牙开发 蓝牙设备的查找和连接
- Web前端开发精品课HTML CSS JavaScript基础教程第六章课后编程题答案
- 第二届全国智能制造(中国制造2025)创新创业大赛华南人工智能专项赛决赛圆满举办
- Scala 之构造器(六)
- php开发过程案例一
- PHP生成带有logo和底图的支付二维码
- Android蓝牙开发【六】hfp连接
- 将 Azure VM 迁移到 Azure 中的托管磁盘
- 数据分析基础(1)
- Win10下OpenCV3 开发环境配置
- Android 键盘适配-中英文适配
- 20171206 AE 开发:栅格重分类方法相关问题记录
- 在HDFS上配置alluxio
- 有道词典(版本7.5)Ctrl+C时查词设置
- 各个地方引用日期选择器的方法