关闭

Android 挂断电话流程

标签: androidbuttontriggersuser电话java
977人阅读 评论(0) 收藏 举报
分类:

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


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

CallNotifier会调用InCallScreen 代码

  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(0, 0); 


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(); 


然后调用 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()); 

  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.     } 


  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

然后进入 GsmCall

  1. public void 
  2. hangup() throws CallStateException { 
  3.     owner.hangup(this); 

然后进入 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(); 


下面进入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); 

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


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

 

原文地址:http://blog.csdn.net/offbye/article/details/6660693

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:142476次
    • 积分:1924
    • 等级:
    • 排名:千里之外
    • 原创:32篇
    • 转载:112篇
    • 译文:0篇
    • 评论:21条
    文章分类
    最新评论