Android 挂断电话流程

今天试图解决android挂断电话没有响应的一个bug,跟踪了一下Android 挂断电话流程,在此做个记录 


有电话打入是RIL会通知CallNotifier, CallNotifier会调用InCallScreen,这些不再我们今天讨论的范围内,简单提一下。

CallNotifier会调用InCallScreen 代码 

InCallScreen.java    packages\apps\phone\src\com\android\Phone

okToShowInCallTouchUi


  1. PhoneUtils.showIncomingCallUi(mPhone);  


然后InCallScreen会显示InCallTouchUI , InCallTouchUI 就是可以拖动接听和挂断的那个界面 ,这个页面持有一个SlidingTab控件,这个就是可以左右拖动的控件了。

InCallTouchUI实现了 SlidingTab.OnTriggerListener, 在该监听者的OnTrigge方法里面处理接听和挂断的动作,代码如下

  1. //  
  2. // SlidingTab.OnTriggerListener implementation  
  3. //  
  4.   
  5. /** 
  6.  * Handles "Answer" and "Reject" actions for an incoming call. 
  7.  * We get this callback from the SlidingTab 
  8.  * when the user triggers an action. 
  9.  * 
  10.  * To answer or reject the incoming call, we call 
  11.  * InCallScreen.handleOnscreenButtonClick() and pass one of the 
  12.  * special "virtual button" IDs: 
  13.  *   - R.id.answerButton to answer the call 
  14.  * or 
  15.  *   - R.id.rejectButton to reject the call. 
  16.  */  
  17. public void onTrigger(View v, int whichHandle) {  
  18.     log("onDialTrigger(whichHandle = " + whichHandle + ")...");  
  19.   
  20.     switch (whichHandle) {  
  21.         case SlidingTab.OnTriggerListener.LEFT_HANDLE:  
  22.             if (DBG) log("LEFT_HANDLE: answer!");  
  23.   
  24.             hideIncomingCallWidget();  
  25.   
  26.             // ...and also prevent it from reappearing right away.  
  27.             // (This covers up a slow response from the radio; see updateState().)  
  28.             mLastIncomingCallActionTime = SystemClock.uptimeMillis();  
  29.   
  30.             // Do the appropriate action.  
  31.             if (mInCallScreen != null) {  
  32.                 // Send this to the InCallScreen as a virtual "button click" event:  
  33.                 mInCallScreen.handleOnscreenButtonClick(R.id.answerButton);  
  34.             } else {  
  35.                 Log.e(LOG_TAG, "answer trigger: mInCallScreen is null");  
  36.             }  
  37.             break;  
  38.   
  39.         case SlidingTab.OnTriggerListener.RIGHT_HANDLE:  
  40.             if (DBG) log("RIGHT_HANDLE: reject!");  
  41.   
  42.             hideIncomingCallWidget();  
  43.   
  44.             // ...and also prevent it from reappearing right away.  
  45.             // (This covers up a slow response from the radio; see updateState().)  
  46.             mLastIncomingCallActionTime = SystemClock.uptimeMillis();  
  47.   
  48.             // Do the appropriate action.  
  49.             if (mInCallScreen != null) {  
  50.                 // Send this to the InCallScreen as a virtual "button click" event:  
  51.                 mInCallScreen.handleOnscreenButtonClick(R.id.rejectButton);   //看到了吧,这个就是挂断电话的代码了,继续往下看  
  52.             } else {  
  53.                 Log.e(LOG_TAG, "reject trigger: mInCallScreen is null");  
  54.             }  
  55.             break;  
  56.   
  57.         default:  
  58.             Log.e(LOG_TAG, "onDialTrigger: unexpected whichHandle value: " + whichHandle);  
  59.             break;  
  60.     }  
  61.   
  62.     // Regardless of what action the user did, be sure to clear out  
  63.     // the hint text we were displaying while the user was dragging.  
  64.     mInCallScreen.updateSlidingTabHint(00);  
  65. }  


