android呼叫流程源码分析

2. ICS Android 4.2 呼叫流程

2.1 呼叫请求

2.1.1 拨号请求(Contact

我们从拨号盘开始分析呼叫流程。我们知道,输入一个号码,点击Call按钮,就开始触发了一个呼叫,然后将进行号码检查、判断,网络状态检查等工作,代码逐步向下调用,经过telephonyril,再通过AT指令,让modem完成信道请求、连接和相应信令处理。

 

1)阶段一

在代码里,DialpadFragment的click将被执行,根据按钮id,执行拨号功能,并调用dialButtonPressed,

                  case R.id.dialButton: {

                if (mNeedCheckSetting) {

                    // Retrieve the haptic feedback setting.

                    mHaptic.checkSystemSetting();

                    mNeedCheckSetting = false;

                }

 

                mHaptic.vibrate();  // Vibrate here too, just like we do for the regular keys

                Profiler.trace(Profiler.DialpadFragmentEnterClick);

                dialButtonPressed();

                Profiler.trace(Profiler.DialpadFragmentLeaveClick);

                return;

 

dialButtonPressed再调用dialButtonPressedInner,主要工作是创建一个intent,并调用doCallOptionHandle开始呼叫相关的处理。

    protected void dialButtonPressedInner(String number, int type) {

                final Intent intent = ContactsUtils.getCallIntent(number,

                        (getActivity() instanceof DialtactsActivity ?

                                ((DialtactsActivity) getActivity()).getCallOrigin() : null), type);

                mCallOptionHandler.doCallOptionHandle(intent);

                mClearDigitsOnStop = true;

…     

 

下面是函数调用栈验证分析过程:

DialpadFragment.dialButtonPressedInner(String, int) line: 2647

DialpadFragment.dialButtonPressed() line: 2634

DialpadFragment.onClick(View) line: 1531

 

一些重要的数据表现形式:

number "12344" (id=830022532488)   //拨出的号码

cachedString "tel:12344" (id=830020302720) //uri里面的号码表述

mAction "android.intent.action.CALL_PRIVILEGED" (id=830016739648) //intentaction

 

 

2)阶段二

mCallOptionHandler 是ContactsCallOptionHandler的引用,其方法doCallOptionHandle主要是获取ITelephony服务,并调用父类CallOptionHandler的方法doCallOptionHandle,这里有2个变量非常重要,mCallOptionHandlerList和mSuccessor。

          public void doCallOptionHandle(Context activityContext, Context applicationContext, Intent intent,

                                   CallOptionBaseHandler.ICallOptionResultHandle resultHandler,

                                   CellConnMgr cellConnMgr, ITelephony telephonyInterface,

                                   boolean isMultipleSim, boolean is3GSwitchSupport) {

        ListIterator<CallOptionBaseHandler> iterator = mCallOptionHandlerList.listIterator();

        CallOptionBaseHandler previousHandler = iterator.next();

        while (iterator.hasNext()) {

            CallOptionBaseHandler currentHandler = (CallOptionBaseHandler)iterator.next();

            previousHandler.setSuccessor(currentHandler);

            previousHandler = currentHandler;

        }

 

        Request request = new Request(activityContext, applicationContext, intent, resultHandler,

                                      cellConnMgr, telephonyInterface, isMultipleSim, is3GSwitchSupport,

                                      mCallOptionHandlerFactory);

        mCallOptionHandlerList.getFirst().handleRequest(request);

    }

 

mCallOptionHandlerList是CallOptionBaseHandler类型的LinkedList,它存放了9个呼叫处理类,每个MO呼叫要依次被这9个呼叫处理类处理,主要完成的功能是号码处理、不同呼叫类型的处理和网络状态相关的处理(详细流程可自行分析)。要实现这样的功能,上面的doCallOptionHandle做了一个循环,通过setSuccessor将下一个处理类作为上一个类的mSuccessor,这样在上一个处理类完成之后,调用mSuccessor就可以执行到下一个类,类似于函数指针。

   public CallOptionHandler(CallOptionHandlerFactory callOptionHandlerFactory) {

        mCallOptionHandlerFactory = callOptionHandlerFactory;

        mCallOptionHandlerList = new LinkedList<CallOptionBaseHandler>();

 

        mCallOptionHandlerList.add(callOptionHandlerFactory.getFirstCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getInternetCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getVideoCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getSimSelectionCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getSimStatusCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getVoiceMailCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getInternationalCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getIpCallOptionHandler());

        mCallOptionHandlerList.add(callOptionHandlerFactory.getFinalCallOptionHandler());

    }

 

第一个执行的类是ContactsFirstCallOptionHandler,之后通过下面的方式跳转到下一个处理类,

         if (null != mSuccessor) {

            mSuccessor.handleRequest(request);

        }

 

再执行到ContactsSimStatusCallOptionHandler的时候,handleRequest根据条件是否需要检查sim状态,发送了一个消息MESSAGE_CHECK_SIM_STATUS, 

    public void handleRequest(final Request request) {

        int slot = request.getIntent().getIntExtra(Constants.EXTRA_SLOT_ID, -1);

            if (needToCheckSIMStatus(slot)) 

                   mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_CHECK_SIM_STATUS, slot, 0));

