Conference Call流程介绍

1.Conference call 基本介绍

    Conference call 就是常说的电话会议,简单的说就是多台设备同时参与某一路通话(假设为A,B,C三台设备),不是常见的两方对话。详细的解释可以查看:https://en.wikipedia.org/wiki/Conference_call. 在多方的参与者中,有一方作为host(A),它是这通呼叫的发起方,可以manager这路conference call(与运营商相关). 其他的参与者(B,C)则叫peer call,这两个参与者没有操作这路conference call的能力。

    Conference call 业务是依赖与运营商的,有些运营商是不支持conference call的,在国内暂时只有CMCC支持conference call。发起Conference call有两种方式:1.将两通call merge成conference。这种方式是最为常见的,A拨打一通电话给B,等B接通后,A再拨打一通电话给C,C再接通后,A就可以将这两通电话merge成一通。2.直接拨打conference。这种方式比较少见,应该是只有在ims的情况下才支持(此处不是很确定,在非IMS的情况下我没有见过能够直接拨打conference。A直接输入B&C的号码直接发起conference call.

     在非IMS Conference call 最多包含6个参与者(包括host),当增加到6方通话后,则不允许继续添加参与者了,但是IMS conference 就没有限制。在GSM情况下,几个参与者组成了一通conference call,host 可以管理这路电话,可以将某个参与者踢出(split)这通conference call. 但是在CDMA情况下,host就没有这个能力了。在注册了IMS的情况下,拨打了一个conference call后(A,B,C),会创建一个conference room,然后A,B,C会依次加入这个conferece room中。在A,B,C均加入这个conference room后,及时非host主动挂断电话(C挂断,AB仍然在通过中),此时AB的通话仍然是conference call.

   Conference call成功后,peer call端(B,C)会在InCallUI上显示host(A)的号码,但是在A端只会显示"Conference call"而不会显示B&C的号码。如果有些运营商有特殊要求(SKT),则可以通过解析sip消息来获取B&C的号码。

2.Conference call 命令下发

   对于conference call,常见的发起方式就是第一种:将两通call merge成conference。下面主要也是解释该流程。Conference call 发起主要可以看作两步:发起两通call & merge两通call.

    发起两通call

发起两通call的流程,没有特殊的流程,不管是MO或是MT都是可以的。只要A有两通电话就可以了。我本地的做的测试时发起的两通MO call。这步没有什么特殊的地方,主要的log逻辑如下:


对于A来说现在已经有了两通MO call了,其中一通call为holding,另外一通为active.

    merge两通call

    如果A手机的simcard开通了conference call的业务,那么在InCallUI上就会有merge的button.点击这个merge button,就可以将这两通单独的call merge成conference。

   点击mergebutton,CallButtonPresenter.java# mergeClicked会相应该click事件。

      packages/apps/Dialer/java/com/android/incallui/CallButtonPresenter.java
      @Override
      public void mergeClicked() {
        Logger.get(context)
            .logCallImpression(
                DialerImpression.Type.IN_CALL_MERGE_BUTTON_PRESSED,
                call.getUniqueCallId(),
                call.getTimeAddedMs());
        TelecomAdapter.getInstance().merge(call.getId());
      }
 packages/apps/Dialer/java/com/android/incallui/call/TelecomAdapter.java
      public void merge(String callId) {
        android.telecom.Call call = getTelecomCallById(callId);
        if (call != null) {
          List<android.telecom.Call> conferenceable = call.getConferenceableCalls();
        /**
         * Returns the list of {@code Call}s with which this {@code Call} is allowed to
         * conference.
         * @ conferenceable.isEmpty The list of conferenceable {@code Call}s.
         */
          if (!conferenceable.isEmpty()) {
            // conferenceable.get(0) --> 被merge的那通call
            call.conference(conferenceable.get(0));
            // It's safe to clear restrict count for merge action.
            DialerCall.clearRestrictedCount();
          } else {
            if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
              call.mergeConference();
              // It's safe to clear restrict count for merge action.
              DialerCall.clearRestrictedCount();
            }
          }
        } else {
          LogUtil.e("TelecomAdapter.merge", "call not in call list " + callId);
        }

此处要注意的是在执行mergeClicked时,往外传递了一个参数call.getId(),这个Call是当前active 的DialerCall对象。然后在TelecomAdapter通过这个CallId来获取android.telecom.Call对象,并进行merge操作。后续的操作比较简单,没有特别的地方与dial方法类似类似,通过IInCallAdapter来操作Call对象.大概的传递如下:

Call.java # conference --> InCallAdapter.java # conference

--> InCallAdapter.java # conference --> CallsManager.java # conference --> Call.java # conferenceWith

从代码可以到,当执行到这里时,两通需要merge的call已经确定下来。

/packages/services/Telecomm/src/com/android/server/telecom/Call.java
void conferenceWith(Call otherCall) {
            if (mConnectionService == null) {
                Log.w(this, "conference requested on a call without a connection service.");
            } else {
                Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall);
                mConnectionService.conference(this, otherCall);
            }
        }

