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