…      

 

其自身的handleMessage处理了这个消息,处理网络状态(通过handleCellConn(),再使用服务IPhoneStatesMgrService的方法verifyPhoneState),并在这里结束了这一阶段的处理过程。可以看到我们的9个处理类还并没有执行完。

        public void handleMessage(Message msg) {

            switch (msg.what) {

                case MESSAGE_CHECK_SIM_STATUS:

                    final int result = mRequest.getCellConnMgr().handleCellConn(msg.arg1,

                            CellConnMgr.REQUEST_TYPE_ROAMING, mRunnable);

                    log("result = " + result);

                    if (result == mRequest.getCellConnMgr().RESULT_WAIT) {

                        showProgressIndication(mRequest);

                    }

                    break;      

 

 

下面是函数调用栈验证分析过程:

ContactsSimStatusCallOptionHandler(SimStatusCallOptionHandler).handleRequest(Request) line: 119

ContactsSimSelectionCallOptionHandler(SimSelectionCallOptionHandler).onMakeCall(SimSelectionCallOptionHandler$CallbackArgs) line: 432

ContactsSimSelectionCallOptionHandler(SimSelectionCallOptionHandler).handleRequest(Request) line: 279

ContactsVideoCallOptionHandler(VideoCallOptionHandler).handleRequest(Request) line: 64

ContactsInternetCallOptionHandler(InternetCallOptionHandler).handleRequest(Request) line: 64

ContactsFirstCallOptionHandler(FirstCallOptionHandler).handleRequest(Request) line: 73

ContactsFirstCallOptionHandler.handleRequest(Request) line: 58

ContactsCallOptionHandler(CallOptionHandler).doCallOptionHandle(Context, Context, Intent, CallOptionBaseHandler$ICallOptionResultHandle, CellConnMgr, ITelephony, boolean, boolean) line: 54

ContactsCallOptionHandler.doCallOptionHandle(Intent) line: 43

DialpadFragment.dialButtonPressedInner(String, int) line: 2670

DialpadFragment.dialButtonPressed() line: 2634

DialpadFragment.onClick(View) line: 1531

 

Tips:】对于MTK平台,调试会有些文件找不到,如ContactsCallOptionHandler,可在.classpath中加入一条记录

<classpathentry kind="src" path="packages/apps/Phone/common/src"/>

 

3)阶段三

在这个阶段,我们需要找到拨号产生的intent如何向下传递的。

 

SimStatusCallOptionHandler的mRunnable将会被执行到,它处理了sim状态之后,又调用到mSuccessor。

    private Runnable mRunnable = new Runnable() {

        public void run() {

            final int result = mRequest.getCellConnMgr().getResult();

            final int slot = mRequest.getCellConnMgr().getPreferSlot();

            log("run, result = " + result + " slot = " + slot);

 

            dismissProgressIndication();

            if (result != com.mediatek.CellConnService.CellConnMgr.RESULT_STATE_NORMAL) {

                mRequest.getResultHandler().onHandlingFinish();

            } else {

                int oldSolt = mRequest.getIntent().getIntExtra(Constants.EXTRA_SLOT_ID, -1);

                log("afterCheckSIMStatus, oldSolt = " + oldSolt);

                if (oldSolt != -1 && slot != oldSolt) {

                    mRequest.getIntent().putExtra(Constants.EXTRA_SLOT_ID, slot);

                }

                //mRequest.getResultHandler().onContinueCallProcess(mRequest.getIntent());

                if (null != mSuccessor) {

                    mSuccessor.handleRequest(mRequest);

                }

            }

        }

    };      

 

这样又走回mCallOptionHandlerList所设计的串行迭代处理逻辑。后面将继续执行VoiceMailCallOptionHandler、InternationalCallOptionHandler、IpCallOptionHandler和FinalCallOptionHandler这几个类的判断处理过程。在FinalCallOptionHandler里面handleRequest结束了串行处理过程,

    public void handleRequest(final Request request) {

        log("handleRequest()");

        request.getResultHandler().onContinueCallProcess(request.getIntent());

    }      

 

最后执行到ContactsCallOptionHandler的onContinueCallProcess,这里就通过sendBroadcast将intent广播出去了,并通过setClassName设置了接收类为com.mediatek.phone.OutgoingCallReceiver,到这里就完成了拨号盘拨号请求阶段的任务。

    public void onContinueCallProcess(Intent intent) {

        /** M: Ensure the Dialogs be dismissed before launch a new "call" @{ */

        dismissDialogs();

        /** @} */

        intent.setAction(Constants.OUTGOING_CALL_RECEIVER);

        intent.setClassName(Constants.PHONE_PACKAGE, Constants.OUTGOING_CALL_RECEIVER);

        ContactsApplication.getInstance().sendBroadcast(intent);

    }      

 

下面是函数调用栈验证分析过程:

ContactsCallOptionHandler.onContinueCallProcess(Intent) line: 59

FinalCallOptionHandler.handleRequest(Request) line: 47

ContactsIpCallOptionHandler(IpCallOptionHandler).handleRequest(Request) line: 91

ContactsInternationalCallOptionHandler(InternationalCallOptionHandler).handleRequest(Request) line: 308

ContactsVoiceMailCallOptionHandler(VoiceMailCallOptionHandler).handleRequest(Request) line: 66

SimStatusCallOptionHandler$2.run() line: 94

 

 

2.1.2 拨号请求(Phone

拨号盘里的拨号过程是在contact进程实现的,当它发出拨号广播后,就被Phone进程的com.mediatek.phone.OutgoingCallReceiver接收到,OutgoingCallReceiver的onReceive收到广播事件后,又发送一个ACTION_NEW_OUTGOING_CALL类型的广播,它自己能接收并处理这个广播,并调用callController.placeCall进行下一步的呼叫处理。

 

    public void onReceive(Context context, Intent intent) {

        if (Constants.OUTGOING_CALL_RECEIVER.equals(intent.getAction())) { //收到contact的广播

            ///Profiler.trace(Profiler.OutgoingCallReceiverEnterActionOnReceive);

            Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);

 

            //String number = PhoneNumberUtils.getNumberFromIntent(intent, context);

            String number = CallOptionUtils.getInitialNumber(context, intent);

            OutgoingCallBroadcaster.sendNewCallBroadcast(context, intent, number, false, this);//转发出去

        } else if (Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())) {//自己接收

                PhoneGlobals.getInstance().callController.placeCall(newIntent);//调用placeCall

            }

      …

 

callController的placeCall完成3部分的工作,1)检查intent的数据并调用placeCallInternal,向底层发起呼叫请求,2)处理底层的返回结果 3)启动通话界面

    public void placeCall(Intent intent) {

              CallStatusCode status = placeCallInternal(intent);

        switch (status) {

            // Call was placed successfully:

            case SUCCESS:

            case EXITED_ECM:

        mApp.displayCallScreen(!intent.getBooleanExtra(Constants.EXTRA_IS_VIDEO_CALL, false), forPlaceCall);

    }

 

placeCallInternal对intent和当前手机状态进行了一些检查,看是是紧急拨号、是否允许拨号等,在进一步向下调用PhoneUtils.placeCallGemini。

                callStatus = PhoneUtils.placeCallGemini(mApp,

                                                        phone,

                                                        number,

                                                        contactUri,

                                                        (isEmergencyNumber || isEmergencyIntent),

                                                        inCallUiState.providerGatewayUri,

                                                        slot);      

 

placeCallGemini则进一步调用GeminiRegister.dial,GeminiRegister.dial 调用((MTKCallManager) callManager).dialGemini,其中MTKCallManager是MTK的封装类,最终会调用的CallManager的dial,在CallManager.dial中调用了basePhone.dial(dialString),basePhone实际上是一个GSMPhone的实例,所以最终调用下面的方法。其中mCT则是GsmCallTracker的实例,它在GSMPhone创建的时候被创建,GSMPhone通过它来完成呼叫相关的处理。

    public Connection

    dial (String dialString, UUSInfo uusInfo) throws CallStateException {

        GsmMmiCode mmi = GsmMmiCode.newFromDialString(networkPortion, this, mUiccApplication.get());

        if (LOCAL_DEBUG) Cclog("dialing w/ mmi '" + mmi + "'...");

        //MTK-END [mtk04070][111118][ALPS00093395]Add Cclog

 

        if (mmi == null) {

            return mCT.dial(newDialString, uusInfo);

        } else if (mmi.isTemporaryModeCLIR()) {

            return mCT.dial(mmi.dialingNumber, mmi.getCLIRMode(), uusInfo);

        } else {

            mPendingMMIs.add(mmi);

            mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));

            mmi.processCode();

 

            // FIXME should this return null or something else?

            return null;

        }

    }      

 