InCallScreen的 handleOnscreenButtonClick方法

  1. /** 
  2.  * Handles button clicks from the InCallTouchUi widget. 
  3.  */  
  4. /* package */ void handleOnscreenButtonClick(int id) {  
  5.     if (DBG) log("handleOnscreenButtonClick(id " + id + ")...");  
  6.   
  7.     switch (id) {  
  8.         // TODO: since every button here corresponds to a menu item that we  
  9.         // already handle in onClick(), maybe merge the guts of these two  
  10.         // methods into a separate helper that takes an ID (of either a menu  
  11.         // item *or* touch button) and does the appropriate user action.  
  12.   
  13.         // Actions while an incoming call is ringing:  
  14.         case R.id.answerButton:  
  15.             internalAnswerCall();  
  16.             break;  
  17.         case R.id.rejectButton:  
  18.             internalHangupRingingCall();             //挂断电话的方法  
  19.             break;  
  20.   
  21.         // The other regular (single-tap) buttons used while in-call:  
  22.         case R.id.holdButton:  
  23.             onHoldClick();  
  24.             break;  
  25.         case R.id.swapButton:  
  26.             internalSwapCalls();  
  27.             break;  
  28.         case R.id.endButton:  
  29.             internalHangup();  
  30.             break;  
  31.         case R.id.dialpadButton:  
  32.             onShowHideDialpad();  
  33.             break;  
  34.         case R.id.bluetoothButton:  
  35.             onBluetoothClick();  
  36.             break;  
  37.         case R.id.muteButton:  
  38.             onMuteClick();  
  39.             break;  
  40.         case R.id.speakerButton:  
  41.             onSpeakerClick();  
  42.             break;  
  43.         case R.id.addButton:  
  44.             PhoneUtils.startNewCall(mCM);  // Fires off an ACTION_DIAL intent  
  45.             break;  
  46.         case R.id.mergeButton:  
  47.         case R.id.cdmaMergeButton:  
  48.             PhoneUtils.mergeCalls(mCM);  
  49.             break;  
  50.         case R.id.manageConferencePhotoButton:  
  51.             // Show the Manage Conference panel.  
  52.             setInCallScreenMode(InCallScreenMode.MANAGE_CONFERENCE);  
  53.             break;  
  54.   
  55.         default:  
  56.             Log.w(LOG_TAG, "handleOnscreenButtonClick: unexpected ID " + id);  
  57.             break;  
  58.     }  
  59.   
  60.     // Just in case the user clicked a "stateful" menu item (i.e. one  
  61.     // of the toggle buttons), we force the in-call buttons to update,  
  62.     // to make sure the user sees the *new* current state.  
  63.     //  
  64.     // (But note that some toggle buttons may *not* immediately change  
  65.     // the state of the Phone, in which case the updateInCallTouchUi()  
  66.     // call here won't have any visible effect.  Instead, those  
  67.     // buttons will get updated by the updateScreen() call that gets  
  68.     // triggered when the onPhoneStateChanged() event comes in.)  
  69.     //  
  70.     // TODO: updateInCallTouchUi() is overkill here; it would be  
  71.     // more efficient to update *only* the affected button(s).  
  72.     // Consider adding API for that.  (This is lo-pri since  
  73.     // updateInCallTouchUi() is pretty cheap already...)  
  74.     updateInCallTouchUi();  
  75. }  


