Android藍牙開發【八】hfp接聽、掛斷電話

繼續研究hfp相關功能。藍牙耳機能夠控制手機接聽、拒接、掛斷電話,撥打電話等功能。本文主要分析下起這些操做的大體流程。 
在系統應用Bluetooth中com_android_bluetooth.cpp提供了多個回調方法,由hardware、協議棧回調過來。藍牙耳機的一些控制命令都會發到這裏。 android

本文基於Android4.3源碼。ide

 

 

1 接通電話

 

藍牙耳機控制手機接通電話,回掉com_android_bluetooth.cpp中的answer_call_callback()函數,該函數主要操做是調用HeadsetStateMachine的onAnswerCall()函數,代碼以下:函數

這裏寫圖片描述
在onAnswerCall()中發送消息(消息類型STACK_EVENT,StackEvent事件類型EVENT_TYPE_ANSWER_CALL)向狀體機,此時通話還沒有接通,audio沒有鏈接,因此此時處於Connected狀態。狀態機收到該消息後調用processAnswerCall()函數。processAnswerCall()代碼以下:測試

 

 
  1. private void processAnswerCall() {ui

  2. if (mPhoneProxy != null) {spa

  3. try {code

  4. //mPhoneProxy是經過bindservice 獲取的。blog

  5. mPhoneProxy.answerCall();接口

  6. } catch (RemoteException e) {事件

  7. }

  8. } else {

  9. }

  10. }

初始化的時候會bind service,綁定的該service爲系統應用Phone下的BluetoothPhoneService(AndroidManifest中該service的action爲android.bluetooth.IBluetoothHeadsetPhone),代碼以下:

 

 
  1. //參數爲android.bluetooth.IBluetoothHeadsetPhone

  2. Intent intent = new Intent(IBluetoothHeadsetPhone.class.getName());

  3. //resolveSystemService該方法是hide的,由系統使用的特殊功能來解決系統應用程序的服務意圖。

  4. intent.setComponent(intent.resolveSystemService(context.getPackageManager(), 0));

  5. if (intent.getComponent() == null || !context.bindService(intent, mConnection, 0)) {

  6. Log.e(TAG, "Could not bind to Bluetooth Headset Phone Service");

  7. }

綁定service成功回調mConnection,在其成功回調中設置的mPhoneProxy。經過mPhoneProxy來調用service中提供的接口。mPhoneProxy.answerCall()跳到BluetoothPhoneService中answerCall。

 

 
  1. public boolean answerCall() {

  2. //申請權限,修改電話狀態

  3. enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);

  4. return PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());

  5. }

 

PhoneUtils調用answerCall,在這裏面去接通電話。answerCall()就不具體分析了。


 

2 拒接、掛斷電話

藍牙耳機控制手機拒接、掛斷電話,回掉com_android_bluetooth.cpp中的hangup_call_callback()函數,該函數主要操做是調用HeadsetStateMachine的onHangupCall()函數,代碼以下:

 

 

 
  1. private void onHangupCall() {

  2. StackEvent event = new StackEvent(EVENT_TYPE_HANGUP_CALL);

  3. sendMessage(STACK_EVENT, event);

  4. }

此時HeadsetStateMachine可能處於Conneted或AudioOn狀態,這兩種狀態收到該消息的處理同樣,都是調用processHangupCall(),代碼以下:

 
  1. private void processHangupCall() {

  2. if (isVirtualCallInProgress()) {

  3. //對於虛擬電話,結束。

  4. terminateScoUsingVirtualVoiceCall();

  5. } else {

  6. if (mPhoneProxy != null) {

  7. try { //掛斷電話

  8. mPhoneProxy.hangupCall();

  9. } catch (RemoteException e) {

  10. }

  11. } else {

  12. }

  13. }

  14. }

對於虛擬電話則直接將其結束。真實的通話跳到BluetoothPhoneService的hangupCall。

 
  1. public boolean hangupCall() {

  2. enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, null);

  3. if (mCM.hasActiveFgCall()) { //掛斷正在進行的通話

  4. return PhoneUtils.hangupActiveCall(mCM.getActiveFgCall());

  5. } else if (mCM.hasActiveRingingCall()) { //中止正在響鈴的電話

  6. return PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());

  7. } else if (mCM.hasActiveBgCall()) { //掛斷保持的電話

  8. return PhoneUtils.hangupHoldingCall(mCM.getFirstActiveBgCall());

  9. }

  10. return false;

  11. }

 

