Android call 流程以及其他case整理(4)--Conference Call

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Clear_ws/article/details/82384358

      Conference Call在项目中的场景不是很多,在日常生活应用也不是很多,基本上项目上的需求原生的代码已经足够。我所接触的只是之前在一个STK 运营商项目上时,对Conference call 有很多UI & 功能的要求,所以趁此也做一个总结,本篇主要是对于IMS的情况做个总结。

1.Conference call 基本介绍

    Conference call 就是常说的电话会议,简单的说就是多台设备同时参与某一路通话(假设为A,B,C三台设备),不是常见的两方对话。详细的解释可以查看:https://en.wikipedia.org/wiki/Conference_call. 在多方的参与者中,有一方作为host(A),它是这通呼叫的发起方,可以manager这路conference call(与运营商相关). 其他的参与者(B,C)则叫peercall,这两个参与者没有操作这路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有两通电话就可以了。我本地的做的测试时发起的两通MOcall。这步没有什么特殊的地方,主要的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已经确定下来。

http://androidxref.com/9.0.0_r3/xref/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/Clear_ws/article/details/82384358