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

分类: Android的原生应用分析 2013-07-13 20:53 2165人阅读 评论(9) 收藏 举报
        前段时间似乎所有的事情都赶在一起,回家、集体出游、出差,折腾了近一个月,终于算暂时清静了,但清静只是暂时,估计马上又要出差了,所以赶紧把蓝牙这一 部分的文章了结下,按之前提到的目录,本文是关于蓝牙接打电话和听音乐的流程分析,对应蓝牙HFP/A2DP的profile,由于这部分也算是蓝牙的经 典功能,所以代码流程并不是很复杂,当然不复杂仅是对于代码调用流程而言,对于HFP/A2DP协议相关的东东还没有精力去看,其难易程序也无法评价。下 面从两个点HFP与A2DP来展开本文的代码跟踪:
       
正文开始之前,先说点题外话,在android系统中蓝牙耳机和听筒两者的音频通道是不一样的,使用蓝牙耳机接听电话和听音乐不仅涉及到本文下面提到的流
程,更要牵扯的音频通道的切换,这是一个相对比较复杂的过程,android的音频系统相关内容可不算少,个人感觉多少了下解相关知识可能有助于我们更好
的蓝牙这部分功能,不过本文的主题当然还是下面两个。

1.蓝牙耳机接听电话

        这个就对应HFP(Hands-freeProfile),Free
your
Hand,蓝牙的初衷之一。先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会优先触发蓝牙接听电话的代码流程,起步代码在
phone\src\com\android\phone\nCallScreen.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 == null) return 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. if (!sBluetoothHfpInterface) return JNI_FALSE;
  5. addr = env->GetByteArrayElements(address, NULL);
  6. if (!addr) {
  7. jniThrowIOException(env, EINVAL);
  8. return JNI_FALSE;
  9. }
  10. //连接在这里
  11. if ( (status = sBluetoothHfpInterface->connect_audio((bt_bdaddr_t *)addr)) !=
  12. BT_STATUS_SUCCESS) {
  13. ALOGE("Failed HF audio connection, status: %d", status);
  14. }
  15. env->ReleaseByteArrayElements(address, addr, 0);
  16. return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
  17. }

上面代码还可以进一步跟到下面/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 (settings\src\com\android\settings\bluetooth)     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. void connect(boolean connectAllProfiles) {
  11. if (!ensurePaired()) {  //要先确保配对
  12. return;
  13. }
  14. mConnectAttempted = SystemClock.elapsedRealtime();
  15. connectWithoutResettingTimer(connectAllProfiles);//没别的了,只能看到这里
  16. }

代码路径这里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. 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. .............

connectInt的实现很简单,直接跳过看里面的profile.connect(mDevice),这里的profile是指A2dpProfile,所以connet()方法的具体实现在

  1. public boolean connect(BluetoothDevice device) {
  2. if (mService == null) return 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 (frameworks\base\core\java\android\bluetooth)  ,为什么是这样看下这个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. Binder跳转
  13. public boolean connect(BluetoothDevice device) {
  14. A2dpService service = getService();
  15. if (service == null) return false;
  16. return service.connect(device);
  17. }
  18. }

之后的跳转和第一部分蓝牙接听电话跳转过程类似,就不重复了,最后会来到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. ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);
  6. if (!sBluetoothA2dpInterface) return JNI_FALSE;
  7. addr = env->GetByteArrayElements(address, NULL);
  8. btAddr = (bt_bdaddr_t *) addr;
  9. if (!addr) {
  10. jniThrowIOException(env, EINVAL);
  11. return JNI_FALSE;
  12. }
  13. if ((status = sBluetoothA2dpInterface->connect((bt_bdaddr_t *)addr)) != BT_STATUS_SUCCESS) {
  14. ALOGE("Failed HF connection, status: %d", status);
  15. }
  16. env->ReleaseByteArrayElements(address, addr, 0);
  17. 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发表 [回复]
非常感谢版主对蓝牙知识的无私分享,写的质量很高,继续支持。期待后续文章。

