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.pswp.cn/news/373105.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373105.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373105.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

android在listview中放入從sdcard讀取的bitmap

重寫viewbinder public class viewbinder_bookmark implements SimpleAdapter.ViewBinder{Overridepublic boolean setViewValue(View view, Object data, String textRepresentation){if(view instanceof ImageView && data instanceof Bitmap){ImageView imageview(I…

將狀態機模式實現為流處理器

在我的上一個博客中,我說我真的以為某些“四人行”(GOF)模式已經過時了,如果不是過時的話肯定不受歡迎。 特別是我說過StateMachine不是那么有用,因為您通常會想到另一種更簡單的方式來執行您正在執行的事情&#xff0…

android 自定義actionbar,如何讓android的actionbar浮動且透明

如上圖所示,谷歌地圖的actionbar是透明的,且浮動在整個布局之上,沒有占用布局空間。其實要做到這樣的效果,我們首先想到的是兩個方面:1.將讓actionbar浮動起來。2.給actionbar一個背景,可以為顏色也可以為圖…

CentOS 7安裝redis及php擴展

安裝remi源 # wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm # rpm -Uvh remi-release-7.rpm # sed -i -e "s/enabled1/enabled0/g" /etc/yum.repos.d/remi.repo 確認使用remi源時安裝的Redis版本。 安裝Redis 使用remi源yum安裝Redis。 # yum …

對Openshift上的Play Framework 2應用進行故障排除

Openshift故障排除 使用“ 自己動手”應用程序類型,您實際上可以有很大的自由度來支持幾乎可以在Linux機器上構建和運行的任何框架或服務器。 但是您必須做功課,并做一些研究。 因此,在本文中,我將向您展示一些我在使用Openshift和…

關于更換頭像的整個過程理解

之前我遇到一個問題,就是怎樣修改頭像,都沒有更改,后來把某個參數置為null,就解決了問題,但是知其然還要知其所以然,現在還是著重去梳理整個流程 頭像,需要關注的是3個變量: 本地地址…

Ajax與CustomErrors的尷尬

在ASP.NET程序中&#xff0c;為了給用戶顯示友好的錯誤信息&#xff0c;通常在web.config中進行如下的設置&#xff1a; <customErrors mode"RemoteOnly" defaultRedirect"/error/error.htm"> </customErrors> 但如果是一個ajax請求在服務端發…

JSF開發人員應該知道的5種有用方法

這篇文章的目的是總結一些JSF開發人員可以在日常工作中使用的便捷方法。 實用程序類是將所有方法放在一起的好地方。 我會稱此類為FacesAccessor。 第一種方法可能是最常用的方法。 它以給定名稱返回托管bean。 必須按faces-config.xml或注釋注冊該bean。 注入是好的&#xff0…

android項目編碼規范,Android 項目規范

Android 項目規范本文檔的目的是定義項目規范。這些應遵循整個 Android 項目以幫助我們保持整潔和統一的代碼庫。 &#x1f642;

Java創建WebService服務及客戶端實現

簡介 WebService是一種服務的提供方式&#xff0c;通過WebService&#xff0c;不同應用間相互間調用變的很方便&#xff0c;網絡上有很多常用的WebService服務&#xff0c;如&#xff1a;http://developer.51cto.com/art/200908/147125.htm&#xff0c;不同的語言平臺對…

01-17權限管理

管理頁面&#xff1a; <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><meta http-equi…

Java靜態方法可能會產生代碼異味

代碼氣味的定義 &#xff08;來自維基百科&#xff09;&#xff1a; “程序源代碼中任何可能表明存在更深層問題的癥狀。” 在Java中&#xff0c; 靜態方法允許您在“類范圍”內執行代碼&#xff0c;而不是像成員方法這樣的實例范圍。 這意味著&#xff0c;它們依賴于類級別的變…

android json 解析圖片,JSON解析并獲取android中的圖像

我想解析包含字符串和圖像的JSON對象。我的代碼正在工作&#xff0c;但它加載圖像太慢。我想加載另一個asynctask或服務的圖像&#xff0c;以減少加載時間。我怎樣才能做到這一點&#xff1f;哪一個是最好的方法使用asynctask或服務&#xff1f;這里是我的代碼JSON解析并獲取an…

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;但是費用疊加。求一個環使得收益和/花費和最大&…

安全密碼存儲–請勿做的事和Java示例

安全存儲密碼的重要性 作為軟件開發人員&#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;其可能具有…