Communication - 02.Call U

App层


  从大拇哥Click CallButton开始手机便已明白,主人这是要打电话。当然,你可以选择直接拨号,也可以通过ContactList,或者从通话记录着手。这些都只是UI的设计不同而已,终归都会有一个统一的入口开始Calling。这个汇合点就是:

android:targetActivity="OutgoingCallBroadcaster"

 

  这是一个独立的Activity,你可以设计各种花里胡哨的拨号方式、Activities,而后通过startActivity跨应用访问来开始 OutgoingCallBroadcaster的生命周期。

  CallController.placeCall调用PhoneUtils.placeCall之后,便由app层进入到了framework层。

  可以看出app层没有什么太复杂的逻辑,重点还是OutgoingCallBroadcaster之前的UI发挥。

 

 

Framework层


  作为办实事的framework层,CallManager类 需要知道手机使用的是何种制式的网络,从而调用该制式的类的方法。显然,这里有“工厂方法”的影子。而GsmCallTracker类 调用RIL去发送AT。同时也需要监控Calling的各种状态返回给上层。

public interface Phone
public class PhoneProxy extends Handler implements Phone {};
public abstract class PhoneBase extends Handler implements Phone {} public class GSMPhone extends PhoneBase {}

 

  从上图可以看出,我们的首要目的是让上层用户获得一个XXXPhone。 

 

  PhoneApp类 onCreate方法 调用PhoneFactory类的静态方法 makeDefaultPhones():

  • PhoneFactory
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription);
int phoneType = getPhoneType(networkMode);



if (phoneType == RILConstants.GSM_PHONE) {

    sProxyPhone = new PhoneProxy(new GSMPhone(context,

    sCommandsInterface, sPhoneNotifier));

    Log.i(LOG_TAG, “Creating GSMPhone”);

} else if (phoneType == RILConstants.CDMA_PHONE) {

    sProxyPhone = new PhoneProxy(new CDMAPhone(context,

    sCommandsInterface, sPhoneNotifier));

    Log.i(LOG_TAG, “Creating CDMAPhone”);

}

 

  Android的应用程序可以使用 PhoneFactory.getDefaultPhone 来获得Phone对象,从而进行一些调用操作。

phone = PhoneFactory.getDefaultPhone();    //返回的是代理,因为面向对象中多态特性适用于多种制式的phone

  这样,上层用户便获得了XXXPhone,但却是个代理。

  到此为止,app.mCM.dial()中,直接调用GSMPhone.dial(),而GSMPhone对象将通话能力交给 GsmCallTracker类 管理和维护。

 

 

  •  GsmCallTracker类

  GsmCallTracker类在 GSMPhone的构造函数 中创建。

mCM.setPhoneType(Phone.PHONE_TYPE_GSM);



mCT = new GsmCallTracker(this);

mSST  = new GsmServiceStateTracker (this);

mSMS  = new GsmSMSDispatcher(this);

mIccFileHandler = new SIMFileHandler(this);

mSIMRecords     = new SIMRecords(this);

mDataConnection = new GsmDataConnectionTracker (this);

mSimCard        = new SimCard(this);



if (!unitTestMode) {

    mSimPhoneBookIntManager = new SimPhoneBookInterfaceManager(this);

    mSimSmsIntManager       = new SimSmsInterfaceManager(this);

    mSubInfo                = new PhoneSubInfo(this);

}



mStkService = StkService.getInstance(mCM, mSIMRecords, mContext, (SIMFileHandler)mIccFileHandler, mSimCard);



mCM.registerForAvailable(this, EVENT_RADIO_AVAILABLE, null);

mSIMRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);

mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);

mCM.registerForOn(this, EVENT_RADIO_ON, null);

mCM.setOnUSSD(this, EVENT_USSD, null);

mCM.setOnSuppServiceNotification(this, EVENT_SSN, null);