ZT android -- 蓝牙 bluetooth (五)接电话与听音乐的更多相关文章

  1. ZT android -- 蓝牙 bluetooth (四)OPP文件传输

    android -- 蓝牙 bluetooth (四)OPP文件传输 分类: Android的原生应用分析 2013-06-22 21:51 2599人阅读 评论(19) 收藏 举报 4.2源码AND ...

  2. ZT android -- 蓝牙 bluetooth (一) 入门

    android -- 蓝牙 bluetooth (一) 入门 分类: Android的原生应用分析 2013-05-19 21:44 4543人阅读 评论(37) 收藏 举报 bluetooth4.2 ...

  3. ZT android -- 蓝牙 bluetooth (三)搜索蓝牙

    android -- 蓝牙 bluetooth (三)搜索蓝牙 分类: Android的原生应用分析 2013-05-31 22:03 2192人阅读 评论(8) 收藏 举报 bluetooth蓝牙s ...

  4. ZT android -- 蓝牙 bluetooth (二) 打开蓝牙

    android -- 蓝牙 bluetooth (二) 打开蓝牙 分类: Android的原生应用分析 2013-05-23 23:57 4773人阅读 评论(20) 收藏 举报 androidblu ...

  5. android -- 蓝牙 bluetooth (四)OPP文件传输

    在前面android -- 蓝牙 bluetooth (一) 入门文章结尾中提到了会按四个方面来写这系列的文章,前面已写了蓝牙打开和蓝牙搜索,这次一起来看下蓝牙文件分享的流程,也就是蓝牙应用opp目录 ...

  6. android -- 蓝牙 bluetooth (三)搜索蓝牙

    接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但 ...

  7. 深入了解Android蓝牙Bluetooth——《进阶篇》

    在 [深入了解Android蓝牙Bluetooth--<基础篇>](http://blog.csdn.net/androidstarjack/article/details/6046846 ...

  8. 深入了解Android蓝牙Bluetooth ——《总结篇》

    在我的上两篇博文中解说了有关android蓝牙的认识以及API的相关的介绍,蓝牙BLE的搜索,连接以及读取. 没有了解的童鞋们请參考: 深入了解Android蓝牙Bluetooth--<基础篇& ...

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

    1.蓝牙耳机接听电话         这个就对应HFP(Hands-freeProfile),Free your Hand,蓝牙的初衷之一.先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会 ...

随机推荐

  1. Prinzipien der Computer Zusammensetzung

    1.Die Einfuerung der Computer System 1.1 Computer Zusammensetzung und Computer Architektur Unter Com ...

  2. spring cloud连载第二篇之Spring Cloud Config

    Spring Cloud Config Spring Cloud Config为分布式服务提供了服务侧和客户侧的外部配置支持.通过Spring Cloud Config你可以有一个统一的地方来管理所有 ...

  3. echart.js 参数解释

    Data参数 获取容器对象 var canvas = document.getElementById("myCanvas"); 渲染 var ctx = canvas.getCon ...

  4. 如何把连接字符串放到App.cfg配置文件中

    首先主Windows程序要添加一个[应用程序配置文件]会在项目中生成一个App.config文件. 在项目中也引用System.Configuration这个引用 当前文档中没有源. 在后台数据类库中 ...

  5. API ,批量添加

    添加引用:cors using   system.web.http.cors API添加这句话:       [EnableCors("*", "*", &qu ...

  6. 如何在 Azure 中创建 ASP.NET Web 应用

    Azure Web 应用提供高度可缩放.自修补的 Web 托管服务. 本快速入门演示如何将第一个 ASP.NET Web 应用部署到 Azure Web 应用中. 完成后,便拥有了一个资源组,该资源组 ...

  7. 六、yarn运行模式

    简介 spark的yarn运行模式根据Driver在集群中的位置分成两种: 1)yarn-client 客户端模式 2)yarn-cluster 集群模式 yarn模式和standalone模式不同, ...

  8. 撩课-Web大前端每天5道面试题-Day7

    1. 你能描述一下渐进增强和优雅降级之间的不同吗? 定义: 优雅降级(graceful degradation): 一开始就构建站点的完整功能, 然后针对浏览器测试和修复 渐进增强(progressi ...

  9. oracle 多列数据相同,部分列数据不同合并不相同列数据

    出现这样一种情况: 前面列数据一致,最后remark数据不同,将remark合并成 解决办法: 最后一列:结果详情: 使用到的语句为: select a,b,c,wm_concat(d) d,wm_c ...

  10. JDBC处理事务

    一.什么是事务? 在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务! 二.事务是必须满足4个条件(AC ...