handlePollCalls方法详解

当调用getCurrentCalls方法查询Call List当前所有的通话连接,查询的结果是交给handlePollCalls来处理的。handlePollCalls方法比较长,我们把它分解成几部分来分析:

@Override
protected void handlePollCalls(AsyncResult ar) {
    1.初始化操作,获取Call List。
    2.更新通话的相关信息。
      2.1 获取conn对象和dc对象。
      2.2 通话状态变化。
          2.2.1 出现新的通话连接。
          2.2.2 通话连接断开。
          2.2.3 通话状态发生变化。
    3.收尾处理,根据最新的通话状态发出消息。
    4.更新手机的状态。
}

1.初始化操作,获取Call List。

    //声明Call List对象,它将保存RIL查询出的当前Call对象列表
    List polledCalls;

    //RIL返回的消息无异常,即getCurrentCalls查询当前Call List成功
    if (ar.exception == null) {
        //将RIL返回的消息结果对象强制转换成List对象
        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();
        return;
    }

    Connection newRinging = null; //创建通话连接对象
    Connection newUnknown = null;
    //除了通话连接断开的其他任何通话状态改变的标志
    boolean hasNonHangupStateChanged = false;   // Any change besides
                                                // a dropped connection
    boolean hasAnyCallDisconnected = false;
    //是否需要延迟查询Call List的标志
    boolean needsPollDelay = false;
    //是否出现未知通话连接的标志
    boolean unknownConnectionAppeared = false;

上面代码中把RILJ发给CDMACallTracker的Handler消息中的result通过强制转换成List对象,然后赋值给polledCalls对象的。


http://blog.csdn.net/linyongan


2.更新通话的相关信息。

CDMACallTracker通过Handler消息获取最新的DriverCall List对象列表,然后遍历mConnections保存的通话连接对象列表,通过DriverCall List中对应的DriverCall对象更新CDMACallTracker中的通话相关信息。

2.1 获取conn对象和dc对象。