然后调用 PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall()); 方法 
  1. /** 
  2.  * Hang up the ringing call (aka "Don't answer"). 
  3.  */  
  4. /* package */ void internalHangupRingingCall() {  
  5.     if (DBG) log("internalHangupRingingCall()...");  
  6.     if (VDBG) PhoneUtils.dumpCallManager();  
  7.     // In the rare case when multiple calls are ringing, the UI policy  
  8.     // it to always act on the first ringing call.v  
  9.     PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());  
  10. }  

  1. static boolean hangupRingingCall(Call ringing) {  
  2.     if (DBG) log("hangup ringing call");  
  3.     int phoneType = ringing.getPhone().getPhoneType();  
  4.   
  5.     if (phoneType == Phone.PHONE_TYPE_CDMA) {  
  6.         // CDMA: Ringing call and Call waiting hangup is handled differently.  
  7.         // For Call waiting we DO NOT call the conventional hangup(call) function  
  8.         // as in CDMA we just want to hungup the Call waiting connection.  
  9.         Call.State state = ringing.getState();  
  10.         if (state == Call.State.INCOMING) {  
  11.             if (DBG) log("hangup ringing call");  
  12.             return hangup(ringing);  
  13.         } else if (state == Call.State.WAITING) {  
  14.             if (DBG) log("hangup Call waiting call");  
  15.             final CallNotifier notifier = PhoneApp.getInstance().notifier;  
  16.             notifier.sendCdmaCallWaitingReject();  
  17.             return true;  
  18.         } else {  
  19.             // This should never happen cause hangupRingingCall should always be called  
  20.             // if the call.isRinging() returns TRUE, which basically means that the call  
  21.             // should either be in INCOMING or WAITING state  
  22.             if (DBG) log("No Ringing call to hangup");  
  23.             return false;  
  24.         }  
  25.     } else if ((phoneType == Phone.PHONE_TYPE_GSM)  
  26.             || (phoneType == Phone.PHONE_TYPE_SIP)) {  
  27.         // GSM:  Ringing Call and Call waiting, both are hungup by calling  
  28.         // hangup(call) function.  
  29.         if (DBG) log("hangup ringing call");  
  30.         return hangup(ringing);                 
  31.     } else {  
  32.         throw new IllegalStateException("Unexpected phone type: " + phoneType);  
  33.     }  
  34. }  


  1. /** 
  2.  * Trivial wrapper around Call.hangup(), except that we return a 
  3.  * boolean success code rather than throwing CallStateException on 
  4.  * failure. 
  5.  * 
  6.  * @return true if the call was successfully hung up, or false 
  7.  *         if the call wasn't actually active. 
  8.  */  
  9. static boolean hangup(Call call) {  
  10.     try {  
  11.         call.hangup();  
  12.         return true;  
  13.     } catch (CallStateException ex) {  
  14.         Log.e(LOG_TAG, "Call hangup: caught " + ex, ex);  
  15.     }  
  16.   
  17.     return false;  
  18. }  

然后进入 GsmCall 
  1. public void  
  2. hangup() throws CallStateException {  
  3.     owner.hangup(this);  
  4. }  

然后进入 frameworks/base/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java 

可以看到很多不同情况下挂断电话的处理,我们只看第一种情况 hangupWaitingOrBackground


  1. //***** Called from GsmCall  
  2.   
  3. /* package */ void  
  4. hangup (GsmCall call) throws CallStateException {  
  5.     if (call.getConnections().size() == 0) {  
  6.         throw new CallStateException("no connections in call");  
  7.     }  
  8.   
  9.     if (call == ringingCall) {  
  10.         if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");  
  11.         cm.hangupWaitingOrBackground(obtainCompleteMessage());  
  12.     } else if (call == foregroundCall) {  
  13.         if (call.isDialingOrAlerting()) {  
  14.             if (Phone.DEBUG_PHONE) {  
  15.                 log("(foregnd) hangup dialing or alerting...");  
  16.             }  
  17.             hangup((GsmConnection)(call.getConnections().get(0)));  
  18.         } else {  
  19.             hangupForegroundResumeBackground();  
  20.         }  
  21.     } else if (call == backgroundCall) {  
  22.         if (ringingCall.isRinging()) {  
  23.             if (Phone.DEBUG_PHONE) {  
  24.                 log("hangup all conns in background call");  
  25.             }  
  26.             hangupAllConnections(call);  
  27.         } else {  
  28.             hangupWaitingOrBackground();  
  29.         }  
  30.     } else {  
  31.         throw new RuntimeException ("GsmCall " + call +  
  32.                 "does not belong to GsmCallTracker " + this);  
  33.     }  
  34.   
  35.     call.onHangupLocal();  
  36.     phone.notifyPreciseCallStateChanged();  
  37. }  


下面进入RIL java, 向rild 发请求了 

  1. public void  
  2. hangupWaitingOrBackground (Message result) {  
  3.     RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,  
  4.                                     result);  
  5.   
  6.     if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));  
  7.   
  8.     send(rr);  
  9. }  

再往下就是  rild , 厂商refence ril工作了 ,其实就是发AT命令,这部分每个厂商可能有自己的实现,就不细说了 。


总之,android的挂断电话流程还是挺复杂的,中间要处理很多不同的情景。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值