在GsmCallTracker的dial方法中,会先将音频通道mute,再进行cm.dial拨号,之后再将状态信息更新到应用。其中cm是RIL.java的实例。这里还封装了一个事件为EVENT_DIAL_CALL_RESULT消息,但RIL层响应拨号请求后,被自身的handler处理。(clearDisconnected()canDial()清空过去的非连接状态的Connections,然后检查是否可以拨打电话。接着检查foregroundCall是否处于Active状态,若是则调用switchWaitingOrHoldingAndActive将它们切换到后台,调用fakeHoldForegroundBeforeDial将前台中的连接全部切换到后台,并且状态变为HOLDING。在进行这些前期检查和准备后,创建一个GsmConnection实例即pendingMO,检查传递过来的电话号码是否有效合法,若不合法则调用pollCallsWhenSafe(),目的是将其标为dropped;若合法则设置为非静音后,调用RIL.dial进行拨号。最后,更新Phone状态并通知给注册者。

   synchronized Connection

    dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {

        clearDisconnected();

            setMute(false);

                if (PhoneNumberUtils.isEmergencyNumber(dialString)

                    cm.emergencyDial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));

                } else {

                    cm.dial(ret.toString(), clirMode, uusInfo, obtainCompleteMessage(EVENT_DIAL_CALL_RESULT));

                }

        updatePhoneState();

        phone.notifyPreciseCallStateChanged();

    }      

 

