今天试图解决android挂断电话没有响应的一个bug,跟踪了一下Android 挂断电话流程,在此做个记录
有电话打入是RIL会通知CallNotifier, CallNotifier会调用InCallScreen,这些不再我们今天讨论的范围内,简单提一下。
CallNotifier会调用InCallScreen 代码
InCallScreen.java packages\apps\phone\src\com\android\Phone
okToShowInCallTouchUi
- PhoneUtils.showIncomingCallUi(mPhone);
然后InCallScreen会显示InCallTouchUI , InCallTouchUI 就是可以拖动接听和挂断的那个界面 ,这个页面持有一个SlidingTab控件,这个就是可以左右拖动的控件了。
InCallTouchUI实现了 SlidingTab.OnTriggerListener, 在该监听者的OnTrigge方法里面处理接听和挂断的动作,代码如下
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- public void onTrigger(View v, int whichHandle) {
- log("onDialTrigger(whichHandle = " + whichHandle + ")...");
-
- switch (whichHandle) {
- case SlidingTab.OnTriggerListener.LEFT_HANDLE:
- if (DBG) log("LEFT_HANDLE: answer!");
-
- hideIncomingCallWidget();
-
-
-
- mLastIncomingCallActionTime = SystemClock.uptimeMillis();
-
-
- if (mInCallScreen != null) {
-
- mInCallScreen.handleOnscreenButtonClick(R.id.answerButton);
- } else {
- Log.e(LOG_TAG, "answer trigger: mInCallScreen is null");
- }
- break;
-
- case SlidingTab.OnTriggerListener.RIGHT_HANDLE:
- if (DBG) log("RIGHT_HANDLE: reject!");
-
- hideIncomingCallWidget();
-
-
-
- mLastIncomingCallActionTime = SystemClock.uptimeMillis();
-
-
- if (mInCallScreen != null) {
-
- mInCallScreen.handleOnscreenButtonClick(R.id.rejectButton);
- } else {
- Log.e(LOG_TAG, "reject trigger: mInCallScreen is null");
- }
- break;
-
- default:
- Log.e(LOG_TAG, "onDialTrigger: unexpected whichHandle value: " + whichHandle);
- break;
- }
-
-
-
- mInCallScreen.updateSlidingTabHint(0, 0);
- }
InCallScreen的 handleOnscreenButtonClick方法
-
-
-
- void handleOnscreenButtonClick(int id) {
- if (DBG) log("handleOnscreenButtonClick(id " + id + ")...");
-
- switch (id) {
-
-
-
-
-
-
- case R.id.answerButton:
- internalAnswerCall();
- break;
- case R.id.rejectButton:
- internalHangupRingingCall();
- break;
-
-
- case R.id.holdButton:
- onHoldClick();
- break;
- case R.id.swapButton:
- internalSwapCalls();
- break;
- case R.id.endButton:
- internalHangup();
- break;
- case R.id.dialpadButton:
- onShowHideDialpad();
- break;
- case R.id.bluetoothButton:
- onBluetoothClick();
- break;
- case R.id.muteButton:
- onMuteClick();
- break;
- case R.id.speakerButton:
- onSpeakerClick();
- break;
- case R.id.addButton:
- PhoneUtils.startNewCall(mCM);
- break;
- case R.id.mergeButton:
- case R.id.cdmaMergeButton:
- PhoneUtils.mergeCalls(mCM);
- break;
- case R.id.manageConferencePhotoButton:
-
- setInCallScreenMode(InCallScreenMode.MANAGE_CONFERENCE);
- break;
-
- default:
- Log.w(LOG_TAG, "handleOnscreenButtonClick: unexpected ID " + id);
- break;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- updateInCallTouchUi();
- }
然后调用 PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall()); 方法
-
-
-
- void internalHangupRingingCall() {
- if (DBG) log("internalHangupRingingCall()...");
- if (VDBG) PhoneUtils.dumpCallManager();
-
-
- PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall());
- }
- static boolean hangupRingingCall(Call ringing) {
- if (DBG) log("hangup ringing call");
- int phoneType = ringing.getPhone().getPhoneType();
-
- if (phoneType == Phone.PHONE_TYPE_CDMA) {
-
-
-
- Call.State state = ringing.getState();
- if (state == Call.State.INCOMING) {
- if (DBG) log("hangup ringing call");
- return hangup(ringing);
- } else if (state == Call.State.WAITING) {
- if (DBG) log("hangup Call waiting call");
- final CallNotifier notifier = PhoneApp.getInstance().notifier;
- notifier.sendCdmaCallWaitingReject();
- return true;
- } else {
-
-
-
- if (DBG) log("No Ringing call to hangup");
- return false;
- }
- } else if ((phoneType == Phone.PHONE_TYPE_GSM)
- || (phoneType == Phone.PHONE_TYPE_SIP)) {
-
-
- if (DBG) log("hangup ringing call");
- return hangup(ringing);
- } else {
- throw new IllegalStateException("Unexpected phone type: " + phoneType);
- }
- }
-
-
-
-
-
-
-
-
- static boolean hangup(Call call) {
- try {
- call.hangup();
- return true;
- } catch (CallStateException ex) {
- Log.e(LOG_TAG, "Call hangup: caught " + ex, ex);
- }
-
- return false;
- }
然后进入 GsmCall
- public void
- hangup() throws CallStateException {
- owner.hangup(this);
- }
然后进入 frameworks/base/telephony/java/com/android/internal/telephony/gsm/GsmCallTracker.java
可以看到很多不同情况下挂断电话的处理,我们只看第一种情况 hangupWaitingOrBackground
-
-
- /* package */ void
- hangup (GsmCall call) throws CallStateException {
- if (call.getConnections().size() == 0) {
- throw new CallStateException("no connections in call");
- }
-
- if (call == ringingCall) {
- if (Phone.DEBUG_PHONE) log("(ringing) hangup waiting or background");
- cm.hangupWaitingOrBackground(obtainCompleteMessage());
- } else if (call == foregroundCall) {
- if (call.isDialingOrAlerting()) {
- if (Phone.DEBUG_PHONE) {
- log("(foregnd) hangup dialing or alerting...");
- }
- hangup((GsmConnection)(call.getConnections().get(0)));
- } else {
- hangupForegroundResumeBackground();
- }
- } else if (call == backgroundCall) {
- if (ringingCall.isRinging()) {
- if (Phone.DEBUG_PHONE) {
- log("hangup all conns in background call");
- }
- hangupAllConnections(call);
- } else {
- hangupWaitingOrBackground();
- }
- } else {
- throw new RuntimeException ("GsmCall " + call +
- "does not belong to GsmCallTracker " + this);
- }
-
- call.onHangupLocal();
- phone.notifyPreciseCallStateChanged();
- }
下面进入RIL java, 向rild 发请求了
- public void
- hangupWaitingOrBackground (Message result) {
- RILRequest rr = RILRequest.obtain(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
- result);
-
- if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
-
- send(rr);
- }
再往下就是 rild , 厂商refence ril工作了 ,其实就是发AT命令,这部分每个厂商可能有自己的实现,就不细说了 。
总之,android的挂断电话流程还是挺复杂的,中间要处理很多不同的情景。