voice call relevant

来源:互联网 发布:nginx发布多个网站 编辑:程序博客网 时间:2024/05/21 17:46

每次收到底层上报的呼叫状态变化时(UNSOL_RESPONSE_CALL_STATE_CHANGED ),都会去查询变化了的具体内容
给modem发的信息是RIL_REQUEST_GET_CALL_LIST.handlePollCalls是用来处理查询之后的结果,分析如下:

handlePollCalls(AsyncResult ar)         List polledCalls;log("****handlepollcalls.....");        if (ar.exception == null) {            polledCalls = (List)ar.result;        } else if (isCommandExceptionRadioNotAvailable(ar.exception)) {            // just a dummy empty ArrayList to cause the loop            // to hang up all the calls            polledCalls = new ArrayList();        } else {            // Radio probably wasn't ready--try again in a bit            // But don't keep polling if the channel is closed            pollCallsAfterDelay(bIsVoice);            return;        }        Connection newRinging = null; //or waiting        boolean hasNonHangupStateChanged = false;   // Any change besides                                                    // a dropped connection        boolean needsPollDelay = false;        boolean unknownConnectionAppeared = false;boolean isSeparateCallConnection = false;        for (int i = 0, curDC = 0, dcSize = polledCalls.size()                ; i < connections.length; i++) {            GsmConnection conn = connections[i];//一个个查询gsmcalltracker里面的连接            DriverCall dc = null;            // polledCall list is sparse            if (curDC < dcSize) {                dc = (DriverCall) polledCalls.get(curDC);                if (dc.index == i+1) {                    curDC++;                } else if(isSeparateCallConnection) {    curDC++;    isSeparateCallConnection = false;} else {                    dc = null;                }            }            if (DBG_POLL) log("poll: conn[i=" + i + "]=" +                    conn+", dc=" + dc);    log("****poll: conn[i=" + i + "]=" +                    conn+", dc=" + dc);            if (conn == null && dc != null) {//进来一个新的呼叫                // Connection appeared in CLCC response that we don't know about                if (pendingMO != null && pendingMO.compareTo(dc)) {                    if (DBG_POLL) log("poll: pendingMO=" + pendingMO);                    // It's our pending mobile originating call                    connections[i] = pendingMO;                    pendingMO.index = i;                    pendingMO.update(dc);                    pendingMO = null;                    // Someone has already asked to hangup this call                    if (hangupPendingMO) {                        hangupPendingMO = false;                        try {                            if (Phone.DEBUG_PHONE) log(                                    "poll: hangupPendingMO, hangup conn " + i);                            hangup(connections[i]);                        } catch (CallStateException ex) {                            Log.e(LOG_TAG, "unexpected error on hangup");                        }                        // Do not continue processing this poll                        // Wait for hangup and repoll                        return;                    }                } else {                    connections[i] = new GsmConnection(phone.getContext(), dc, this, i);    log("****setting connection");                    // it's a ringing call                    if (connections[i].getCall() == ringingCall) {log("****new ringing connection");                        newRinging = connections[i];                    } else {                        // Something strange happened: a call appeared                        // which is neither a ringing call or one we created.                        // Either we've crashed and re-attached to an existing                        // call, or something else (eg, SIM) initiated the call.                        Log.i(LOG_TAG,"Phantom call appeared " + dc);                        // If it's a connected call, set the connect time so that                        // it's non-zero.  It may not be accurate, but at least                        // it won't appear as a Missed Call.                        if (dc.state != DriverCall.State.ALERTING                                && dc.state != DriverCall.State.DIALING) {                            connections[i].connectTime = System.currentTimeMillis();                        }                        unknownConnectionAppeared = true;                    }                }                <span style="color:#000099;"><strong>hasNonHangupStateChanged = true;</strong></span>            } else if (conn != null && dc == null) {//modem呼叫断开时走这                // Connection missing in CLCC response that we were                // tracking.log("****no connection let's drop");if(conn.isVoice != bIsVoice) {    log("Different call came 1");    continue;}                droppedDuringPoll.add(conn);                // Dropped connections are removed from the CallTracker                // list but kept in the GsmCall list                connections[i] = null;            } else if (conn != null && dc != null && !conn.compareTo(dc)) {                // Connection in CLCC response does not match what                // we were tracking. Assume dropped call and new calllog("****connection not matching let's drop");if(conn.isVoice != dc.isVoice) {    log("Different call came 2");    curDC--;    isSeparateCallConnection = true;    continue;}                    droppedDuringPoll.add(conn);                connections[i] = new GsmConnection (phone.getContext(), dc, this, i);                if (connections[i].getCall() == ringingCall) {                    newRinging = connections[i];                } // else something strange happened                hasNonHangupStateChanged = true;            } else if (conn != null && dc != null) { /* implicit conn.compareTo(dc)有连接且查询到的call也存在,正常外呼走这 */                boolean changed;log("****conn dc both has value");                changed = conn.update(dc);   //尝试更新连接,如果返回的值位changed,则表明查询前后call的状态有变化                hasNonHangupStateChanged = hasNonHangupStateChanged || changed;log("****hasNonHangupStateChanged = "+hasNonHangupStateChanged);            }            if (REPEAT_POLLING) {                if (dc != null) {                    // FIXME with RIL, we should not need this anymore                    if ((dc.state == DriverCall.State.DIALING                            /*&& cm.getOption(cm.OPTION_POLL_DIALING)*/)                        || (dc.state == DriverCall.State.ALERTING                            /*&& cm.getOption(cm.OPTION_POLL_ALERTING)*/)                        || (dc.state == DriverCall.State.INCOMING                            /*&& cm.getOption(cm.OPTION_POLL_INCOMING)*/)                        || (dc.state == DriverCall.State.WAITING                            /*&& cm.getOption(cm.OPTION_POLL_WAITING)*/)                    ) {                        // Sometimes there's no unsolicited notification                        // for state transitions                        bIsVoice = dc.isVoice;                        needsPollDelay = true;                    }                }            }        }        // This is the first poll after an ATD.        // We expect the pending call to appear in the list        // If it does not, we land here        if (pendingMO != null) {            Log.d(LOG_TAG,"Pending MO dropped before poll fg state:"                            + foregroundCall.getState());            droppedDuringPoll.add(pendingMO);            pendingMO = null;            hangupPendingMO = false;        }        if (newRinging != null) {//如果是新进来的呼叫            phone.notifyNewRingingConnection(newRinging);        }        /* clear the "local hangup" and "missed/rejected call"         cases from the "dropped during poll" list         These cases need no "last call fail" reason .清除掉一些因本地挂机,以及网络拒绝的原因相关的连接       */                for (int i = droppedDuringPoll.size() - 1; i >= 0 ; i--) { //本地原因引起的挂断</span>            GsmConnection conn = droppedDuringPoll.get(i);    log("****welcome to drop call");            if (conn.isIncoming() && conn.getConnectTime() == 0) {                // Missed or rejected call                Connection.DisconnectCause cause;                if (conn.cause == Connection.DisconnectCause.LOCAL) {//进来的call不接听                    cause = Connection.DisconnectCause.INCOMING_REJECTED;                } else {                    cause = Connection.DisconnectCause.INCOMING_MISSED;                }                if (Phone.DEBUG_PHONE) {                    log("missed/rejected call, conn.cause=" + conn.cause);                    log("setting cause to " + cause);                }                droppedDuringPoll.remove(i);                conn.onDisconnect(cause);            } else if (conn.cause == Connection.DisconnectCause.LOCAL) {//主动挂断                // Local hanguplog("****local hangup");                droppedDuringPoll.remove(i);                conn.onDisconnect(Connection.DisconnectCause.LOCAL);            } else if (conn.cause ==                Connection.DisconnectCause.INVALID_NUMBER) {//拨出的号码无效log("****invalid hangup");                droppedDuringPoll.remove(i);                conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER);            }        }        // Any non-local disconnects: determine cause如果有非本地原因要清除的连接        if (droppedDuringPoll.size() > 0) {            //bIsVoice = droppedDuringPoll.get(0).isVoice();    log("****dropping call bIsVoice = "+bIsVoice);    if(bIsVoice) {                cm.getLastCallFailCause(//先查找上次失败的原因                    obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));            } else {                cm.getLastVTCallFailCause(        obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));    }        }        if (needsPollDelay) {    log("****needspolldelay");            pollCallsAfterDelay(bIsVoice);        }        // Cases when we can no longer keep disconnected Connection's新进来的呼叫        // with their previous calls        // 1) the phone has started to ring        // 2) A Call/Connection object has changed state...        //    we may have switched or held or answered (but not hung up)        if (newRinging != null || hasNonHangupStateChanged) {    log("****newringing and hasnonhangupstatechanged");            internalClearDisconnected();        }        updatePhoneState();//跟新gsmphone的状态        if (unknownConnectionAppeared) {    log("****unknown connection appeared");            phone.notifyUnknownConnection();        }        if (hasNonHangupStateChanged || newRinging != null) {    log("****hasnonhangupstatechanged and newringing");            phone.notifyPreciseCallStateChanged();        }        //dumpState();    }



GsmCallTracker.java里面维护了三个call对象

    GsmCall ringingCall = new GsmCall(this);
    // A call that is ringing or (call) waiting
    GsmCall foregroundCall = new GsmCall(this);
    GsmCall backgroundCall = new GsmCall(this);

call可以有如下几种状态:

IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING

的但是不是每一个call都可以拥有上面所有的状态.具体一个call对象可以拥有哪些状态可以查看parentFromDcstate函数

private GsmCall
663    parentFromDCState (DriverCall.State state) {
664        switch (state) {
665            case ACTIVE:
666            case DIALING:
667            case ALERTING:
668                return owner.foregroundCall;foreground call can have ACTIVE/DIALING/ALERTING three states
669            //break;
670 
671            case HOLDING:
672                return owner.backgroundCall;
673            //break;
674 
675            case INCOMING:
676            case WAITING:
677                return owner.ringingCall;
678            //break;
679 
680            default:
681                throw new RuntimeException("illegal call state: " + state);
682        }
683    }
684 

三个call对象的介绍如下

ringingCall(用来管理处于INCOMINGWAITING状态的通话)//刚进来的电话且初始化gsmconnection 时,call的状态会变成INCOMING

foregroundCall(用来管理处于DAILINGALERTINGACTIVE状态的通话)

backgroundCall(用来管理HOLD的通话)。

往外打的电话可以有DAILING,ALERTING,ACTIVE 三种状态

打进来的电话可以有INCOMING,WAITING,ACTIVE 三种状态

往modem发了挂断命令时call的状态变为DISCONNECTING(正在断开)。


而手机的状态只有下面三种:IDLE, RINGING, OFFHOOK; 每一种状态的解释Android代码里直接有介绍如下:

    /**
     * The phone state. One of the following:<p>
     * <ul>
     * <li>IDLE = no phone activity</li>
     * <li>RINGING = a phone call is ringing or call waiting.
     *  In the later case, another call is active as well 有电话在在打进来或者等待接听</li>
     * <li>OFFHOOK = The phone is off hook. At least one call
     * exists that is dialing, active or holding and no calls are
     * ringing or waiting. 有正在打出去的电话 or 已经接通了的call or 处于hold状态的call</li>
     * </ul>
     */



一个call对象里面有一个连接数组

 public ArrayList<Connection> connections = new ArrayList<Connection>();

GsmCallTracker里面也有一个connections数组,to record the call list .and in GsmConnection ,it have a member type  GsmCall parent.

every time poll current call, the connections arry will updata it's state .主要包括把parent在三种类型的call之间切换(根据返回的DriverCall.state来选择parent应该是ringing or foreground or background call),如果call的类型没切换,可以是变化了call 的状态.或者DriverCall.state和查询前一样,返回false

所以一个backgroundCall里面其实可以保存多个通话连接,只是暂时挂起。再比如,在开始呼叫时,gsmcalltracker会初始化一个gsmconnection

        isIncoming = false;        isVoice = ct.isMOVoice;        createTime = System.currentTimeMillis();        this.parent = parent;//这里的parent就是call。且是foregroundCall。相反呼进来的电话是ringincall        parent.attachFake(this, GsmCall.State.DIALING);//将这个connection加入到call的connection数组成员中,且将parent的状态设置为DIALING


gsmcalltracker的构造函数里有

cm.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);