RIL.dial实现如下,封装一个RIL_REQUEST_DIAL类型的消息发送出去,将拨号请求发送给RILD,之后RILD再使用AT指令向modem发送呼叫请求。对于RILD之后的处理我们暂不进行分析了。(AT及modem这部分是feature phone很常规的呼叫流程过程,不属于android的特色代码。)

   public void

    dial(String address, int clirMode, UUSInfo uusInfo, Message result) {

        RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);

 

        rr.mp.writeString(address);

        rr.mp.writeInt(clirMode);

        rr.mp.writeInt(0); // UUS information is absent

 

        if (uusInfo == null) {

            rr.mp.writeInt(0); // UUS information is absent

        } else {

            rr.mp.writeInt(1); // UUS information is present

            rr.mp.writeInt(uusInfo.getType());

            rr.mp.writeInt(uusInfo.getDcs());

            rr.mp.writeByteArray(uusInfo.getUserData());

        }

        send(rr);

    }      

 

这个拨号过程的调用栈如下,

下面是函数调用栈验证分析过程:

RIL.dial(String, int, UUSInfo, Message) line: 1148

GsmCallTracker.dial(String, int, UUSInfo) line: 420

GsmCallTracker.dial(String, UUSInfo) line: 443

GSMPhone.dial(String, UUSInfo) line: 1007

GSMPhone.dial(String) line: 999

CallManager.dial(Phone, String) line: 1382

MTKCallManager.dialGemini(Phone, String, int) line: 147

GeminiRegister.dial(Object, Phone, String, int) line: 964

PhoneUtils.placeCallGemini(Context, Phone, String, Uri, boolean, Uri, int) line: 859

CallController.placeCallInternal(Intent) line: 746

CallController.placeCall(Intent) line: 349

OutgoingCallReceiver.onReceive(Context, Intent) line: 72

LoadedApk$ReceiverDispatcher$Args.run() line: 788

 

 

RIL响应后,会调用processResponse,再调用processSolicited,processSolicited对RIL_REQUEST_DIAL处理比较简单,没有实质性的代码。在最后的代码里面,如下,则对之前封装的EVENT_DIAL_CALL_RESULT消息进行处理,进行回调状态处理。

        if (rr.mResult != null) {

            AsyncResult.forMessage(rr.mResult, ret, null);

            rr.mResult.sendToTarget();

        }      

 

这个回调处理实际由GsmCallTracker的handleMessage完成。

EVENT_DIAL_CALL_RESULT执行的调用栈,

GsmCallTracker.handleMessage(Message) line: 1271

