转载请注明出处:http://blog.csdn.net/vnanyesheshou/article/details/71429860

本文已授权微信公众号 fanfan程序媛 独家发布 扫一扫文章底部的二维码或在微信搜索 fanfan程序媛 即可关注

继续研究hfp相关功能。蓝牙耳机可以控制手机接听、拒接、挂断电话,拨打电话等功能。本文主要分析下起这些操作的大致流程。
在系统应用Bluetooth中com_android_bluetooth.cpp提供了多个回调方法,由hardware、协议栈回调过来。蓝牙耳机的一些控制命令都会发到这里。
本文基于Android4.3源码。


1 接通电话

蓝牙耳机控制手机接通电话,回掉com_android_bluetooth.cpp中的answer_call_callback()函数,该函数主要操作是调用HeadsetStateMachine的onAnswerCall()函数,代码如下:


在onAnswerCall()中发送消息(消息类型STACK_EVENT,StackEvent事件类型EVENT_TYPE_ANSWER_CALL)向状体机,此时通话尚未接通,audio没有连接,所以此时处于Connected状态。状态机收到该消息后调用processAnswerCall()函数。processAnswerCall()代码如下:

private void processAnswerCall() {if (mPhoneProxy != null) {try {//mPhoneProxy是通过bindservice 获取的。mPhoneProxy.answerCall();} catch (RemoteException e) {}} else {}
}

初始化的时候会bind service,绑定的该service为系统应用Phone下的BluetoothPhoneService(AndroidManifest中该service的action为android.bluetooth.IBluetoothHeadsetPhone),代码如下:

//参数为android.bluetooth.IBluetoothHeadsetPhone
Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());
//resolveSystemService该方法是hide的,由系统使用的特殊功能来解决系统应用程序的服务意图。
intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));
if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");
}

绑定service成功回调mConnection,在其成功回调中设置的mPhoneProxy。通过mPhoneProxy来调用service中提供的接口。mPhoneProxy.answerCall()跳到BluetoothPhoneService中answerCall。

public boolean answerCall() {//申请权限,修改电话状态enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);return PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
}

PhoneUtils调用answerCall,在这里面去接通电话。answerCall()就不具体分析了。


2 拒接、挂断电话

蓝牙耳机控制手机拒接、挂断电话,回掉com_android_bluetooth.cpp中的hangup_call_callback()函数,该函数主要操作是调用HeadsetStateMachine的onHangupCall()函数,代码如下:

private void onHangupCall() {StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);sendMessage(STACK_EVENT, event);
}

此时HeadsetStateMachine可能处于Conneted或AudioOn状态,这两种状态收到该消息的处理一样,都是调用processHangupCall(),代码如下:

private void processHangupCall() {if (isVirtualCallInProgress()) {//对于虚拟电话,结束。terminateScoUsingVirtualVoiceCall();} else {if (mPhoneProxy != null) {try { //挂断电话mPhoneProxy.hangupCall();} catch (RemoteException e) {}} else {}}
}

对于虚拟电话则直接将其结束。真实的通话跳到BluetoothPhoneService的hangupCall。

public boolean hangupCall() {enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);if (mCM.hasActiveFgCall()) { //挂断正在进行的通话return PhoneUtils.hangupActiveCall(mCM.getActiveFgCall());} else if (mCM.hasActiveRingingCall()) { //停止正在响铃的电话return PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());} else if (mCM.hasActiveBgCall()) { //挂断保持的电话return PhoneUtils.hangupHoldingCall(mCM.getFirstActiveBgCall());}return false;
}

hangupCall中会根据状态处理通话,优先处理正在进行的通话、其次是尚未接通的电话、最后是保持的电话。


3 更改通话音量

蓝牙耳机更改通话的音量,回掉com_android_bluetooth.cpp中的volume_control_callback()函数,该函数主要操作是调用HeadsetStateMachine的onVolumeChnaged()函数,代码如下:

private void onVolumeChanged(int type, int volume) {StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);event.valueInt = type;event.valueInt2 = volume;sendMessage(STACK_EVENT, event);
} 