hangupCall中會根據狀態處理通話,優先處理正在進行的通話、其次是還沒有接通的電話、最後是保持的電話。


 

3 更改通話音量

藍牙耳機更改通話的音量,回掉com_android_bluetooth.cpp中的volume_control_callback()函數,該函數主要操做是調用HeadsetStateMachine的onVolumeChnaged()函數,代碼以下:

 
  1. private void onVolumeChanged(int type, int volume) {

  2. StackEvent event = new StackEvent(EVENT_TYPE_VOLUME_CHANGED);

  3. event.valueInt = type;

  4. event.valueInt2 = volume;

  5. sendMessage(STACK_EVENT, event);

  6. }

此時HeadsetStateMachine可能處於Conneted或AudioOn狀態,這兩種狀態收到該消息的處理同樣,都是調用processVolumeEvent,代碼以下:
 

 
  1. private void processVolumeEvent(int volumeType, int volume) {

  2. if (volumeType == HeadsetHalConstants.VOLUME_TYPE_SPK) {

  3. mPhoneState.setSpeakerVolume(volume);

  4. //是否在ui上顯示

  5. int flag = (getCurrentState() == mAudioOn) ? AudioManager.FLAG_SHOW_UI : 0;

  6. //設置SCO通道聲音大小。

  7. mAudioManager.setStreamVolume(AudioManager.STREAM_BLUETOOTH_SCO, volume, flag);

  8. } else if (volumeType == HeadsetHalConstants.VOLUME_TYPE_MIC) {

  9. // 只是存了下該volume值,並無設置mic。

  10. mPhoneState.setMicVolume(volume);

  11. } else {

  12. }

  13. }

 

更改音量兩種類型,VOLUME_TYPE_MIC類型,保存了下該值,並無看到具體用該值的地方。對於VOLUME_TYPE_SPK類型的,會設置SCO聲音大小。若是此時處於AudioOn狀態,則會在UI上顯示。


 

4 撥打電話

藍牙耳機進行撥打電話,回掉com_android_bluetooth.cpp中的dial_call_callback函數,該函數主要操做是調用HeadsetStateMachine的onDialCall()函數,代碼以下:

 

 
  1. private void onDialCall(String number) {

  2. StackEvent event = new StackEvent(EVENT_TYPE_DIAL_CALL);

  3. event.valueString = number;

  4. sendMessage(STACK_EVENT, event);

  5. }


此時HeadsetStateMachine可能處於Conneted或AudioOn狀態,這兩種狀態收到該消息的處理同樣,都是調用processDialCall,代碼以下:

 
  1. private void processDialCall(String number) {

  2. String dialNumber;

  3. if ((number == null) || (number.length() == 0)) {

  4. //獲取最近向外打的電話號碼

  5. dialNumber = mPhonebook.getLastDialledNumber();

  6. if (dialNumber == null) { //沒有最近撥打的電話,迴應error

  7. atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_ERROR, 0);

  8. return;

  9. }

  10. } else if (number.charAt(0) == '>') {

  11. //測試

  12. } else {

  13. // Remove trailing ';'

  14. if (number.charAt(number.length() - 1) == ';') {

  15. number = number.substring(0, number.length() - 1);

  16. }

  17. dialNumber = PhoneNumberUtils.convertPreDial(number);

  18. }

  19. terminateScoUsingVirtualVoiceCall(); // 終止虛擬呼叫

  20. Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED,

  21. Uri.fromParts(SCHEME_TEL, dialNumber, null));

  22. intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

  23. mService.startActivity(intent); //開啓撥打電話的界面

  24. mDialingOut = true;

  25. sendMessageDelayed(DIALING_OUT_TIMEOUT, DIALING_OUT_TIMEOUT_VALUE);

  26. }

藍牙耳機發過來的命令可能攜帶電話號碼,也可能不帶,對於沒有電話號碼則查詢最近的撥打電話記錄,撥打最近撥打的電話。對於有號碼,則撥打該號碼。  Intent.ACTION_CALL_PRIVILEGED(該變量是hide的,執行任何號碼的呼叫,緊急或不緊急):」android.intent.action.CALL_PRIVILEGED」  經過該action打開系統應用Phone中的OutgoingCallBroadcaster界面,向外進行撥打電話。

相關文章
相關標籤/搜索