GsmCallTracker(Handler).dispatchMessage(Message) line: 107

 

2.1.3 进入通话界面

由于之前提到的callController的placeCall里启动了呼叫界面,拨号请求完成后,会进入inCallScreenonCreate会被执行,开始构建通话界面。

 

 

在拨号请求后,updatePhoneState会被调用到,最初处理的是phone stateIDLEOFF HOOK的变化,updatePhoneState的主要作用是更新Phone的呼叫状态,并将状态通知。

    private void    updatePhoneState() {

        PhoneConstants.State oldState = state;

 

        if (ringingCall.isRinging()) {

            state = PhoneConstants.State.RINGING;

        } else if (pendingMO != null ||

                !(foregroundCall.isIdle() && backgroundCall.isIdle())) {

            state = PhoneConstants.State.OFFHOOK;

        } else {

            state = PhoneConstants.State.IDLE;

        }

 

        if (state == PhoneConstants.State.IDLE && oldState != state) {

            voiceCallEndedRegistrants.notifyRegistrants(

                new AsyncResult(null, null, null));

        } else if (oldState == PhoneConstants.State.IDLE && oldState != state) {

            voiceCallStartedRegistrants.notifyRegistrants (

                    new AsyncResult(null, null, null));

        }

 

        if (state != oldState) {

            phone.notifyPhoneStateChanged();

        }

    }      

代码里还有两种特殊情况,

如果新状态变为IDLE,则表示通话断开,要发给voiceCallEndedRegistrants里面的注册函数处理,在GsmDataConnectionTracker里通过

p.getCallTracker().registerForVoiceCallEnded (this, DctConstants.EVENT_VOICE_CALL_ENDED, null); 来注册。

如果上一个状态是IDLE,则表示新建立通话,要发给voiceCallStartedRegistrants里面的注册函数处理,在GsmDataConnectionTracker里通过p.getCallTracker().registerForVoiceCallStarted (this, DctConstants.EVENT_VOICE_CALL_STARTED, null);  

 

对于EVENT_VOICE_CALL_ENDED和.EVENT_VOICE_CALL_STARTED 的处理,先通过GsmDataConnectionTracker的handlemessage处理,没有匹配的case,再转交给父类 ,父类调用onVoiceCallEnded和onVoiceCallStarted方法进行处理,这两个方法在父类是抽象函数,实际又调用到了子类的函数。