for (int i = 0, curDC = 0, dcSize = polledCalls.size()
        ; i < mConnections.length; i++) {
    //依次获取connections中的对象
    CdmaConnection conn = mConnections[i];
    DriverCall dc = null;

    // polledCall list is sparse
    if (curDC < dcSize) {
        dc = (DriverCall) polledCalls.get(curDC);
        //DriverCall对象与connections中对象的关系,通过下标对应
        if (dc.index == i+1) {
        //conn与dc对象匹配成功,下次循环则获取下一个dc对象
            curDC++;
     } else {
       //conn与dc对象未匹配成功,则不取出dc对象
       dc = null;
    }
 }

if (DBG_POLL) log("poll: conn[i=" + i + "]=" + conn+", dc=" + dc);

根据下标i的值获取mConnections数组中的CdmaConnection对象conn,并且通过curDC的值从polledCalls列表中获取DriverCall对象dc,这两个对象通过dc.index == i+1完成匹配。(因为index 从1开始,而i从0开始的)


2.2 通话状态变化。

2.2.1 出现新的通话连接。

//conn代表旧的通话连接的基本信息,dc代表新的。出现新的通话连接
if (conn == null && dc != null) {
    //主动发起拨号请求后,第一次查询到Call List后,进入这里
    // Connection appeared in CLCC response that we don't know about
    if (mPendingMO != null && mPendingMO.compareTo(dc)) {

        if (DBG_POLL) log("poll: pendingMO=" + mPendingMO);

        // It's our pending mobile originating call
        mConnections[i] = mPendingMO;
        //把i的值赋值给index
        mPendingMO.mIndex = i;
        mPendingMO.update(dc);
        mPendingMO = null;

        // Someone has already asked to hangup this call
        if (mHangupPendingMO) {
            mHangupPendingMO = false;
            // Re-start Ecm timer when an uncompleted emergency call ends
            if (mIsEcmTimerCanceled) {
                handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
            }

            try {
                if (Phone.DEBUG_PHONE) log(
                        "poll: hangupPendingMO, hangup conn " + i);
                hangup(mConnections[i]);
            } catch (CallStateException ex) {
                Rlog.e(LOG_TAG, "unexpected error on hangup");
            }

            // Do not continue processing this poll
            // Wait for hangup and repoll
            return;
        }
    } else {//接收到新来电
        if (Phone.DEBUG_PHONE) {
            log("pendingMo=" + mPendingMO + ", dc=" + dc);
        }
        //通过dc创建新的Connection对象
        mConnections[i] = new CdmaConnection(mPhone.getContext(), dc, this, i);

        Connection hoConnection = getHoConnection(dc);
        if (hoConnection != null) {
            // Single Radio Voice Call Continuity (SRVCC) completed
            mConnections[i].migrateFrom(hoConnection);
            // Updating connect time for silent redial cases (ex: Calls are transferred
            // from DIALING/ALERTING/INCOMING/WAITING to ACTIVE)
            if (hoConnection.mPreHandoverState != CdmaCall.State.ACTIVE &&
                    hoConnection.mPreHandoverState != CdmaCall.State.HOLDING) {
                mConnections[i].onConnectedInOrOut();
            }

            mHandoverConnections.remove(hoConnection);
            mPhone.notifyHandoverStateChanged(mConnections[i]);
        } else {
            // find if the MT call is a new ring or unknown connection
            //把mConnections对象赋值给newRinging 
            newRinging = checkMtFindNewRinging(dc,i);
            if (newRinging == null) {
                unknownConnectionAppeared = true;
                newUnknown = mConnections[i];
            }
        }
        checkAndEnableDataCallAfterEmergencyCallDropped();
    }
    hasNonHangupStateChanged = true;
}

2.2.2 通话连接断开。

else if (conn != null && dc == null) {
    //通话连接断开
    // This case means the RIL has no more active call anymore and
    // we need to clean up the foregroundCall and ringingCall.
    // Loop through foreground call connections as
    // it contains the known logical connections.
    int count = mForegroundCall.mConnections.size();
    for (int n = 0; n < count; n++) {
        if (Phone.DEBUG_PHONE) log("adding fgCall cn " + n + " to droppedDuringPoll");
        CdmaConnection cn = (CdmaConnection)mForegroundCall.mConnections.get(n);
        //把旧的conn对象添加到删除通话连接列表中  
        mDroppedDuringPoll.add(cn);
    }
    count = mRingingCall.mConnections.size();
    // Loop through ringing call connections as
    // it may contain the known logical connections.
    for (int n = 0; n < count; n++) {
        if (Phone.DEBUG_PHONE) log("adding rgCall cn " + n + " to droppedDuringPoll");
        CdmaConnection cn = (CdmaConnection)mRingingCall.mConnections.get(n);
        mDroppedDuringPoll.add(cn);
    }
    mForegroundCall.setGeneric(false);
    mRingingCall.setGeneric(false);

    // Re-start Ecm timer when the connected emergency call ends
    if (mIsEcmTimerCanceled) {
        handleEcmTimer(CDMAPhone.RESTART_ECM_TIMER);
    }
    // If emergency call is not going through while dialing
    checkAndEnableDataCallAfterEmergencyCallDropped();

    // Dropped connections are removed from the CallTracker
    // list but kept in the Call list
    //设置旧的通话连接为null
    mConnections[i] = null;
} 

2.2.3 通话状态发生变化。

else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
    //通话状态发生变化
    // Call collision case
    if (conn.isIncoming() != dc.isMT) {
        if (dc.isMT == true){
            // Mt call takes precedence than Mo,drops Mo
            mDroppedDuringPoll.add(conn);
            // find if the MT call is a new ring or unknown connection
            newRinging = checkMtFindNewRinging(dc,i);
            if (newRinging == null) {
                unknownConnectionAppeared = true;
                newUnknown = conn;
            }
            checkAndEnableDataCallAfterEmergencyCallDropped();
        } else {
            // Call info stored in conn is not consistent with the call info from dc.
            // We should follow the rule of MT calls taking precedence over MO calls
            // when there is conflict, so here we drop the call info from dc and
            // continue to use the call info from conn, and only take a log.
            Rlog.e(LOG_TAG,"Error in RIL, Phantom call appeared " + dc);
        }
    } else {
        boolean changed;
        //通过dc对象更新conn对象,返回通话状态是否已经更新的标志
        changed = conn.update(dc);
        hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
    }
}

