zoukankan      html  css  js  c++  java
  • ZT 4.3 android bluetooth hfp分析

    4.3 android bluetooth hfp分析

    592人阅读 评论(3) 收藏 举报

    所有程序执行的代码都是有入口的,在这里我们暂时分析一种情景,蓝牙打开着,蓝牙耳机连接。

    在设置界面点击蓝牙耳机操作:

    packages/apps/Settings/src/com/android/settings/bluetooth/BluetoothDevicePreference.java

    1. void onClicked() {  
    2.     int bondState = mCachedDevice.getBondState();  
    3.   
    4.     if (mCachedDevice.isConnected()) {  
    5.         askDisconnect();  
    6.     } else if (bondState == BluetoothDevice.BOND_BONDED) { //已经配对,但是未连接  
    7.         <span style="color:#ff0000;">mCachedDevice.connect(true);</span>  
    8.     } else if (bondState == BluetoothDevice.BOND_NONE) { //没有配对  
    9.         <span style="color:#ff0000;">pair();</span>  
    10.     }  
    11. }  

    mCachedDevice.connect(true);方法会直接调用CachedBluetoothDevice.java的connect的方法。

    packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java

    1.    void connect(boolean connectAllProfiles) {  
    2.         if (!ensurePaired()) {  //配对处理暂时不关注  
    3.             return;  
    4.         }  
    5.         mConnectAttempted = SystemClock.elapsedRealtime();  
    6.         connectWithoutResettingTimer(connectAllProfiles);  
    7.     }  

    代码执行到connectWithoutResettingTimer,注释就不粘贴了。 

    1.     private void connectWithoutResettingTimer(boolean connectAllProfiles) {  
    2.          ......  
    3.         // Reset the only-show-one-error-dialog tracking variable  
    4.         mIsConnectingErrorPossible = true;  
    5.         int preferredProfiles = 0;  
    6.         for (LocalBluetoothProfile profile : mProfiles) {  
    7.             if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {  
    8.                 if (profile.isPreferred(mDevice)) {  
    9.                     ++preferredProfiles;  
    10.                     connectInt(profile);  
    11.                 }  
    12.             }  
    13.         }  
    14.         if (DEBUG) Log.d(TAG, "Preferred profiles = " + preferredProfiles);  
    15.   
    16.         if (preferredProfiles == 0) {  
    17.             connectAutoConnectableProfiles();  
    18.         }  
    19.     }  

    不同的协议实现类,继承于LocalBluetoothProfile,以HeadsetProfile为例。

    1. synchronized void connectInt(LocalBluetoothProfile profile) {  
    2.     if (!ensurePaired()) {  
    3.         return;  
    4.     }  
    5.     if (<span style="color:#ff0000;">profile.connect(mDevice)</span>) {  
    6.         if (Utils.D) {  
    7.             Log.d(TAG, "Command sent successfully:CONNECT " + describe(profile));  
    8.         }  
    9.         return;  
    10.     }  
    11.     Log.i(TAG, "Failed to connect " + profile.toString() + " to " + mName);  
    12. }  

    这个方法中,profile.connnect(),直接调用 HeadsetProfile的connnect(),其实headset/handsfree 是共用同一个service。

    packages/apps/Settings/src/com/android/settings/bluetooth/HeadsetProfile.java

    1. public boolean connect(BluetoothDevice device) {  
    2.     if (mService == nullreturn false;  
    3.     List<BluetoothDevice> sinks = mService.getConnectedDevices();  
    4.     if (sinks != null) {//断开所有连接  
    5.         for (BluetoothDevice sink : sinks) {  
    6.             mService.disconnect(sink);  
    7.         }  
    8.     }  
    9.     return mService.connect(device);  
    10. }  


    mService.connect 直接进入framwork层,调用Bluetooth Headset server的api。

    frameworks/base/core/java/android/bluetooth/BluetoothHeadset.java

    1. public boolean connect(BluetoothDevice device) {  
    2.     if (DBG) log("connect(" + device + ")");  
    3.     if (mService != null && isEnabled() &&  
    4.         isValidDevice(device)) {  
    5.         try {  
    6.             return mService.connect(device);  
    7.         } catch (RemoteException e) {  
    8.             Log.e(TAG, Log.getStackTraceString(new Throwable()));  
    9.             return false;  
    10.         }  
    11.     }  
    12.     if (mService == null) Log.w(TAG, "Proxy not attached to service");  
    13.     return false;  
    14. }  

    framework 中转了一下,利用Binder机制再次进入Bluetooth模块。

    frameworks/base/core/java/android/bluetooth/IBluetoothHeadset.aidl,实现类是

    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java
    注意顺序:Setting->framewrok->Bluetooth

    1. public boolean connect(BluetoothDevice device) {  
    2.     HeadsetService service = getService();  
    3.     if (service == nullreturn false;  
    4.     return service.connect(device);  
    5. }  


    IBluetoothHeadset.Sub仅仅是一个连接,什么都不做直接进入HeadsetService.java的connect 方法。

    1.     public boolean connect(BluetoothDevice device) {         
    2.         enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,  
    3.                                        "Need BLUETOOTH ADMIN permission");  
    4.   
    5.         if (getPriority(device) == BluetoothProfile.PRIORITY_OFF) {  
    6.             return false;  
    7.         }  
    8.         int connectionState = mStateMachine.getConnectionState(device);  
    9.         if (connectionState == BluetoothProfile.STATE_CONNECTED ||  
    10.             connectionState == BluetoothProfile.STATE_CONNECTING) {  
    11.             return false;  
    12.         }  
    13.         mStateMachine.sendMessage(HeadsetStateMachine.CONNECT, device);//万恶的状态机开始工作  
    14.         return true;  
    15.     }  

    很讨厌android状态机,感觉把java搞成了非面向对象,进入状态机代码实现:

    packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetStateMachine.java

    记住当前为 未连接状态,所以应该进入 Disconnected的状态,Disconnected的processMessage方法中:

    1. switch(message.what) {  
    2.     case CONNECT:  
    3.         BluetoothDevice device = (BluetoothDevice) message.obj;  
    4.         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,  
    5.                        BluetoothProfile.STATE_DISCONNECTED);  
    6.   
    7.         if (!<span style="color:#ff0000;">connectHfpNative(getByteAddress(device)) </span>) {  
    8.             broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,  
    9.                            BluetoothProfile.STATE_CONNECTING);  
    10.             break;  
    11.         }  
    12.   
    13.         synchronized (HeadsetStateMachine.this) {  
    14.             mTargetDevice = device;  
    15.             transitionTo(mPending);  
    16.         }  


    connectHfpNative 连接,jni 方法 简单了。

    packagesappsluetoothjniCom_android_bluetooth_hfp.cpp

    1. {"connectHfpNative""([B)Z", (void *) connectHfpNative},  
    1. static jboolean connectHfpNative(JNIEnv *env, jobject object, jbyteArray address) {  
    2. jbyte *addr;  
    3. bt_status_t status;  
    4.   
    5. ALOGI("%s: sBluetoothHfpInterface: %p", __FUNCTION__, sBluetoothHfpInterface);  
    6. if (!sBluetoothHfpInterface) return JNI_FALSE;  
    7.   
    8. addr = env->GetByteArrayElements(address, NULL);  
    9. if (!addr) {  
    10.     jniThrowIOException(env, EINVAL);  
    11.     return JNI_FALSE;  
    12. }  
    13.   
    14. <span style="color:#ff0000;">if ((status = sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {</span>  
    15.     ALOGE("Failed HF connection, status: %d", status);  
    16. }  
    17. env->ReleaseByteArrayElements(address, addr, 0);  
    18. return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  


    关键函数是sBluetoothHfpInterface->connect((bt_bdaddr_t *)addr), connect定义在 hardware/libhardware/include/hardware/bt_hf.h,

    但是实现却在:external/bluetooth/bluedroid/btif/src/btif_hf.c 中实现

    1. #include <hardware/bt_hf.h>  
    2. static bt_status_t connect( bt_bdaddr_t *bd_addr )  
    3. {  
    4.     CHECK_BTHF_INIT();  
    5.     return btif_queue_connect(UUID_SERVCLASS_AG_HANDSFREE, bd_addr, connect_int);  
    6. }  

    看定义,直接连接handsfree

    #define UUID_SERVCLASS_AG_HANDSFREE             0X111F  /* Handsfree profile */

    btif_queue_connect的定义在下面文件中定义

    external/bluetooth/bluedroid/btif/src/btif_profile_queue.c

    进入bt_status_t btif_queue_connect看定义:

    1.   
    1. bt_status_t btif_queue_connect(uint16_t uuidconst bt_bdaddr_t *bda,  
    2.                         btif_connect_cb_t *connect_cb)  
    3. {  
    4.     connect_node_t node;  
    5.     memset(&node, 0, sizeof(connect_node_t));  
    6.     memcpy(&(node.bda), bda, sizeof(bt_bdaddr_t));  
    7.     node.uuid = uuid;  
    8.     node.p_cb = connect_cb;  
    9.   
    10.     return btif_transfer_context(queue_int_handle_evt, BTIF_QUEUE_CONNECT_EVT,  
    11.                           (char*)&node, sizeof(connect_node_t), NULL);  
    12. }  

    btif_transfer_context定义在如下文件中

    external/bluetooth/bluedroid/btif/src/btif_core.c

    btif_transfer_context内容就不粘贴了,里面发送消息,调用

    external/bluetooth/bluedroid/gki/common/gki_buffer.c

    1. void GKI_send_msg (UINT8 task_id, UINT8 mbox, void *msg)  

    GKI_send_msg发送一条GKI信息到BTA,GKI_send_msg有三个参数,第一个参数是线程id,也作为task id, 通过bta_sys_init获得,第二个参数是mailbox id,第三个是上一步封装好的p_msg

    GKI_send_msg 首先对p_msg进一步封装成event,通过链表存到mailbox id对应的任务队列中,调用external/bluetooth/bluedroid/gki/ulinux/gki_ulinux.c :: GKI_send_event
    进行发送。
    GKI_send_event设置事件掩码
    gki_cb.com.OSWaitEvt[task_id] |= event;, 
    通过pthread_cond_signal(&gki_cb.os.thread_evt_cond[task_id]);通知另外线程。
    这样,在另外一个等待线程函数中gki_ulinux.c :: GKI_wait可以返回了。

  • 相关阅读:
    日常排雷:redis报错 could not get a resource from the pool
    阿里云centos服务器tomcat启动后,浏览器请求无响应
    并发生产顺序单据号测试
    json 数据 格式,请求接口,部分字段无法注入
    baomidou 动态数据源@DS 使用问题
    SpringMVC框架深入(八)--SpringMVC原理
    Spring框架深入(七)--json数据交互
    框架理论深入(六)--拦截器
    Spring框架深入(五)--文件上传和异常处理
    int和Integer的区别
  • 原文地址:https://www.cnblogs.com/jeanschen/p/3550655.html
Copyright © 2011-2022 走看看