ZT android -- 蓝牙 bluetooth (五)接电话与听音乐
android -- 蓝牙 bluetooth (五)接电话与听音乐
正文开始之前,先说点题外话,在android系统中蓝牙耳机和听筒两者的音频通道是不一样的,使用蓝牙耳机接听电话和听音乐不仅涉及到本文下面提到的流
程,更要牵扯的音频通道的切换,这是一个相对比较复杂的过程,android的音频系统相关内容可不算少,个人感觉多少了下解相关知识可能有助于我们更好
的蓝牙这部分功能,不过本文的主题当然还是下面两个。
1.蓝牙耳机接听电话
your
Hand,蓝牙的初衷之一。先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会优先触发蓝牙接听电话的代码流程,起步代码在
phone\src\com\android\phone\nCallScreen.java的connectBluetoothAudio()
/disconnectBluetoothAudio(),只看连接部分好了,注意下面代码里的注释,
- /* package */ void connectBluetoothAudio() {
- if (VDBG) log("connectBluetoothAudio()...");
- if (mBluetoothHeadset != null) {
- // TODO(BT) check return
- mBluetoothHeadset.connectAudio();
- }
- // Watch out: The bluetooth connection doesn't happen instantly;
- // the connectAudio() call returns instantly but does its real
- // work in another thread. The mBluetoothConnectionPending flag
- // is just a little trickery to ensure that the onscreen UI updates
- // instantly. (See isBluetoothAudioConnectedOrPending() above.)
- mBluetoothConnectionPending = true;
- mBluetoothConnectionRequestTime = SystemClock.elapsedRealtime();
接下来就跳到蓝牙应用的管辖范围,代码在packages/apps/Bluetooth/src/com/android/bluetooth/hfp/HeadsetService.java,
- public boolean connectAudio() {
- HeadsetService service = getService();
- if (service == null) return false;
- return service.connectAudio();
- }
很明显下一个目标是HeadsetService,直接看具体实现,这部分代码跳转都比较清晰,下面代码会先判断当前状态是否正确,关于
HeadsetStateMachine几个状态可以参持这个/packages/apps/Bluetooth/src/com/android
/bluetooth/hfp/HeadsetStateMachine.java的最前的代码注释。
- boolean connectAudio() {
- // TODO(BT) BLUETOOTH or BLUETOOTH_ADMIN permission
- enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- if (!mStateMachine.isConnected()) {
- return false;
- }
- if (mStateMachine.isAudioOn()) {
- return false;
- }
- mStateMachine.sendMessage(HeadsetStateMachine.CONNECT_AUDIO);
- return true;
- }
走进HeadsetStateMachine状态机,找到CONNECT_AUDIO分支,就看带Native的方法connectAudioNative(getByteAddress(mCurrentDevice));
- static jboolean connectAudioNative(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_audio((bt_bdaddr_t *)addr)) !=
- BT_STATUS_SUCCESS) {
- ALOGE("Failed HF audio connection, status: %d", status);
- }
- env->ReleaseByteArrayElements(address, addr, 0);
- return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
- }
上面代码还可以进一步跟到下面/external/bluetooth/bluedroid/btif/src/btif_hf.c,到了这里其实流程已经结束了,对于这里消息流转估计要放到以后再写了
- static bt_status_t connect_audio( bt_bdaddr_t *bd_addr )
- {
- CHECK_BTHF_INIT();
- if (is_connected(bd_addr))
- {
- BTA_AgAudioOpen(btif_hf_cb.handle);
- /* Inform the application that the audio connection has been initiated successfully */
- btif_transfer_context(btif_in_hf_generic_evt, BTIF_HFP_CB_AUDIO_CONNECTING,
- (char *)bd_addr, sizeof(bt_bdaddr_t), NULL);
- return BT_STATUS_SUCCESS;
- }
- return BT_STATUS_FAIL;
- }
2.在蓝牙列表中连接蓝牙耳机
- DevicePickerFragment.java (settings\src\com\android\settings\bluetooth) 3884 2013-6-26
- void onClicked() {
- int bondState = mCachedDevice.getBondState();
- if (mCachedDevice.isConnected()) {
- askDisconnect();
- } else if (bondState == BluetoothDevice.BOND_BONDED) {
- mCachedDevice.connect(true);
- } .......
- }
- void connect(boolean connectAllProfiles) {
- if (!ensurePaired()) { //要先确保配对
- return;
- }
- mConnectAttempted = SystemClock.elapsedRealtime();
- connectWithoutResettingTimer(connectAllProfiles);//没别的了,只能看到这里
- }
代码路径这里packages/apps/Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java,具体代码看下面
- // Try to initialize the profiles if they were not.
- ...........
- // Reset the only-show-one-error-dialog tracking variable
- mIsConnectingErrorPossible = true;
- int preferredProfiles = 0;
- for (LocalBluetoothProfile profile : mProfiles) {
- if (connectAllProfiles ? profile.isConnectable() : profile.isAutoConnectable()) {
- if (profile.isPreferred(mDevice)) {
- ++preferredProfiles;
- connectInt(profile);//连接在这里,
- }
- }
- }
- .............
connectInt的实现很简单,直接跳过看里面的profile.connect(mDevice),这里的profile是指A2dpProfile,所以connet()方法的具体实现在
- public boolean connect(BluetoothDevice device) {
- if (mService == null) return false;
- List<BluetoothDevice> sinks = getConnectedDevices();
- if (sinks != null) {
- for (BluetoothDevice sink : sinks) {
- mService.disconnect(sink);
- }}
- return mService.connect(device);
- }
下面是 BluetoothA2dp.java (frameworks\base\core\java\android\bluetooth) ,为什么是这样看下这个private BluetoothA2dp mService;就知道了
- public boolean connect(BluetoothDevice device) {
- if (mService != null && isEnabled() &&
- isValidDevice(device)) {
- try {
- return mService.connect(device);
- } catch (RemoteException e) {
- Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
- return false;
- }
- }...........
- return false;
- Binder跳转
- public boolean connect(BluetoothDevice device) {
- A2dpService service = getService();
- if (service == null) return false;
- return service.connect(device);
- }
- }
之后的跳转和第一部分蓝牙接听电话跳转过程类似,就不重复了,最后会来到packages/apps/Bluetooth/jni
/com_android_bluetooth_a2dp.cpp的connectA2dpNative,同样到下面的代码,我们能看到的开放的代码也就
是这些,再下面要看vendor的具体实现了。
- static jboolean connectA2dpNative(JNIEnv *env, jobject object, jbyteArray address) {
- jbyte *addr;
- bt_bdaddr_t * btAddr;
- bt_status_t status;
- ALOGI("%s: sBluetoothA2dpInterface: %p", __FUNCTION__, sBluetoothA2dpInterface);
- if (!sBluetoothA2dpInterface) return JNI_FALSE;
- addr = env->GetByteArrayElements(address, NULL);
- btAddr = (bt_bdaddr_t *) addr;
- if (!addr) {
- jniThrowIOException(env, EINVAL);
- return JNI_FALSE;
- }
- if ((status = sBluetoothA2dpInterface->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;
那到此为止,本文关于蓝牙耳机与蓝牙接听电话的流程分析也就结束了,同时蓝牙这一系列的文章也暂时结束,当然后续依然会关注蓝牙。本系列的第一篇文章标题是入门,现在想想,这五篇文章下来也不过是刚刚入门而已,协议部分更是没怎么涉及呢,对于蓝牙BT需要深入研究的地方还有很多,仅希望这五篇文章可以帮你快速了解android蓝牙代码流程,回顾以前四篇文章请点击链接:
更多
0
- 7楼 q261525978 2014-01-08 10:02发表 [回复]

- 你好,请问配对的时候,配对方式是根据什么来选择的?比如手机跟手机配对使用的是随机生成pin码,手机跟一些耳机配对要输入pin码。关于源码里面的配对,我反着追踪,到JNI里面一个函数ssp_request_callback(),一直找不到哪里调用了这个函数
- 6楼 ping550 2014-01-07 16:12发表 [回复]

- 你好,请教一个问题,蓝牙打电话时(蓝牙耳机接电话),语音数据在android内核层是如何传输的,我想知道在内核层的接口函数,求指教,谢谢!
- 5楼 syngalon螳螳 2013-11-04 15:36发表 [回复]

- 在xref 的4.2代码中, DevicePickerFragment.java跟你讲的不一致,没有onClicked方法,有onDevicePreferenceClick方法。逻辑不一样呢
ZT android -- 蓝牙 bluetooth (五)接电话与听音乐的更多相关文章
- ZT android -- 蓝牙 bluetooth (四)OPP文件传输
android -- 蓝牙 bluetooth (四)OPP文件传输 分类: Android的原生应用分析 2013-06-22 21:51 2599人阅读 评论(19) 收藏 举报 4.2源码AND ...
- ZT android -- 蓝牙 bluetooth (一) 入门
android -- 蓝牙 bluetooth (一) 入门 分类: Android的原生应用分析 2013-05-19 21:44 4543人阅读 评论(37) 收藏 举报 bluetooth4.2 ...
- ZT android -- 蓝牙 bluetooth (三)搜索蓝牙
android -- 蓝牙 bluetooth (三)搜索蓝牙 分类: Android的原生应用分析 2013-05-31 22:03 2192人阅读 评论(8) 收藏 举报 bluetooth蓝牙s ...
- ZT android -- 蓝牙 bluetooth (二) 打开蓝牙
android -- 蓝牙 bluetooth (二) 打开蓝牙 分类: Android的原生应用分析 2013-05-23 23:57 4773人阅读 评论(20) 收藏 举报 androidblu ...
- android -- 蓝牙 bluetooth (四)OPP文件传输
在前面android -- 蓝牙 bluetooth (一) 入门文章结尾中提到了会按四个方面来写这系列的文章,前面已写了蓝牙打开和蓝牙搜索,这次一起来看下蓝牙文件分享的流程,也就是蓝牙应用opp目录 ...
- android -- 蓝牙 bluetooth (三)搜索蓝牙
接上篇打开蓝牙继续,来一起看下蓝牙搜索的流程,触发蓝牙搜索的条件形式上有两种,一是在蓝牙设置界面开启蓝牙会直接开始搜索,另一个是先打开蓝牙开关在进入蓝牙设置界面也会触发搜索,也可能还有其它触发方式,但 ...
- 深入了解Android蓝牙Bluetooth——《进阶篇》
在 [深入了解Android蓝牙Bluetooth--<基础篇>](http://blog.csdn.net/androidstarjack/article/details/6046846 ...
- 深入了解Android蓝牙Bluetooth ——《总结篇》
在我的上两篇博文中解说了有关android蓝牙的认识以及API的相关的介绍,蓝牙BLE的搜索,连接以及读取. 没有了解的童鞋们请參考: 深入了解Android蓝牙Bluetooth--<基础篇& ...
- android -- 蓝牙 bluetooth (五)接电话与听音乐
1.蓝牙耳机接听电话 这个就对应HFP(Hands-freeProfile),Free your Hand,蓝牙的初衷之一.先来看这个功能的场景,手机来电,手机与蓝牙耳机已连接,这时会 ...
随机推荐
- Unicode和UTF-8之间的转换
转自:http://www.cnblogs.com/xdotnet/archive/2007/11/23/unicode_and_utf8.html#undefined 最近在用VC++开发一个小工具 ...
- tar.gz和bin,以及rpm,deb等linux后缀的文件的区别
先说tar.gz源码类的吧.这种要你手动安装,编译,首先你还要安装了相关的编译软件如gcc g++ 等.一般操作 configure , make && make install. 其 ...
- 王亮:游戏AI探索之旅——从alphago到moba游戏
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云加社区技术沙龙 发表于云+社区专栏 演讲嘉宾:王亮,腾讯AI高级研究员.2013年加入腾讯,从事大数据预测以及游戏AI研发工作.目前 ...
- vue 实战问题-watch 数组或者对象
1.普通的watch data() { return { frontPoints: 0 } }, watch: { frontPoints(newValue, oldValue) { console. ...
- ES6 读书笔记
一.let和const命令 二.变量的解构赋值 三.字符串的扩展 四.数值的扩展 五.正则的扩展 六.数组的扩展 七.函数的扩展 八.对象的扩展 九.symbol 十.proxy和reflect 十一 ...
- C语言经典面试题目(转的,不过写的的确好!)
第一部分:基本概念及其它问答题 1.关键字static的作用是什么? 这个简单的问题很少有人能回答完全.在C语言中,关键字static有三个明显的作用: 1). 在函数体,一个被声明为静态的变量在这一 ...
- css的四种书写方式
优先级: 外部样式 < 内部样式表 < 内联样式表: 优先级,即:同名的选择器右边的会覆盖左边 1.内部样式表 <head> <style> /*内部样式表,一般用 ...
- 读取excel的方法(可用于批量导入)
FileStream stream = File.Open(filePath, FileMode.Open, FileAccess.Read); //1. Reading from a binary ...
- [LeetCode]Find Pivot
Find Pivot: Given an array of integers nums, write a method that returns the "pivot" index ...
- eclipse的debug技巧之一
如下图,断点设置的地方会在i==0时就停住,假如我们想在i等于某个值的时候才停住,那么可以添加“过滤条件” 具体操作是在debug模式下,右键breakpoints标签下我们所设置的断点,点击右键菜单 ...