后续的调用逻辑没有太复杂也没有写特别需要注意的地方,与MO call类似,大概的调用逻辑如下:

ConnectionServiceWrapper.java # conference --> ConnectionServiceWrapper.java # conference
--> ConnectionService.java # conference # MSG_CONFERENCE # conference # onConference
--> TelephonyConnectionService.java # onConference # performConference
--> TelephonyConnection.java # performConference
--> IMSphone.java # conference()
--> ImsPhoneCallTracker.java # conference
--> ImsCall.java # merge
--> ImsCallSession.java # merge()
--> ImsCallSessionImpl.java # merge
--> ImsServiceClassTracker.java # sendConferenceRequest
--> ImsServiceSub.java # sendConferenceRequest
--> ImsSenderRxr.java # processSolicited

由于我是用的product产品打的log,所以这块log不是很多,主要的log逻辑如下:

在此处需要重点注意的是,当ImsSenderRxr下发了CONFERENCE后,Modem后面会上报三通Call.从log中也可以看出,前两通就是之前分别A打给B,A打给C的电话,属于normalcall,但是第三通call是重新创建的一通Conference call.


3.Conference call 过程

在第二步中,Tele已经成功的将conference 命令发给Modem,在等待Modem返回前,上层需要做一些准备,从而来为接下来modem的返回做准备。这块代码没有很复杂,基本都是在vendor/qcom下面实现的,主要通过UNSOL_RESPONSE_CALL_STATE_CHANGED 和 UNSOL_REFRESH_CONF_INFO 来推动的。下面通过log来分步解释下业务逻辑:

 1.modem告知当前两通call 状态发生变化,然后让上层开始处理这些变化,基本没有太多的逻辑变化,与正常的通话相同,有需  注意一点的是此时两通call的状态都是holding状态。

 2.modem再次告知call 状态发生变化。此处需要注意的是,此时多了一通call,而且是一通conference call.然后为这通conference call创建对于的callSession。

                                                                 

3.当为第三通call创建了对应的callsession等完毕后,除了第三通call是一通conf外,与其他两通call暂时没有区别。需要注意的是,虽然此时modem上报了三通call,但第三通call并没有传递到上层,即在UI上并没有暂时还没有显示这通call.                              

4.如开头所说,ims conference call会创建一个conference room.然后会将多方通话依次加到conference room.从下面的log中可以看到,这次已经将第一个和第二个参与者加入到conference room 中。而且merge的信息中可以看到参与者的号码(我在log避免号码泄露,将自己的号码改成了xxxx).

5.当一通电话的参与者已经进入了conference room,那么就没有必要再去维护第一通电话了。所以第一通电话就会被end。

6.第二通电话参与者加入到conference room,加入成功后会将第二通电话end掉。至此,所有参与者都已经成功的加入了conference room中了,所以modem已经处理完毕了,返回response.


 

4.处理Modem response

当modem告知conference 成功后,modem返回个conference 的response.此处处理与正常流程相同。其最终的处理逻辑在
vendor/qcom/proprietary/telephony-apps/ims/src/org/codeaurora/ims/ImsServiceClassTracker.java

