智能电话会议系统(11)---Android 6.0 Phone 多方通话流程

Android 6.0 Phone 多方通话流程

 

写在前面的话

本文主要分析Voice Call多方通话的流程,分析GSM和CDMA在多方通话不同的地方,研究的代码是Android 6.0的,目前只关注framework层。


1. 多方通话的概念

下面引用来自《百度百科》的一段文字:

多方通话的发起流程是:主席方用户A先呼叫参与方用户B,B用户接通呼叫,形成一个典型的两人通话的基本呼叫场景,此后A用户通过终端菜单发起保持B用户呼叫的请求,此时B用户会听到一段背景音乐,并且与A用户的通话暂时断开,主席方A用户紧接着呼叫另一个参与方C用户,在与C用户建立了一个基本通话之后,由A用户发起建立多方通话的业务请求,此时B用户听到的背景音乐将会取消,A、B、C三方加入到一个多方通话中来,他们每个人都可以听到其他两人的话音。其他的四方通话、五方通话的建立过程与此类似。

在明确了多方通话的概念之后,下面我们就来分析多方通话的整个过程吧


本文来自本文来自http://blog.csdn.net/linyongan ,转载请务必注明出处。,转载请务必注明出处。


2. 多方通话的流程(GSM)

GSM

如果图片看不清的话,可以右键选择在新标签中打开图片


多方通话主要分成四个部分:

  1. A呼叫B,B接电话,A与B开始通话。
  2. A再呼叫C,A与B的通话变成HOLDING,C接电话。
  3. 点击“合并”按钮,建立多方通话。
  4. 挂断电话。

2.1 A呼叫B,B接通电话

拨打电话的流程,在Android 5.1 Phone MO(去电)流程分析(应用层) 《 Android 5.1 Phone MO(去电)流程分析(Framework层) 》 里已经详细介绍过了,如果还没有看过这两篇文章的童鞋,建议先看看这两篇文章啦。


2.2 A呼叫C,并且Hold住B

2.2小节又可以细分成三部分:

  1. 用户输入C的号码,点击拨号按钮,流程从应用层一直走到Framework层(即来到GsmCallTracker.java的dial()方法里,步骤2~5)。
  2. 将A与B的通话switch成HOLDING状态。
  3. 调用RIL.java的dial()方法发起RIL_REQUEST_DIAL请求(步骤12~15)。

其中第一部分和第三部分跟上面说的MO(去电)流程是一样的,所以这里重点讲解第二部分(步骤6~11) 
在GsmCallTracker.java的dial()方法里

/* The new call must be assigned to the foreground call.
   That call must be idle, so place anything that's
   there on hold */