onVoiceCallStarted

    protected void onVoiceCallStarted() {

        if (DBG) log("onVoiceCallStarted");

 

        if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {

            notifyDataConnection(Phone.REASON_VOICE_CALL_STARTED);            

        }

 

        if (isConnected() && ! mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {

            if (DBG) log("onVoiceCallStarted stop polling");

            stopNetStatPoll();

            stopDataStallAlarm();

        }

    }      

 

onVoiceCallEnded

    protected void onVoiceCallEnded() {

        if (DBG) log("onVoiceCallEnded");

 

        if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {

            notifyDataConnection(Phone.REASON_VOICE_CALL_ENDED);

        }

 

        if (isConnected()) {

            if (!mPhone.getServiceStateTracker().isConcurrentVoiceAndDataAllowed()) {

                startNetStatPoll();

                startDataStallAlarm(DATA_STALL_NOT_SUSPECTED);

            } else {

                // clean slate after call end.

                resetPollStats();

            }

        }

        // reset reconnect timer

        setupDataOnReadyApns(Phone.REASON_VOICE_CALL_ENDED);

    }

 

 

我们重点关注notifyPhoneStateChanged是怎样将信息传递到应用上层的,onVoiceCallEnded和onVoiceCallStarted的过程类似。(使用SIM1拨号,观察log居然发现sim2收到了EVENT_VOICE_CALL_STARTED_PEER事件并进行了处理,待分析。)

1

notifyPhoneStateChanged首先通过DefaultPhoneNotifier的notifyPhoneState,将信息传递给ITelephonyRegistry的notifyCallState,再通过AIDL,将消息传递给TelephonyRegistry的notifyCallState,这里会进行注册函数的遍历,并将状态通知到每个注册的函数,之后还会进行广播。

    public void notifyCallState(int state, String incomingNumber) {

        if (!checkNotifyPermission("notifyCallState()")) {

            return;

        }

        synchronized (mRecords) {

            mCallState = state;

            mCallIncomingNumber = incomingNumber;

            for (Record r : mRecords) {

                if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {

                    try {

                        r.callback.onCallStateChanged(state, incomingNumber); //遍历

                    } catch (RemoteException ex) {

                        mRemoveList.add(r.binder);

                    }

                }

            }

            handleRemoveListLocked();

        }

        broadcastCallStateChanged(state, incomingNumber); //广播

    }

                

2

TelephonyRegistry里遍历的回调函数onCallStateChanged会将消息发给IPhoneStateListener,通过AIDL方法,由PhoneStateListener接收并处理。

 

3

在应用最上层,DialpadFragment.java通过SlotUtils.listenAllSlots间接向TelephonyRegistry注册,

SlotUtils.listenAllSlots(getActivity(), mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE

                | PhoneStateListener.LISTEN_SERVICE_STATE);     

并且,DialpadFragment.java创建PhoneStateListener的实例,并实现了onCallStateChanged方法。

        public void onCallStateChanged(int state, String incomingNumber) {

            if (state == TelephonyManager.CALL_STATE_IDLE) {

 

                final boolean phoneIsInUse = phoneIsInUse();

                if (dialpadChooserVisible()) {

                    if (!phoneIsInUse) {

                        showDialpadChooser(false);

                        adjustListViewLayoutParameters();

                    }

                }

 

                //modified by yanbin.zhang for fr441210 begin 2013-06-11

                if (!phoneIsInUse && !simIsnotInUse()) {

                //modified by yanbin.zhang for fr441210 begin 2013-06-11

                    if (mDigits != null) {

                        mDigits.setHint(null);

                    }

                }

            }

        }   

 

 

在PhoneStateListener的handleMessage处理LISTEN_CALL_STATE时,通过如下代码,调用DialpadFragment.java里的onCallStateChanged方法。

                case LISTEN_CALL_STATE:

                    PhoneStateListener.this.onCallStateChanged(msg.arg1, (String)msg.obj);

                    break;      

 

这样就完成了Phone state的向上传递。

 

2.2 呼叫建立



2.2.1 ECPI

 

RILJ收到呼叫请求并发出相应后,随即收到RILC的事件并进行处理,如下,

       case RIL_UNSOL_CALL_PROGRESS_INFO:

                if (RILJ_LOGD) unsljLogvRet(response, ret);

                if (mCallProgressInfoRegistrants != null) {

                    mCallProgressInfoRegistrants.notifyRegistrants(

                                        new AsyncResult (null, ret, null));

其中mCallProgressInfoRegistrants里面的处理实体是在GsmCallTracker注册完成的,注册函数为cm.registerForCallProgressInfo(this, EVENT_CALL_PROGRESS_INFO, null);,里面生成一个Registrants并添加到mCallProgressInfoRegistrants:mCallProgressInfoRegistrants.add()。

 

这样,EVENT_CALL_PROGRESS_INFO消息就会被传递给GsmCallTracker.java的handleMessage来处理,事件的处理过程在handleCallProgressInfo里面。

 

EVENT_CALL_PROGRESS_INFO携带的消息格式和说明如下,在handleCallProgressInfo有说明。

/* +ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], "",<cause>

          *

          * if msg_type = DISCONNECT_MSG or ALL_CALLS_DISC_MSG,

          * +ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>,,,"",,"",<cause> 

          * 

          * if msg_type = STATE_CHANGE_HELD or STATE_CHANGE_ACTIVE or STATE_CHANGE_DISCONNECTED,

          * +ECPI:<call_id>, <msg_type>,,,,,"",,""

          *

          * if others, 

          * +ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], ""

          * msg_type:

          *     0  O  CSMCC_SETUP_MSG   呼叫建立,用于MT

          *     1  X  CSMCC_DISCONNECT_MSG 呼叫断开

          *     2  O  CSMCC_ALERT_MSG  等待接通提示

          *     3  X  CSMCC_CALL_PROCESS_MSG 呼叫处理

          *     4  X  CSMCC_SYNC_MSG  信息同步

          *     5  X  CSMCC_PROGRESS_MSG  呼叫处理

          *     6  O  CSMCC_CALL_CONNECTED_MSG  呼叫连接

          *   129  X  CSMCC_ALL_CALLS_DISC_MSG  断开所有呼叫

          *   130  O  CSMCC_MO_CALL_ID_ASSIGN_MSG  CALL ID指定

          *   131  O  CSMCC_STATE_CHANGE_HELD   呼叫保持状态

          *   132  O  CSMCC_STATE_CHANGE_ACTIVE  呼叫激活状态

          *   133  O  CSMCC_STATE_CHANGE_DISCONNECTED  呼叫断开 

          *   134  X  CSMCC_STATE_CHANGE_MO_DISCONNECTING  MO主动断开呼叫

          */

 

handleCallProgressInfo,从名字可以看出,它负责处理呼叫处理信息,分析其代码,其处理的呼叫分MOMT两大类,涉及的过程和知识点有呼叫建立和维护的各种状态,处理语音和视频呼叫、多方通话等功能。

 

上面是一个MO呼叫的AT数据,从LOG上可以看到,ATD拨号命令发出后,收到很多ECPI数据,ECPI含义是呼叫处理信息(Call Process information)。可以看到MO呼叫建立时,会依次收到msg_type为130,3,4,2,132,6CPI信息,从前面的描述可以看到,MO呼叫建立过程为,

130  CALL ID指定

3      CSMCC_CALL_PROCESS_MSG 呼叫处理

4    CSMCC_SYNC_MSG  信息同步

2    CSMCC_ALERT_MSG  等待接通提示

132    CSMCC_STATE_CHANGE_ACTIVE  呼叫激活状态

6    CSMCC_CALL_CONNECTED_MSG  呼叫连接

 

1.1.1 ECPI消息处理

1

先分析msg_type为130的处理过程,数据如下:

+ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], "",<cause>

+ECPI: 1,     130,       0,       0    ,0,    0,      "10086",129,""

由于代码量较大,我们只列出LOG表示handleCallProgressInfo代码的执行流程,这需要对照代码分析。LOG分两部分说明,

第一部分:

 

可以看出conn == null, pendingMO != null,Phone State=OFFHOOK,Foreground call,Background call,这些变量对我们分析呼叫的状态十分重要,我们将在其他地方展开详述。

期间调用的处理函数主要为

 

第二部分:

 

可以看出newRinging = null,droppedDuringPoll.size=0hasNonHangupStateChanged=true

期间调用的处理函数主要为internalClearDisconnected,updatePhoneState,phone.notifyPreciseCallStateChanged。

 

internalClearDisconnected主要完成清除ringingCall、foregroundCall、backgroundCall里已挂断电话的connection。这几个呼叫实例是GsmCall类,继承自Call,维护call connection的状态。通过attach将呼叫连接添加到ArrayList里面,再在呼叫过程中,对这些这些连接进行维护。

    private void    internalClearDisconnected() {

        ringingCall.clearDisconnected();

        foregroundCall.clearDisconnected();

        backgroundCall.clearDisconnected();

    }      

 

updatePhoneState的流程分析见上文。

 

notifyPreciseCallStateChanged将state通知到注册到的应用,应用注册是通过PhoneBase 的registerForPreciseCallStateChanged完成的。

对于呼叫部分,在CallManager里的registerForPhoneStates,registerSinglePhoneStates里面进行注册。CallManager再在handler里面处理EVENT_PRECISE_CALL_STATE_CHANGED消息,并将消息通知给mPreciseCallStateRegistrantsGemini和mPreciseCallStateRegistrants

的注册客户。

mPreciseCallStateRegistrantsGemini里存放的实例是通过registerForPreciseCallStateChangedEx注册的,经查找没有发现有客户注册。

mPreciseCallStateRegistrants里存放的实例是通过registerForPreciseCallStateChanged来注册,对于呼叫部分,是InCallScreen.java来注册的。InCallScreen的handleMessage处理PHONE_STATE_CHANGED事件,调用onPhoneStateChanged,更新通话界面和相关状态。

 

2

再分析msg_type为2的处理过程,数据如下:

+ECPI:<call_id>, <msg_type>, <is_ibt>, <is_tch>, <dir>, <call_mode>[, <number>, <toa>], "",<cause>

ECPI: 1,        2,        1,      1,     0,     0,     "10086",129,""

 

msg_type为2State变为ALERTING,会通知到应用,这个状态通知上去之后,界面会变化为等待对方接听的界面(影响界面变化的应该为notifyPreciseCallStateChanged)。

 

期间调用的处理函数主要为internalClearDisconnected,updatePhoneState,phone.notifyPreciseCallStateChanged。

 

3

对于其他类型的消息分析过程类似,不详细说明。

 

1.1.1 界面更新

notifyPreciseCallStateChanged将phone state通知到InCallScreen.java,InCallScreen的handleMessage处理PHONE_STATE_CHANGED事件,调用onPhoneStateChanged,onPhoneStateChanged再调用requestUpdateScreen来更新界面。而onPhoneStateChanged则给mHandler发送REQUEST_UPDATE_SCREEN消息,让mHandler再调用updateScreen来刷新屏幕,

                case REQUEST_UPDATE_SCREEN:

                case REQUEST_UPDATE_SCREEN_EXT:

                    updateScreen();

                    break;   

 

updateScreen负责界面的刷新。主要的刷新函数是mCallCard.updateState(mCM)。

 

1.1 呼叫结束


呼叫主动断开:

 


1.1.1 ECPI消息处理

 

呼叫结束的AT数据如下。

可以看出,需要处理的CPI信息依次如下,

          *   134  X  CSMCC_STATE_CHANGE_MO_DISCONNECTING  MO主动断开呼叫

*    1  X  CSMCC_DISCONNECT_MSG 呼叫断开

          *   129  X  CSMCC_ALL_CALLS_DISC_MSG  断开所有呼叫

          *   133  O  CSMCC_STATE_CHANGE_DISCONNECTED  呼叫断开 

对象这些信息的代码分析就不再赘述了。

 

133的处理log

 

updatePhoneState的时候会调用onVoiceCallEnded。并且sim2会收到一个EVENT_VOICE_CALL_ENDED_PEER事件,并执行onVoiceCallEndedPeer。

 

1.1.1 界面更新

断开连接时,通过onDisconnect来更新界面,调用栈如下:

InCallScreen.updateScreen(boolean) line: 4033

InCallScreen.onDisconnect(AsyncResult, int) line: 3527

InCallScreen.access$400(InCallScreen, AsyncResult, int) line: 178

InCallScreen$1.handleMessage(Message) line: 523

InCallScreen$1(Handler).dispatchMessage(Message) line: 107

 

 

1.1 MT呼叫

1.1.1 接听

ringingCall正处于INCOMING则调用RIL.acceptCall去接听电话;若是WAITING状态,则调用switchWaitingOrHoldingAndActive将其切换到前台。

 

1.1.2 拒接

当ringingCall处于INCOMING时,则调用RIL.rejectCall拒绝;否则抛出异常,表示没有来电却去接听它。

 

1.2 挂断呼叫

它区分是ringingCallforegroundCall还是backgroundCall。若是ringingCall,则调用RIL.hangupWaitingOrBackground;若是foregroundCall,并且是在DIALINGALERTING状态则调用调用hangup (GsmConnection conn)挂断,否则调用hangupForegroundResumeBackground挂断前台通话后恢复后台通话;若是backgroundCallringingCall在响铃,则调用hangupAllConnections挂断所有在backgroundCall的通话连接,否则调用hangupWaitingOrBackground挂断呼叫等待和后台通话。

 

switchWaitingOrHoldingAndActive():进行电话切换
conference():进行电话会议
separate():分离出一路通话
fakeHoldForegroundBeforeDial()将前台电话(ForegroundCall)中的电话连接(GSMConnectionsclone后台后,将这些连接删除,将Foreground置为IDLE状态,将后台电话BackgroundCall置为HOLDING状态。
clearDisconnected():清除已Disconnected的电话连接并更新电话状态,然后通知注册者当前最新的状态。
internalClearDisconnected():将ringingCall、 foregroundCall和 backgroundCall中状态为DISCONNECTED的 connections清除掉,若没有connection则将该GSMCall置为idle状态。
updatePhoneState():更新Phone状态,并向注册者通知语音通话开始或结束。
canDial():检查是否可以拨打电话,只有同时满足下列条件才可以:(1)射频Raido未关闭(2PendingMO这个Connection为空(3RingCall这个GSMCall未处于响铃状态(4)系统没有禁止电话拨打功能(5ForegroundCallBackgroundCall2GSMCall都不处于活动状态。其中当为INCOMING WAITING表示在响铃;当为DIALING 和 ALERTING时 表示在拨号;当不为IDLE 、 DISCONNECTED DISCONNECTING时表示活动状态,即处在上述的响铃、拨号、ACTIVE HOLDING时表示处于活动状态。
canConference():当ForegroundCall处于ActiveBackgroundCall处于HOLDING以及它们的连接数少于5个时则可进行电话会议
canTransfer():当ForegroundCall处于ActiveBackgroundCall处于HOLDING时则可进行交换。
hangup (GsmConnection conn):挂断某路电话连接
hangupWaitingOrBackground():挂断呼叫等待和后台电话
hangupForegroundResumeBackground():挂断前台电话并恢复后台电话
hangupConnectionByIndex(GsmCall call, int index):挂断某个索引指定的前台通话
hangupAllConnections(GsmCall call):挂断所有电话通路

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值