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


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

CallNotifier会调用InCallScreen 代码

 PhoneUtils.showIncomingCallUi(mPhone);


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

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

    //    // SlidingTab.OnTriggerListener implementation    //    /**     * Handles "Answer" and "Reject" actions for an incoming call.     * We get this callback from the SlidingTab     * when the user triggers an action.     *     * To answer or reject the incoming call, we call     * InCallScreen.handleOnscreenButtonClick() and pass one of the     * special "virtual button" IDs:     *   - R.id.answerButton to answer the call     * or     *   - R.id.rejectButton to reject the call.     */    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();                // ...and also prevent it from reappearing right away.                // (This covers up a slow response from the radio; see updateState().)                mLastIncomingCallActionTime = SystemClock.uptimeMillis();                // Do the appropriate action.                if (mInCallScreen != null) {                    // Send this to the InCallScreen as a virtual "button click" event:                    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();                // ...and also prevent it from reappearing right away.                // (This covers up a slow response from the radio; see updateState().)                mLastIncomingCallActionTime = SystemClock.uptimeMillis();                // Do the appropriate action.                if (mInCallScreen != null) {                    // Send this to the InCallScreen as a virtual "button click" event:                    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;        }        // Regardless of what action the user did, be sure to clear out        // the hint text we were displaying while the user was dragging.        mInCallScreen.updateSlidingTabHint(0, 0);    }


InCallScreen的 handleOnscreenButtonClick方法

    /**     * Handles button clicks from the InCallTouchUi widget.     */    /* package */ void handleOnscreenButtonClick(int id) {        if (DBG) log("handleOnscreenButtonClick(id " + id + ")...");        switch (id) {            // TODO: since every button here corresponds to a menu item that we            // already handle in onClick(), maybe merge the guts of these two            // methods into a separate helper that takes an ID (of either a menu            // item *or* touch button) and does the appropriate user action.            // Actions while an incoming call is ringing:            case R.id.answerButton:                internalAnswerCall();                break;            case R.id.rejectButton:                internalHangupRingingCall();             //挂断电话的方法                break;            // The other regular (single-tap) buttons used while in-call:            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);  // Fires off an ACTION_DIAL intent                break;            case R.id.mergeButton:            case R.id.cdmaMergeButton:                PhoneUtils.mergeCalls(mCM);                break;            case R.id.manageConferencePhotoButton:                // Show the Manage Conference panel.                setInCallScreenMode(InCallScreenMode.MANAGE_CONFERENCE);                break;            default:                Log.w(LOG_TAG, "handleOnscreenButtonClick: unexpected ID " + id);                break;        }        // Just in case the user clicked a "stateful" menu item (i.e. one        // of the toggle buttons), we force the in-call buttons to update,        // to make sure the user sees the *new* current state.        //        // (But note that some toggle buttons may *not* immediately change        // the state of the Phone, in which case the updateInCallTouchUi()        // call here won't have any visible effect.  Instead, those        // buttons will get updated by the updateScreen() call that gets        // triggered when the onPhoneStateChanged() event comes in.)        //        // TODO: updateInCallTouchUi() is overkill here; it would be        // more efficient to update *only* the affected button(s).        // Consider adding API for that.  (This is lo-pri since        // updateInCallTouchUi() is pretty cheap already...)        updateInCallTouchUi();    }


然后调用PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall()); 方法

    /**     * Hang up the ringing call (aka "Don't answer").     */    /* package */ void internalHangupRingingCall() {        if (DBG) log("internalHangupRingingCall()...");        if (VDBG) PhoneUtils.dumpCallManager();        // In the rare case when multiple calls are ringing, the UI policy        // it to always act on the first ringing call.v        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) {            // CDMA: Ringing call and Call waiting hangup is handled differently.            // For Call waiting we DO NOT call the conventional hangup(call) function            // as in CDMA we just want to hungup the Call waiting connection.            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 {                // This should never happen cause hangupRingingCall should always be called                // if the call.isRinging() returns TRUE, which basically means that the call                // should either be in INCOMING or WAITING state                if (DBG) log("No Ringing call to hangup");                return false;            }        } else if ((phoneType == Phone.PHONE_TYPE_GSM)                || (phoneType == Phone.PHONE_TYPE_SIP)) {            // GSM:  Ringing Call and Call waiting, both are hungup by calling            // hangup(call) function.            if (DBG) log("hangup ringing call");            return hangup(ringing);                       } else {            throw new IllegalStateException("Unexpected phone type: " + phoneType);        }    }


    /**     * Trivial wrapper around Call.hangup(), except that we return a     * boolean success code rather than throwing CallStateException on     * failure.     *     * @return true if the call was successfully hung up, or false     *         if the call wasn't actually active.     */    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


    //***** Called from GsmCall    /* 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的挂断电话流程还是挺复杂的,中间要处理很多不同的情景。



更多相关文章

  1. eclipse:运行 Android 项目时出现 “Unable to execute dex: Mult
  2. HorizontalScrollView不显示滚动条,布局完全填充的方法
  3. repo sync同步Android 源代码下载到99%出错
  4. Android数据存储的方法
  5. 【Android Studio快捷键】之代码提示
  6. 在Android命令行启动程序的方法
  7. Android的strings.xml不在代码显示转html原生就支持的部分html语
  8. Android电话和短信拦截器

随机推荐

  1. Android经典应用程序开发
  2. Android各种访问权限Permission详解
  3. Android高手进阶教程(十一)之----Android
  4. Android应用程序的生命周期
  5. Android(安卓)Things:你应该了解的Android
  6. Android(安卓)Relative Layout 安卓相对
  7. Andriod是什么?
  8. android surfaceflinger研究----Surface
  9. android国际化
  10. 《第一行代码--Android》读书笔记之多线