此时HeadsetStateMachine可能处于Conneted或AudioOn状态,这两种状态收到该消息的处理一样,都是调用processVolumeEvent,代码如下:

private void processVolumeEvent(int volumeType, int volume) {if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {mPhoneState.setSpeakerVolume(volume);//是否在ui上显示int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;//设置SCO通道声音大小。mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);} else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {// 只是存了下该volume值,并没有设置mic。mPhoneState.setMicVolume(volume);} else {}
}

更改音量两种类型,VOLUME_TYPE_MIC类型,保存了下该值,并没有看到具体用该值的地方。对于VOLUME_TYPE_SPK类型的,会设置SCO声音大小。如果此时处于AudioOn状态,则会在UI上显示。


4 拨打电话

蓝牙耳机进行拨打电话,回掉com_android_bluetooth.cpp中的dial_call_callback函数,该函数主要操作是调用HeadsetStateMachine的onDialCall()函数,代码如下:

private void onDialCall(String number) {StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);event.valueString = number;sendMessage(STACK_EVENT, event);
}

此时HeadsetStateMachine可能处于Conneted或AudioOn状态,这两种状态收到该消息的处理一样,都是调用processDialCall,代码如下:

private void processDialCall(String number) {String dialNumber;if ((number == null) || (number.length() == 0)) {//获取最近向外打的电话号码dialNumber = mPhonebook.getLastDialledNumber();if (dialNumber == null) { //没有最近拨打的电话,回应erroratResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);return;}} else if (number.charAt(0) == '>') {//测试} else {// Remove trailing ';'if (number.charAt(number.length() - 1) == ';') {number = number.substring(0, number.length() - 1);}dialNumber = PhoneNumberUtils.convertPreDial(number);}terminateScoUsingVirtualVoiceCall(); // 终止虚拟呼叫       Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,Uri.fromParts(SCHEME_TEL, dialNumber, null));intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);mService.startActivity(intent); //开启拨打电话的界面mDialingOut = true;sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE);
}

蓝牙耳机发过来的命令可能携带电话号码,也可能不带,对于没有电话号码则查询最近的拨打电话记录,拨打最近拨打的电话。对于有号码,则拨打该号码。
Intent.ACTION_CALL_PRIVILEGED(该变量是hide的,执行任何号码的呼叫,紧急或不紧急):”android.intent.action.CALL_PRIVILEGED”
通过该action打开系统应用Phone中的OutgoingCallBroadcaster界面,向外进行拨打电话。

欢迎扫一扫关注我的微信公众号,定期推送优质技术文章:

Android 蓝牙开发(八)hfp接听、挂断电话相关推荐

  1. android 关闭蓝牙打电话功能,Android蓝牙开发【八】hfp接听、挂断电话

    继续研究hfp相关功能.蓝牙耳机可以控制手机接听.拒接.挂断电话,拨打电话等功能.本文主要分析下起这些操作的大致流程. 在系统应用Bluetooth中com_android_bluetooth.cpp ...

  2. android自动接听和挂断电话

    实现android自动接听和挂断电话功能.代码如下: 添加权限 <uses-permission android:name="android.permission.CALL_PHONE ...

  3. Android(AIDL)自动重复拨号及挂断/接听电话

    Android默认没有提供挂断/接听电话的api,需要伪装com/android/internal/telephony/ITelephony.aidl的接口来欺骗系统.而自动重复拨号可以通过(Broa ...

  4. Android蓝牙开发(一)蓝牙模块及核心API

    本文主要介绍Android蓝牙开发中基础知识:蓝牙模块及核心API. 关于蓝牙的连接及通讯功能实现,欢迎查阅下一篇文章:Android蓝牙开发(二)蓝牙消息传输实现. 蓝牙模块 从蓝牙4.0开始包含两 ...

  5. HFP:不活跃的车载设备在打电话时,不能挂断电话

    Android P:Only active device can hung up call . 非 active device 不能挂断电话. step1:车载先连接 手机 step2:蓝牙耳机再连接 ...

  6. Android蓝牙开发 — 经典蓝牙BLE蓝牙

    一,前期基础知识储备 1)蓝牙是一种支持设备之间短距离通信的无线电技术(其他还包括红外,WIFI): 支持移动电话.笔记本电脑.无线耳机等设备之间进行信息的交换: Android支持的蓝牙协议栈:Bl ...

  7. android 自动挂断,android实现接通和挂断电话

    本文实例为大家分享了android实现接通和挂断电话的具体代码,供大家参考,具体内容如下 关键代码:[PhoneUtils类] package com.ebupt.phonerecorddemo.se ...

  8. android挂断电话广播,android实现接通和挂断电话

    android实现接通和挂断电话 发布时间:2020-08-21 01:52:02 来源:脚本之家 阅读:230 作者:WillenWu 本文实例为大家分享了android实现接通和挂断电话的具体代码 ...

  9. Android - 蓝牙开发

    文章目录 科普 SIG 类型 制式 选择 逻辑链路控制适配协议 (L2CAP) L2CAP的功能 蓝牙框架和 RFCOMM 协议 蓝牙安全 白名单机制 编程 蓝牙权限 Classic Bluetoot ...

最新文章

  1. connection could not be established with host smtp.exmail.qq.com
  2. 2012秋江苏省计算机二级上机试题,江苏省高校计算机等级考试二级2012秋资料.doc...
  3. 【POJ - 1486】Sorting Slides(思维建图,二分图求必须边,关建边,图论)
  4. 强封锁之后,华为正寻求10亿美元贷款;苹果发布iOS 12.3.1正式版,出击垃圾短信;联想CFO为“联想搬出中国”言论道歉;...
  5. java纯数字正则表达式_JAVA验证数字的正则表达式,来一发
  6. CentOS 7系统升级备份恢复实验记录
  7. 美国大学计算机与信息科学,【计算机学术讲堂】美国福特汉姆大学计算机与信息科学系Md Zakirul Alam Bhuiyan博士莅临我院作学术报告...
  8. 接管客厅第一步:联想智能音箱体验评测
  9. intellij IDEA 报 非法字符 \65279 原因及解决方法
  10. oracle安装最后一步完成了就消失了_Oracle安装过程中遇到的一些问题及解决方案...
  11. 华为音量键只能调通话_华为手机音量键的隐藏功能,知道一个就会好用不少!...
  12. 搞定Prometheus普罗米修斯监控系统
  13. 解读APP新一代验证登录方式——一键登录/免密认证
  14. 【linux】运行run文件显示cannot execute binary file
  15. ST MCU的国产替代
  16. IM软件中的语音录制与播放【iOS】
  17. Hcash:见证量子计算和后量子密码的“矛盾较量”
  18. Web前端工程师常去的15个技术网站
  19. SecureCRT 使用rz上传文件报错 Zmodem transfer canceled by remote side或失败
  20. java 键盘输入多种方法

热门文章

  1. 【JAVA项目实战】【图书管理系统】用户查询功能【Servlet】+【Jsp】+【Mysql】
  2. 第一次使用Eclipse:编写简单的Java小程序
  3. C语言数据结构——遍历二叉树
  4. 帝国CMS开发主播/直播视频网站源码+WAP/可封装APP运营
  5. 区块链教程、区块链指南、区块链中文手册、区块链原理
  6. ^_^ 给力,找了好久,终让我找到了,好多VB源码免费下载,路过的朋友快看看吧
  7. 纯DOS下安装Linux
  8. 华为快应用 - web标签无法加载部分网页
  9. 如何理解计算机网络的体系结构,如何理解计算机组成和计算机体系结构?
  10. Amazon EC2 Deep Dive 亚马逊EC2深度解析 Lynda课程中文字幕