首页
Web开发
Windows程序
编程语言
数据库
移动开发
系统相关
微信
其他好文
会员
首页
>
移动开发
> 详细
android -- 蓝牙 bluetooth (五)接电话与听音乐
时间:
2015-05-30 12:07:26
阅读:
175
评论:
0
收藏:
0
[点我收藏+]
标签:
前段时间似乎所有的事情都赶在一起,回家、集体出游、出差,折腾了近一个月,终于算暂时清静了,但清静只是暂时,估计马上又要出差了,所以赶紧把蓝牙这一部分的文章了结下,按之前提到的目录,本文是关于蓝牙接打电话和听音乐的流程分析,对应蓝牙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(),只看连接部分好了,注意下面代码里的注释,
[java]
view plain
copy
/* 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,
[java]
view plain
copy
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的最前的代码注释。
[java]
view plain
copy
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));
[java]
view plain
copy
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,到了这里其实流程已经结束了,对于这里消息流转估计要放到以后再写了
[java]
view plain
copy
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.在蓝牙列表中连接蓝牙耳机
A2dp的连接过程,在蓝牙搜索结果列表连接一个蓝牙耳机,既然是从设备列表开始,所以起步代码自然是这个了
[java]
view plain
copy
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,具体代码看下面
[java]
view plain
copy
// 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()方法的具体实现在
[java]
view plain
copy
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;就知道了
[java]
view plain
copy
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的具体实现了。
[java]
view plain
copy
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蓝牙代码流程,回顾以前四篇文章请点击链接:
android -- 蓝牙 bluetooth (一) 入门
android -- 蓝牙 bluetooth (二) 打开蓝牙
android -- 蓝牙 bluetooth (三)搜索蓝牙
android -- 蓝牙 bluetooth (四)OPP文件传输
最后感谢在前面文章中网友的热心回复与纠正,学习路上一起分享是快乐的。谢谢!
android -- 蓝牙 bluetooth (五)接电话与听音乐
标签:
原文地址:http://www.cnblogs.com/Free-Thinker/p/4539978.html
踩
(
0
)
赞
(
0
)
举报
评论
一句话评论(
0
)
登录后才能评论!
分享档案
更多>
2021年07月29日 (22)
2021年07月28日 (40)
2021年07月27日 (32)
2021年07月26日 (79)
2021年07月23日 (29)
2021年07月22日 (30)
2021年07月21日 (42)
2021年07月20日 (16)
2021年07月19日 (90)
2021年07月16日 (35)
周排行
更多
关闭苹果IOS app自动更新
2021-07-29
开发一个即时通讯App
2021-07-28
iOS 跳转App Store进行评分
2021-07-26
诺基亚短信生成!太好玩了
2021-07-26
【Azure 应用服务】App Service 配置 Application Settings 访问Storage Account得到 could not be resolved: '*.file.core.windows.net'的报错。没有解析成对应中国区 Storage Account地址 *.file.core.chinacloudapi.cn
2021-07-26
Android系统编程入门系列之界面Activity响应丝滑的传统动画
2021-07-26
uniapp h5,app两端复制文本
2021-07-22
uni-app滚动视图容器(scroll-view)之监听上拉事件
2021-07-21
新型横向移动工具原理分析、代码分析、优缺点以及检测方案
2021-07-19
Android系统编程入门系列之界面Activity交互响应
2021-07-19
友情链接
兰亭集智
国之画
百度统计
站长统计
阿里云
chrome插件
新版天听网
关于我们
-
联系我们
-
留言反馈
© 2014
mamicode.com
版权所有 联系我们:gaon5@hotmail.com
迷上了代码!