if (mForegroundCall.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();
    /* This is a hack to delay DIAL so that it is sent out to RIL only after
       EVENT_SWITCH_RESULT is received. We've seen failures when adding a new call to
       multi-way conference calls due to DIAL being sent out before SWITCH is processed */
    try {
        Thread.sleep(500);
    } catch (InterruptedException e) {
        // do nothing
    }

    /* 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();
}

上面的代码做了两件事: 
1.当发现ForegroundCall正处于ACTIVE状态,用户又要发起DIAL请求,则调用switchWaitingOrHoldingAndActive()方法把当前的ForegroundCall变成Holding状态。 
2.并且让线程休眠500毫秒,目的是延迟DIAL请求的下发,等待RIL上报REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE(但是,即便延迟了500毫秒,但是并不能确保在RIL上报REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE之后才下发DIAL请求)

其实第三部分(步骤12~15)还有些值得注意的地方,就是Call的状态变化。ID为1的Call一直都处于HOLDING状态,而ID为2的Call就从DIALING—->ALERTING—->ACTIVE变化。等待A与C的通话建立,就变成id=1,HOLDING;id=2,ACTIVE状态(步骤15),此时Foreground Call是A与C,Background Call就是A与B,此时B是听不到A与C对话的。


2.3 建立多方通话merge as a conference call

用户点击建立多方通话的按钮之后,流程一直走,从应用层走到PhoneUtils.java的mergeCalls()方法里

    static void mergeCalls(CallManager cm) {
        int phoneType = cm.getFgPhone().getPhoneType();
        if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
            省略...
        } else {
            try {
                log("mergeCalls(): calling cm.conference()...");
                cm.conference(cm.getFirstActiveBgCall());
            } catch (CallStateException ex) {
                Log.w(LOG_TAG, "mergeCalls: caught " + ex, ex);
            }
        }
    }

在这里会对Phone的类型做判断,这里分析的是GSM多方通话流程,所以走进else语句,依次调CallManager.java—->GSMPhone.java—->GsmCallTracker.java的conference()方法(步骤16~19),最后调用RIL.java的conference()方法向RILC发送RIL_REQUEST_CONFERENCE请求建立多方通话(步骤20)。

2.4 挂断电话

对于GSM的话,多方通话中的电话是可以一通一通地挂断的;而CDMA的话,点击挂断按钮,就把所有电话都挂断。


3. 多方通话的流程(CDMA)

CDMA

如果图片看不清的话,可以右键选择在新标签中打开图片


3.1 A呼叫B,B接通电话

同2.1节。


3.2 A呼叫C,并且Hold住B

拨号流程从应用层走到framework层, 
在CdmaCallTracker.javaCdmaCallTracker.java的dial 方法里的流程就有点不一样了(步骤5

Connection dial (String dialString, int clirMode) throws CallStateException { 
     ...
     // The new call must be assigned to the foreground call.
     // That call must be idle, so place anything that's
     // there on hold
     if (mForegroundCall.getState() == CdmaCall.State.ACTIVE) {
         return dialThreeWay(dialString);
     }
     ...
}

private Connection dialThreeWay (String dialString) {
    if (!mForegroundCall.isIdle()) {
        // Check data call
        disableDataCallInEmergencyCall(dialString);
        // Attach the new connection to foregroundCall
        mPendingMO = new CdmaConnection(mPhone,
                                checkForTestEmergencyNumber(dialString), this, mForegroundCall);
        // Some network need a empty flash before sending the normal one
        m3WayCallFlashDelay = mPhone.getContext().getResources()
                    .getInteger(com.android.internal.R.integer.config_cdma_3waycall_flash_delay);
        if (m3WayCallFlashDelay > 0) {
            mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_THREE_WAY_DIAL_BLANK_FLASH));
        } else {
            mCi.sendCDMAFeatureCode(mPendingMO.getAddress(),
                        obtainMessage(EVENT_THREE_WAY_DIAL_L2_RESULT_CDMA));
        }
    return null;
}

如果发现当前ForegroundCall的状态是ACTIVE,则进入dialThreeWay()方法里,然后调用RIL.java的sendCDMAFeatureCode()方法发出RIL_REQUEST_CDMA_FLASH请求(注意,这次拨号并没有发出RIL_REQUEST_DIAL请求,也没有switch A与B那通电话的状态)


3.3 merge call

建立多方通话,从PhoneUtils.java的mergeCallsmergeCalls方法开始

static void mergeCalls(CallManager cm) {
        int phoneType = cm.getFgPhone().getPhoneType();
        if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
            log("mergeCalls(): CDMA...");
            PhoneGlobals app = PhoneGlobals.getInstance();
            if (app.cdmaPhoneCallState.getCurrentCallState()
                    == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
                // Set the Phone Call State to conference
                app.cdmaPhoneCallState.setCurrentCallState(
                        CdmaPhoneCallState.PhoneCallState.CONF_CALL);

                // Send flash cmd
                // TODO: Need to change the call from switchHoldingAndActive to
                // something meaningful as we are not actually trying to swap calls but
                // instead are merging two calls by sending a Flash command.
                log("- sending flash...");
                switchHoldingAndActive(cm.getFirstActiveBgCall());
            }
        }else {
            try {
            log("mergeCalls(): calling cm.conference()...");
                cm.conference(cm.getFirstActiveBgCall());
            } catch (CallStateException ex) {
                Log.w(LOG_TAG, "mergeCalls: caught " + ex, ex);
            }
        }
    }

依次调PhoneUtils.java—->CallManager.java—->CDMAPhone.java的switchHoldingAndActive()方法(步骤14~16)。 
接着调CdmaCallTracker.java的switchWaitingOrHoldingAndActive()方法(步骤17

void switchWaitingOrHoldingAndActive() throws CallStateException {
    // Should we bother with this check?
    if (mRingingCall.getState() == CdmaCall.State.INCOMING) {
        throw new CallStateException("cannot be in the incoming state");
    } else if (mForegroundCall.getConnections().size() > 1) {
        flashAndSetGenericTrue();
    } else {
        // Send a flash command to CDMA network for putting the other party on hold.
        // For CDMA networks which do not support this the user would just hear a beep
        // from the network. For CDMA networks which do support it will put the other
        // party on hold.
        mCi.sendCDMAFeatureCode("", obtainMessage(EVENT_SWITCH_RESULT));
    }
}

最后还是调用RIL.java的sendCDMAFeatureCode()方法发出RIL_REQUEST_CDMA_FLASH请求(步骤20)。(最后把两通电话合并成一通电话?这个问题还需继续深究。根据挂断电话时一次性把所有通话都挂断,可推测只有一通电话)


3.4 挂断电话

对于CDMA的话,与GSM不同。点击挂断按钮,就把所有电话都挂断。


4 扩展的问题

假如多方通话已建立,那么这时候再拨打紧急电话会怎样?紧急电话能拨通吗? 
PS:GSM网络下,iphone5S是把多方通话挂断,然后再拨紧急电话;安卓如果想实现这样的效果,还需自己修改代码。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值