/**
         * We have detected that a initial conference call has been fully configured. The internal
         * state of both {@code ImsCall} objects need to be cleaned up to reflect the new state.
         * This function should only be called in the context of the merge host to simplify logic
         *
         */
        private void processMergeComplete() {
     
            synchronized(ImsCall.this) {
                if (isMultiparty()) {
                    ...             
                } else {                
                    ...
                    if (isSessionAlive(mSession) && !isSessionAlive(mMergePeer.getCallSession())) {            
                     ...
                    } else if (!isSessionAlive(mSession) &&
                                    isSessionAlive(mMergePeer.getCallSession())) {
                     ...
                    } else {
                        // Handles case 1 explained in callSessionMergeComplete
                        // The transient session stays with us and the disconnect sound should not be
                        // played when we ripple up the disconnect for the merge peer because it was
                        // only disconnected to be added to the conference.
                        finalHostCall = this;
                        finalPeerCall = mMergePeer;
                        mMergePeer.markCallAsMerged(false);
                        swapRequired = false;
                        setIsMerged(false);
                        mMergePeer.setIsMerged(true);
                        if (CONF_DBG) {
                            logi("processMergeComplete :: transient will stay with us (I'm the host).");
                        }
                    }
     
                    if (CONF_DBG) {
                        logi("processMergeComplete :: call=" + finalHostCall + " is the final host");
                    }
     
                    // Add the transient session to the ImsCall that ended up being the host for the
                    // conference.
                    finalHostCall.setTransientSessionAsPrimary(transientConferenceSession);
                }
                ...
            }
            if (listener != null) {
                try {
                    // finalPeerCall will have the participant that was not merged and
                    // it will be held state
                    // if peer was merged successfully, finalPeerCall will be null
                    listener.onCallMerged(finalHostCall, finalPeerCall, swapRequired);
                } catch (Throwable t) {
                    loge("processMergeComplete :: ", t);
                }
                if (mConferenceParticipants != null && !mConferenceParticipants.isEmpty()) {
                    try {
                        listener.onConferenceParticipantsStateChanged(finalHostCall,
                                mConferenceParticipants);
                    } catch (Throwable t) {
                        loge("processMergeComplete :: ", t);
                    }
                }
            }
            return;
        }

1.高通的IMS模块将这些conference 的信息传递给Tele-FW,IMSCall处理上报的信息,确认Host,更改multiparty状态等,主要log如下:

2.依次为参与者创建conference的connection,并以此connection创建对应的call对象。    

                                             
 3.后面两个参与者创建方式与第一次相同

                                                            
4.将新创建的三个call设置成conference call的child.

5.创建对应的DialerCall,并且end掉之前的两通(A-B,A-C)Dialer。

 至此,Conference 的整个过程就全部完成了。


 5.总结--各种Call对象的转换

   此篇分析完全是根据log & 代码来看的,肯定有些错误的地方,后期发现会来更改。我对conference call的理解,比较混乱就是各个call对象,而且各种转换,以至于到最后在Dialer里面有4个call对象。所以单独把这快拎出来总结下:

*在A拨号给B时,属于正常的拨号,所以都是正常的一个;
*在A拨号给C时,也是正常的拨号,所以都是正常的两个;


*在3.2时,由于已经发起了conference,modem会上报一个conference call.这通conferencall 暂时是一通不带任何信息的call.        * 其实可以把这通conference call想象成一通可接入的room,后期merge的过程,是各个参与者(A,B,C)分别接入这个room中,可以想象成A,B,C分别拨给conference room;


*在3.5 & 3.5时,A ,B,C分别merge了,所以ims需要维护的就越来越少,最终只需要维护一通conferen 就够了。
*在4.2时,分别通过participant 的connection 创建了对应的三通Telecom call ,而且此时还未end 之前A->B & A->C的call.        
*在4.5时,整体merge完成,ims 就一通conference call,其他都是4通call.这4通call可以理解成:conference call 1通,A 拨给conference room 的1通,B拨给conference room 的1通,C拨给conference room 的1通。

猜你喜欢

转载自blog.csdn.net/ZhongGuoRenMei/article/details/105724558