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

继续研究hfp相关功能。蓝牙耳机可以控制手机接听、拒接、挂断电话,拨打电话等功能。本文主要分析下起这些操作的大致流程。

在系统应用Bluetooth中com_android_bluetooth.cpp提供了多个回调方法,由hardware、协议栈回调过来。蓝牙耳机的一些控制命令都会发到这里。

本文基于Android4.3源码。

1 接通电话

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

f36f71a181b7626981bd85bff3392617.png

在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) { //没有最近拨打的电话,回应error

atResponseCodeNative(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界面,向外进行拨打电话。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/373105.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

将状态机模式实现为流处理器

在我的上一个博客中,我说我真的以为某些“四人行”(GOF)模式已经过时了,如果不是过时的话肯定不受欢迎。 特别是我说过StateMachine不是那么有用,因为您通常会想到另一种更简单的方式来执行您正在执行的事情&#xff0…

android 自定义actionbar,如何让android的actionbar浮动且透明

如上图所示,谷歌地图的actionbar是透明的,且浮动在整个布局之上,没有占用布局空间。其实要做到这样的效果,我们首先想到的是两个方面:1.将让actionbar浮动起来。2.给actionbar一个背景,可以为颜色也可以为图…

Ajax与CustomErrors的尴尬

在ASP.NET程序中&#xff0c;为了给用户显示友好的错误信息&#xff0c;通常在web.config中进行如下的设置&#xff1a; <customErrors mode"RemoteOnly" defaultRedirect"/error/error.htm"> </customErrors> 但如果是一个ajax请求在服务端发…

Java创建WebService服务及客户端实现

简介 WebService是一种服务的提供方式&#xff0c;通过WebService&#xff0c;不同应用间相互间调用变的很方便&#xff0c;网络上有很多常用的WebService服务&#xff0c;如&#xff1a;http://developer.51cto.com/art/200908/147125.htm&#xff0c;不同的语言平台对…

Java静态方法可能会产生代码异味

代码气味的定义 &#xff08;来自维基百科&#xff09;&#xff1a; “程序源代码中任何可能表明存在更深层问题的症状。” 在Java中&#xff0c; 静态方法允许您在“类范围”内执行代码&#xff0c;而不是像成员方法这样的实例范围。 这意味着&#xff0c;它们依赖于类级别的变…

Node Express4.x 片段视图 partials

1.在Express 4.x使用片段视图&#xff0c;需要引入partials模块 步骤&#xff1a; 1.在全局中安装express-partials模块&#xff1a; 2.在本地模块中安装express-partials,将模块安装到package.json中&#xff1a; 3.在入口文件(如&#xff1a;app.js)中引入模块&#xff1a; v…

bzoj1690:[Usaco2007 Dec]奶牛的旅行(分数规划+spfa判负环)

PS:此题数组名皆引用&#xff1a;戳我 题目大意&#xff1a;有n个点m条有向边的图&#xff0c;边上有花费&#xff0c;点上有收益&#xff0c;点可以多次经过&#xff0c;但是收益不叠加&#xff0c;边也可以多次经过&#xff0c;但是费用叠加。求一个环使得收益和/花费和最大&…

红米note4x Android7,红米Note4X能升级安卓7.0吗?红米Note4X如何升级Android7.0?

欢迎来到PPL网站的行业资讯知识分类&#xff0c;你现在观看的这篇文章要和大家分享的是关于红米Note4X能升级安卓7.0吗&#xff1f;红米Note4X如何升级Android7.0&#xff1f;的一些相关内容&#xff0c;希望大家能够感兴趣&#xff0c;并且希望我们能够帮助到你&#xff01;在…

java基础----数字签名算法的介绍

数字签名&#xff08;又称公钥数字签名&#xff09;是一种类似写在纸上的普通的物理签名&#xff0c;但是使用了公钥加密领域的技术实现&#xff0c;用于鉴别数字信息的方法。关于数字签名的介绍&#xff0c;可以参见百度百科&#xff1a;http://baike.baidu.com/view/7626.htm…

Android宫格自动换行,九宫格视图的布局及展示(相册选择)

上周一个朋友带的项目出了点问题&#xff0c;招的ios开发人员在实现选取相册图片后用九宫格的样式展示时遇到了瓶颈&#xff0c;花了将近2周都没有解决。后来在跟我交流的过程中他把项目的图片发给我看了下&#xff0c;看完我就笑了&#xff0c;这就只是个算法的问题&#xff0…

具有LCS方法的通用文本比较工具

常见的问题是检测并显示两个文本&#xff08;尤其是几百行或几千行&#xff09;的差异。 使用纯java.lang.String类方法可能是一种解决方案&#xff0c;但是对于此类操作最重要的问题是&#xff0c;“性能”将不能令人满意。 我们需要一种有效的解决方案&#xff0c;其可能具有…

eclipse 开发 scala

(环境&#xff1a;jdk1.7,scala插件scala-2.1.1.2-site.zip) 1:下载scala插件 http://download.scala-ide.org/sdk/helium/e38/scala211/stable/site2&#xff1a;解压到本地将这两个文件里的jar包全部复制到eclipse的安装目录对应的文件夹里三&#xff1a;重启eclipse这时会提…

Quartz Scheduler失火指令说明

有时&#xff0c;Quartz无法在您需要的时间运行您的工作。 这有三个原因&#xff1a; 所有工作线程都忙于运行其他作业&#xff08;可能具有更高的优先级&#xff09; 调度程序本身已关闭 该作业是在过去的开始时间安排的&#xff08;可能是编码错误&#xff09; 您可以通过…

改进租房练习

代码基本没有改动&#xff0c;函数有变化&#xff0c;老师只用了一个函数&#xff0c;自己做写了4个function&#xff0c;减少了代码量 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitio…

Google App Engine JAX-RS REST服务

在本文中&#xff0c;您将学习如何使用JAX-RS参考实现&#xff08;Jersey&#xff09;创建REST服务并将其部署在Google AppEngine上。 先决条件 对于本教程&#xff0c;您将需要&#xff1a; Google AppEngine帐户 Eclipse Galileo&#xff08;3.5.x&#xff09; 适用于Java的…

鸿蒙系统的全面开源,华为:打造全球的操作系统,鸿蒙今日全面开源!

原标题&#xff1a;华为&#xff1a;打造全球的操作系统&#xff0c;鸿蒙今日全面开源&#xff01;今日下午&#xff0c;2019华为全球开发者大会在华为松山湖基地正式开幕。华为正式对外推出了自研操作系统——鸿蒙系统(Harmony OS)。华为消费者业务CEO余承东指出&#xff0c;鸿…

html5 游戏制作教程,html5一步步实现超级玛丽游戏制作(新手教程源码)

【实例简介】【实例截图】【核心代码】My first Gamebody {border:none 0px;margin:0px;padding:10px;font-size : 16px;background-color : #f3f3f3;}canvas {border : 1px solid blue;}// 页面初始化函数function init(){//加载图片,并存入全局变量 ImgCache,// 加载完成后,调…

交友系统设计:哪种地理空间邻近算法更快?

小熊学Java&#xff1a;https://javaxiaobear.cn 交友与婚恋是人们最基本的需求之一。随着互联网时代的不断发展&#xff0c;移动社交软件已经成为了人们生活中必不可少的一部分。然而&#xff0c;熟人社交并不能完全满足年轻人的社交与情感需求&#xff0c;于是陌生人交友平台…

Apache Camel教程– EIP,路由,组件,测试和其他概念的简介

公司之间的数据交换增加了很多。 必须集成的应用程序数量也增加了。 这些接口使用不同的技术&#xff0c;协议和数据格式。 但是&#xff0c;这些应用程序的集成应以标准化的方式建模&#xff0c;有效实现并由自动测试支持。 企业集成模式&#xff08;EIP&#xff09;[1]中存在…

iOS开发UI篇—UITableview控件简单介绍

一、基本介绍 在众多移动应⽤用中,能看到各式各样的表格数据 。 在iOS中,要实现表格数据展示,最常用的做法就是使用UITableView&#xff0c;UITableView继承自UIScrollView,因此支持垂直滚动,⽽且性能极佳 。 UITableview有分组和不分组两种样式&#xff0c;可以在storyboard或…