所以当ril.java每次接到底层上报的UNSOL_RESPONSE_CALL_STATE_CHANGED 时,就会给gsmcalltracker 发EVENT_CALL_STATE_CHANGE事件

这个事件的处理就是初始化查询当前的call的状态

    protected void pollCallsWhenSafe() {        needsPoll = true;        if (checkNoOperationsPending()) {            lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);//当查询完call list的状态后返回到calltracker的事件            cm.getCurrentCalls(lastRelevantPoll);//调用ril实例查询call的状态        }    }

查询完后给gsmcalltracker发的事件EVENT_POLL_CALLS_RESULT,其处理就是 handlePollCalls函数。

挂断电话时分不同的call对象,如果电话已经接通了则RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND

如果是挂断正在打进来的电话则用RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND


对于MT voice call

在PhoneBase.java文件里有

 mCM.setOnCallRing(this, EVENT_CALL_RING, null);

当ril接到UNSOL_CALL_RING 时,就会报告EVENT_CALL_RING这个事件给GsmPhone 。其处理为notifyIncomingRing

    private void notifyIncomingRing() {        if (!mIsVoiceCapable)            return;        AsyncResult ar = new AsyncResult(null, this, null);        mIncomingRingRegistrants.notifyRegistrants(ar);//通知注册过次事件的模块    }

在CallManager.java里有 

phone.registerForIncomingRing(mHandler, EVENT_INCOMING_RING, null);

在callmanager里的处理也是通知注册次事件的模块

                case EVENT_INCOMING_RING:                    if (VDBG) Log.d(LOG_TAG, " handleMessage (EVENT_INCOMING_RING)");                    // The event may come from RIL who's not aware of an ongoing fg call                    if (!hasActiveFgCall()) {                        mIncomingRingRegistrants.notifyRegistrants((AsyncResult) msg.obj);                    }

InCallScreen.java里有

mCM.registerForIncomingRing(mHandler, PHONE_INCOMING_RING, null);
mCM是CallManager的实例

上层如果接听了,则会调用gsmphone里的acceptCall-->gamcalltracker.acceptCall函数来发RIL_REQUEST_ANSWER给modem




0 0
原创粉丝点击