1. 程式人生 > >Android call 流程以及其他case整理(4)--Conference Call

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

      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通。