zoukankan      html  css  js  c++  java
  • ZT android -- 蓝牙 bluetooth (五)接电话与听音乐

    android -- 蓝牙 bluetooth (五)接电话与听音乐

    分类: Android的原生应用分析 2165人阅读 评论(9) 收藏 举报
            前段时间似乎所有的事情都赶在一起,回家、集体出游、出差,折腾了近一个月,终于算暂时清静了,但清静只是暂时,估计马上又要出差了,所以赶紧把蓝牙这一 部分的文章了结下,按之前提到的目录,本文是关于蓝牙接打电话和听音乐的流程分析,对应蓝牙HFP/A2DP的profile,由于这部分也算是蓝牙的经 典功能,所以代码流程并不是很复杂,当然不复杂仅是对于代码调用流程而言,对于HFP/A2DP协议相关的东东还没有精力去看,其难易程序也无法评价。下 面从两个点HFP与A2DP来展开本文的代码跟踪:

            正文开始之前,先说点题外话,在android系统中蓝牙耳机和听筒两者的音频通道是不一样的,使用蓝牙耳机接听电话和听音乐不仅涉及到本文下面提到的流 程,更要牵扯的音频通道的切换,这是一个相对比较复杂的过程,android的音频系统相关内容可不算少,个人感觉多少了下解相关知识可能有助于我们更好 的蓝牙这部分功能,不过本文的主题当然还是下面两个。

          1.蓝牙耳机接听电话

            这个就对应HFP(Hands-freeProfile),Free your Hand,蓝牙的初衷之一。先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会优先触发蓝牙接听电话的代码流程,起步代码在 phonesrccomandroidphone CallScreen.java的connectBluetoothAudio() /disconnectBluetoothAudio(),只看连接部分好了,注意下面代码里的注释,
    1. /* package */ void connectBluetoothAudio() {  
    2.   if (VDBG) log("connectBluetoothAudio()...");  
    3.   if (mBluetoothHeadset != null) {  
    4.       // TODO(BT) check return  
    5.       mBluetoothHeadset.connectAudio();  
    6.   }  
    7.   // Watch out: The bluetooth connection doesn't happen instantly;  
    8.   // the connectAudio() call returns instantly but does its real  
    9.   // work in another thread.  The mBluetoothConnectionPending flag  
    10.   // is just a little trickery to ensure that the onscreen UI updates  
    11.   // instantly. (See isBluetoothAudioConnectedOrPending() above.)  
    12.   mBluetoothConnectionPending = true;  
    13.   mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();  
             接下来就跳到蓝牙应用的管辖范围,代码在packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java,
    1. public boolean connectAudio() {  
    2.     HeadsetService service = getService();  
    3.     if (service == nullreturn false;  
    4.     return service.connectAudio();  
    5. }  
            很明显下一个目标是HeadsetService,直接看具体实现,这部分代码跳转都比较清晰,下面代码会先判断当前状态是否正确,关于 HeadsetStateMachine几个状态可以参持这个/packages/apps/Bluetooth/src/com/android /bluetooth/hfp/HeadsetStateMachine.java的最前的代码注释。
    1. boolean connectAudio() {  
    2.      // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission  
    3.      enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");  
    4.      if (!mStateMachine.isConnected()) {  
    5.          return false;  
    6.      }  
    7.      if (mStateMachine.isAudioOn()) {  
    8.          return false;  
    9.      }  
    10.      mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);  
    11.      return true;  
    12.  }  
           走进HeadsetStateMachine状态机,找到CONNECT_AUDIO分支,就看带Native的方法connectAudioNative(getByteAddress(mCurrentDevice));
    1. static jboolean connectAudioNative(JNIEnv *env, jobject object, jbyteArray address) {  
    2.     jbyte *addr;  
    3.     bt_status_t status;  
    4.   
    5.     if (!sBluetoothHfpInterface) return JNI_FALSE;  
    6.   
    7.     addr = env->GetByteArrayElements(address, NULL);  
    8.     if (!addr) {  
    9.         jniThrowIOException(env, EINVAL);  
    10.         return JNI_FALSE;  
    11.     }  
    12. //连接在这里  
    13.     if ( (status = sBluetoothHfpInterface->connect_audio((bt_bdaddr_t *)addr)) !=    
    14.          BT_STATUS_SUCCESS) {  
    15.         ALOGE("Failed HF audio connection, status: %d", status);  
    16.     }  
    17.     env->ReleaseByteArrayElements(address, addr, 0);  
    18.     return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  
    19. }  
           上面代码还可以进一步跟到下面/external/bluetooth/bluedroid/btif/src/btif_hf.c,到了这里其实流程已经结束了,对于这里消息流转估计要放到以后再写了
    1. static bt_status_t connect_audio( bt_bdaddr_t *bd_addr )  
    2. {  
    3.     CHECK_BTHF_INIT();  
    4.     if (is_connected(bd_addr))  
    5.     {  
    6.         BTA_AgAudioOpen(btif_hf_cb.handle);  
    7.         /* Inform the application that the audio connection has been initiated successfully */  
    8.         btif_transfer_context(btif_in_hf_generic_evt, BTIF_HFP_CB_AUDIO_CONNECTING,  
    9.                               (char *)bd_addr, sizeof(bt_bdaddr_t), NULL);  
    10.         return BT_STATUS_SUCCESS;  
    11.     }  
    12.     return BT_STATUS_FAIL;  
    13. }  

     2.在蓝牙列表中连接蓝牙耳机

         A2dp的连接过程,在蓝牙搜索结果列表连接一个蓝牙耳机,既然是从设备列表开始,所以起步代码自然是这个了
    1. DevicePickerFragment.java (settingssrccomandroidsettingsluetooth)     3884     2013-6-26  
    2.     void onClicked() {  
    3.       int bondState = mCachedDevice.getBondState();  
    4.       if (mCachedDevice.isConnected()) {  
    5.           askDisconnect();  
    6.       } else if (bondState == BluetoothDevice.BOND_BONDED) {  
    7.           mCachedDevice.connect(true);  
    8.       } .......  
    9.   }  
    10.   
    11.     void connect(boolean connectAllProfiles) {  
    12.       if (!ensurePaired()) {  //要先确保配对  
    13.           return;  
    14.       }  
    15.       mConnectAttempted = SystemClock.elapsedRealtime();  
    16.       connectWithoutResettingTimer(connectAllProfiles);//没别的了,只能看到这里  
    17.   }  
          代码路径这里packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java,具体代码看下面
    1. // Try to initialize the profiles if they were not.  
    2.      ...........  
    3.       // Reset the only-show-one-error-dialog tracking variable  
    4.       mIsConnectingErrorPossible = true;  
    5.   
    6.       int preferredProfiles = 0;  
    7.       for (LocalBluetoothProfile profile : mProfiles) {  
    8.           if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {  
    9.               if (profile.isPreferred(mDevice)) {  
    10.                   ++preferredProfiles;  
    11.                   connectInt(profile);//连接在这里,  
    12.               }  
    13.           }  
    14.       }  
    15.      .............  
           connectInt的实现很简单,直接跳过看里面的profile.connect(mDevice),这里的profile是指A2dpProfile,所以connet()方法的具体实现在
    1. public boolean connect(BluetoothDevice device) {  
    2.     if (mService == nullreturn false;  
    3.     List<BluetoothDevice> sinks = getConnectedDevices();  
    4.     if (sinks != null) {  
    5.         for (BluetoothDevice sink : sinks) {  
    6.             mService.disconnect(sink);  
    7.     }}  
    8.     return mService.connect(device);  
    9. }  
            下面是 BluetoothA2dp.java (frameworksasecorejavaandroidluetooth)  ,为什么是这样看下这个private BluetoothA2dp mService;就知道了
    1. public boolean connect(BluetoothDevice device) {  
    2.       if (mService != null && isEnabled() &&  
    3.           isValidDevice(device)) {  
    4.           try {  
    5.               return mService.connect(device);  
    6.           } catch (RemoteException e) {  
    7.               Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));  
    8.               return false;  
    9.           }  
    10.       }...........  
    11.       return false;  
    12.        
    13.       Binder跳转  
    14.       public boolean connect(BluetoothDevice device) {  
    15.           A2dpService service = getService();  
    16.           if (service == nullreturn false;  
    17.           return service.connect(device);  
    18.       }  
    19.        
    20.   }  
            之后的跳转和第一部分蓝牙接听电话跳转过程类似,就不重复了,最后会来到packages/apps/Bluetooth/jni /com_android_bluetooth_a2dp.cpp的connectA2dpNative,同样到下面的代码,我们能看到的开放的代码也就 是这些,再下面要看vendor的具体实现了。
    1. static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {  
    2.   jbyte *addr;  
    3.   bt_bdaddr_t * btAddr;  
    4.   bt_status_t status;  
    5.   
    6.   ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);  
    7.   if (!sBluetoothA2dpInterface) return JNI_FALSE;  
    8.   
    9.   addr = env->GetByteArrayElements(address, NULL);  
    10.   btAddr = (bt_bdaddr_t *) addr;  
    11.   if (!addr) {  
    12.       jniThrowIOException(env, EINVAL);  
    13.       return JNI_FALSE;  
    14.   }  
    15.   if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {  
    16.       ALOGE("Failed HF connection, status: %d", status);  
    17.   }  
    18.   env->ReleaseByteArrayElements(address, addr, 0);  
    19.   return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;  
           那到此为止,本文关于蓝牙耳机与蓝牙接听电话的流程分析也就结束了,同时蓝牙这一系列的文章也暂时结束,当然后续依然会关注蓝牙。本系列的第一篇文章标题是入门,现在想想,这五篇文章下来也不过是刚刚入门而已,协议部分更是没怎么涉及呢,对于蓝牙BT需要深入研究的地方还有很多,仅希望这五篇文章可以帮你快速了解android蓝牙代码流程,回顾以前四篇文章请点击链接:
           最后感谢在前面文章中网友的热心回复与纠正,学习路上一起分享是快乐的。谢谢!

    更多 0
     
    查看评论
    7楼 q261525978 2014-01-08 10:02发表 [回复]
    你好,请问配对的时候,配对方式是根据什么来选择的?比如手机跟手机配对使用的是随机生成pin码,手机跟一些耳机配对要输入pin码。关于源码里面的配对,我反着追踪,到JNI里面一个函数ssp_request_callback(),一直找不到哪里调用了这个函数
    6楼 ping550 2014-01-07 16:12发表 [回复]
    你好,请教一个问题,蓝牙打电话时(蓝牙耳机接电话),语音数据在android内核层是如何传输的,我想知道在内核层的接口函数,求指教,谢谢!
    Re: balmy 2014-01-07 17:38发表 [回复]
    回复ping550:你的问题让我倍感压力啊,抱歉,目前没研究那么深,真的不知道怎么回答你,暂时帮不上你了。
    5楼 syngalon螳螳 2013-11-04 15:36发表 [回复]
    在xref 的4.2代码中, DevicePickerFragment.java跟你讲的不一致,没有onClicked方法,有onDevicePreferenceClick方法。逻辑不一样呢
    4楼 xyp5299 2013-09-11 21:51发表 [回复]
    请问,后面还会有其它的文章吗?期待中
    Re: balmy 2013-09-12 08:35发表 [回复]
    回复xyp5299:暂时是没有了,最近在折腾一些琐事,有段时间没怎么看书总结了。后续暂时还没有计划。
    3楼 xyp5299 2013-08-20 16:25发表 [回复]
    很有用,谢谢 ,期待后续更精彩的文章 。
    2楼 frogoscar 2013-07-30 16:59发表 [回复]
    期待更多关于蓝牙的好文章!很受用,谢谢
    1楼 andger032 2013-07-15 11:58发表 [回复]
    非常感谢版主对蓝牙知识的无私分享,写的质量很高,继续支持。期待后续文章。
  • 相关阅读:
    krpano--控制热点跳转到场景的指定视角
    bzoj 4237: 稻草人 -- CDQ分治
    bzoj 4176: Lucas的数论 -- 杜教筛,莫比乌斯反演
    bzoj 3545/3551: [ONTAK2010]Peaks -- 主席树,最小生成树,倍增
    bzoj 4627: [BeiJing2016]回转寿司 -- 权值线段树
    bzoj 1901: Zju2112 Dynamic Rankings -- 主席树,树状数组,哈希
    bzoj 3252: 攻略 -- 长链剖分+贪心
    bzoj 5055: 膜法师 -- 树状数组
    bzoj 1006: [HNOI2008]神奇的国度 -- 弦图(最大势算法)
    bzoj 1176: [Balkan2007]Mokia&&2683: 简单题 -- cdq分治
  • 原文地址:https://www.cnblogs.com/jeanschen/p/3528253.html
Copyright © 2011-2022 走看看