mSST.registerForNetworkAttach(this, EVENT_REGISTERED_TO_NETWORK, null);

 

  从Tracker可见其与 Handler消息机制 的相关性,而GSMCallTracker在本质上就是一个Handler,那么Tracker与底层的通信机制,也就是与RIL的前端RILJ(ril java)的communication是关键。GsmCallTracker端注册三个EVENT,从而接收并响应RIL对象发出的三种类型的Handler消息。

    //***** Constructors



    GsmCallTracker (GSMPhone phone) {

        this.phone = phone;

        cm = phone.mCM;



        cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);

        cm.registerForOn(this, EVENT_RADIO_AVAILABLE, null);

        cm.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);

    }

 

  handleMessage方法中接收并响应RIL对象发出的 Handler回调消息类型,其中对应以上的三个EVENT:

    case EVENT_CALL_STATE_CHANGE:

        pollCallsWhenSafe();

    break;



    case EVENT_RADIO_AVAILABLE:

        handleRadioAvailable();

    break;



    case EVENT_RADIO_NOT_AVAILABLE:

        handleRadioNotAvailable();

    break;

  

  发现状态变化,最终都会调用cm.getCurrentCalls方法,向RIL对象查询当前Call List。RIL处理完毕,再次向上层给Tracker发送消息。

  handleMessage方法中接收,并调用handlePollCalls,根据Call List当前所有的通话连接完成通话状态的更新。

    case EVENT_POLL_CALLS_RESULT:

        ar = (AsyncResult)msg.obj;



        if (msg == lastRelevantPoll) {

            if (DBG_POLL) log(

                    "handle EVENT_POLL_CALL_RESULT: set needsPoll=F");

            needsPoll = false;

            lastRelevantPoll = null;

            handlePollCalls((AsyncResult)msg.obj);

        }

    break;

   

 

 

  •   通话管理模型

  Modem收到AT指令返回字符串返回值,解析创建 DriverCall对象列表,该列表能够真实反映出Modem无线通信模块中所有通话连接的真实信息!

  GsmConnection对象表示一个通话连接,根据DriverCall的一些基本信息创建,并更新。

  GsmConnection更新的同时同步调用所属的GsmCall对象,并更新相关信息。

 

  在 GSMCallTracker 中维护着通话列表,顺序记录了正连接上的通话状态。

三路电话,每一路默认最大7个连接:
GsmCall ringingCall = new GsmCall(this); GsmCall foregroundCall = new GsmCall(this); GsmCall backgroundCall = new GsmCall(this);

  分为了三个类别进行管理:

RingingCall: INCOMING ,WAITING ForegourndCall: ACTIVE, DIALING ,ALERTING BackgroundCall: HOLDING   

  GSMCallTracker通过GsmConnection与DriverCall之间的比较 从而判断通话连接状况的前后变化,改变通话状态相关信息做出相应调整。

  handlePollCalls((AsyncResult)msg.obj) 解析出最新通话连接状况,与上一次connection状态比较。而后更新通话相关信息,主要是CallTracker对象中的state,  connections,  foregroundCall,  backgroundCall,  ringingCall对象的更新。

 

   OK, 让我们来继续拨号:

    Connection

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

        // note that this triggers call state changed notif

        clearDisconnected();



        if (!canDial()) {

            throw new CallStateException("cannot dial in current state");

        }



        // The new call must be assigned to the foreground call.

        // That call must be idle, so place anything that's

        // there on hold

        if (foregroundCall.getState() == GsmCall.State.ACTIVE) {

            // this will probably be done by the radio anyway

            // but the dial might fail before this happens

            // and we need to make sure the foreground call is clear

            // for the newly dialed connection

            switchWaitingOrHoldingAndActive();



            // Fake local state so that

            // a) foregroundCall is empty for the newly dialed connection

            // b) hasNonHangupStateChanged remains false in the

            // next poll, so that we don't clear a failed dialing call

            fakeHoldForegroundBeforeDial();

        }



        if (foregroundCall.getState() != GsmCall.State.IDLE) {

            //we should have failed in !canDial() above before we get here

            throw new CallStateException("cannot dial in current state");

        }



        // Dialing, prepare to create a connection