3.收尾处理,根据最新的通话状态发出消息。

//如果接收到来电请求
if (newRinging != null) {
    mPhone.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 = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
    CdmaConnection conn = mDroppedDuringPoll.get(i);

    if (conn.isIncoming() && conn.getConnectTime() == 0) {
        // Missed or rejected call
        int cause;
        if (conn.mCause == DisconnectCause.LOCAL) {
            cause = DisconnectCause.INCOMING_REJECTED;
        } else {
            cause = DisconnectCause.INCOMING_MISSED;
        }

        if (Phone.DEBUG_PHONE) {
            log("missed/rejected call, conn.cause=" + conn.mCause);
            log("setting cause to " + cause);
        }
        mDroppedDuringPoll.remove(i);
        hasAnyCallDisconnected |= conn.onDisconnect(cause);
    } else if (conn.mCause == DisconnectCause.LOCAL
            || conn.mCause == DisconnectCause.INVALID_NUMBER) {
        mDroppedDuringPoll.remove(i);
        hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
    }
}

/* Disconnect any pending Handover connections */
for (Iterator<Connection> it = mHandoverConnections.iterator();
        it.hasNext();) {
    Connection hoConnection = it.next();
    log("handlePollCalls - disconnect hoConn= " + hoConnection);
    ((ImsPhoneConnection)hoConnection).onDisconnect(DisconnectCause.NOT_VALID);
    it.remove();
}

//(mDroppedDuringPoll中仍然存在通话连接消失的对象,也就是还有远程断开通话连接对象
// Any non-local disconnects: determine cause
if (mDroppedDuringPoll.size() > 0) {
    //向RIL层查询最后一次通话连接断开的原因
    mCi.getLastCallFailCause(
        obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
}

if (needsPollDelay) {
    pollCallsAfterDelay();
}

// 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 || hasAnyCallDisconnected) {
    internalClearDisconnected();
}
//同步更新Phone.State手机状态
updatePhoneState();
//出现未知的通话连接
if (unknownConnectionAppeared) {
    mPhone.notifyUnknownConnection(newUnknown);
}
//非通话连接断开的情况或者是接收到新的来电请求
if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
    mPhone.notifyPreciseCallStateChanged();
}

4.更新手机的状态。

 private void updatePhoneState() {
    //首先记录之前的状态
    PhoneConstants.State oldState = mState;
    //根据三个mState状态,更新Phone.State手机状态
    if (mRingingCall.isRinging()) {//来电状态
        mState = PhoneConstants.State.RINGING;
    } else if (mPendingMO != null ||
            !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
        mState = PhoneConstants.State.OFFHOOK;//摘机状态
    } else {
        mState = PhoneConstants.State.IDLE;//待机状态
    }

    //如果通话状态转变为待机状态,则发起通知,设置数据连接可用
    if (mState == PhoneConstants.State.IDLE && oldState != mState) {
        mVoiceCallEndedRegistrants.notifyRegistrants(
            new AsyncResult(null, null, null));
    }
    //如果通话状态转变为非待机状态,则发起通知,设置数据连接不可用
    else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
        mVoiceCallStartedRegistrants.notifyRegistrants (
                new AsyncResult(null, null, null));
    }
    if (Phone.DEBUG_PHONE) {
        log("update phone state, old=" + oldState + " new="+ mState);
    }
    //手机状态已经更新,通过Phone对象发出状态变化通知
    if (mState != oldState) {
        mPhone.notifyPhoneStateChanged();
    }
}

通过Call对象获取其State状态从而更新Phone.State状态,它们之间的状态是保持同步的。
Call.State和Phone.State之间的状态转换过程如下:
这里写图片描述
可以看到Call.State共有9个状态,Phone.State共有3个状态;Call.State中,除去IDLE和INCOMING之外的7个状态,提炼成Phone.State的OFFHOOK。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值