pendingMO
= new GsmConnection(phone.getContext(), dialString, this, foregroundCall); hangupPendingMO = false; if (pendingMO.address == null || pendingMO.address.length() == 0 || pendingMO.address.indexOf(PhoneNumberUtils.WILD) >= 0 ) { // Phone number is invalid pendingMO.cause = Connection.DisconnectCause.INVALID_NUMBER; // handlePollCalls() will notice this call not present // and will mark it as dropped. pollCallsWhenSafe(); } else { // Always unmute when initiating a new call setMute(false); // Dialing... goto RIL JAVA
cm.dial(pendingMO.address, clirMode, uusInfo, obtainCompleteMessage()); } updatePhoneState(); phone.notifyPreciseCallStateChanged();
return pendingMO; }

 

 

 

 

RILJ层


   RILJ层,即为RIL的最上层,也就是JAVA编写的部分,主要用于Handler交互。

    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()); } if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr); }

  主要分为两部分:1.构建RILRequest;2. 把它发出去。

    private void

    send(RILRequest rr) {

        Message msg;



        if (mSocket == null) {

            rr.onError(RADIO_NOT_AVAILABLE, null);

            rr.release();

            return;

        }



        msg = mSender.obtainMessage(EVENT_SEND, rr);



        acquireWakeLock();



        msg.sendToTarget();

    }

  

  Target就是 class RILSender。

  RILJ作为RIL的最前端与framework通过handler交流,与下层则通过socket通信。有点“承前启后”的意思。

        @Override public void handleMessage(Message msg) {

            RILRequest rr = (RILRequest)(msg.obj);

            RILRequest req = null;



            switch (msg.what) {

                case EVENT_SEND:

                    /**

                     * mRequestMessagePending++ already happened for every

                     * EVENT_SEND, thus we must make sure

                     * mRequestMessagePending-- happens once and only once

                     */

                    boolean alreadySubtracted = false;

                    try {

                        LocalSocket s;



                        s = mSocket;



                        if (s == null) {

                            rr.onError(RADIO_NOT_AVAILABLE, null);

                            rr.release();

                            if (mRequestMessagesPending > 0)

                                mRequestMessagesPending--;

                            alreadySubtracted = true;

                            return;

                        }



                        synchronized (mRequestsList) {

                            mRequestsList.add(rr);

                            mRequestMessagesWaiting++;

                        }



                        if (mRequestMessagesPending > 0)

                            mRequestMessagesPending--;

                        alreadySubtracted = true;



                        byte[] data;



                        data = rr.mp.marshall();

                        rr.mp.recycle();

                        rr.mp = null;



                        if (data.length > RIL_MAX_COMMAND_BYTES) {

                            throw new RuntimeException(

                                    "Parcel larger than max bytes allowed! "

                                                          + data.length);

                        }



                        // parcel length in big endian

                        dataLength[0] = dataLength[1] = 0;

                        dataLength[2] = (byte)((data.length >> 8) & 0xff);

                        dataLength[3] = (byte)((data.length) & 0xff);



                        //Log.v(LOG_TAG, "writing packet: " + data.length + " bytes");



                        s.getOutputStream().write(dataLength);

                        s.getOutputStream().write(data);

                    }
            ... ... case EVENT_WAKE_LOCK_TIMEOUT:

  最后将数据写入通过LocalSocket连接对象获取输出流。

 

  以上便是Java部分,RIL的重难点在于下面的C部分。简单的阐述,只是针对dial,但对流程的理解多少有点帮助。

  Telephony的架构有太多细节可以探究,有志者可以摸索,“自己动脑丰衣足食”,本人摸索完毕后就不再此赘述了。

 

补充:看到园内一篇介绍开会注意事项的随笔,其中有云:戒条八不要忘记把会议总结发送给与会人

         我这也算是个人的学习总结性随笔,好记性不如烂笔头。

  

HAPPY WEEKEND:-)

 

你可能感兴趣的:(call)