2019独角兽企业重金招聘Python工程师标准>>>

Test Step:
接听123456789,
接听123456780,
合并,
123456780挂断,
123456789挂断,

Dialer

当我们接听第二路电话时,会触发到AnswerPresenter的onAnswer()

public void onAnswer(int videoState, Context context) {
......
if (mCall.getSessionModificationState()
== Call.SessionModificationState.RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
......
} else {
Log.d(this, "onAnswer (answerCall) mCallId=" + mCallId + " videoState=" + videoState);
TelecomAdapter.getInstance().answerCall(mCall.getId(), videoState);
}
}

通过判断发现不是升级到video的请求,因此,接听电话。调用TelecomAdapter的answerCall()。

void answerCall(String callId, int videoState) {
android.telecom.Call call = getTelecomCallById(callId);
if (call != null) {
call.answer(videoState);
} else {
......
}
}

继续调android.telecom.Call的方法。

public void answer(int videoState) {
mInCallAdapter.answerCall(mTelecomCallId, videoState);
}

接下来就到了fw的InCallAdapter,它会通知telecom接听指定的call。

public void answerCall(String callId, int videoState) {
try {
mAdapter.answerCall(callId, videoState); // mAdapter为IInCallAdapter对象
} catch (RemoteException e) {
}
}

Telecom

再通过AIDL调用telecom的InCallAdapter。

public void answerCall(String callId, int videoState) {
......
mCallsManager.answerCall(call, videoState);
......
}

然后来到了CallsManager,如果foreground call不是ringing call,且是active或dialing状态,
那么就先将其hold,然后再接听来电。

public void answerCall(Call call, int videoState) {
......
if (foregroundCall != null && foregroundCall != call &&
(foregroundCall.isActive() ||
foregroundCall.getState() == CallState.DIALING ||
foregroundCall.getState() == CallState.PULLING)) {
......
foregroundCall.hold();
......
}
......
call.answer(videoState);
......
}
}

先来看看hold的流程。
com.android.server.telecom.Call.java

void hold() {
......
mConnectionService.hold(this);
......
}

转到ConnectionServiceWrapper进行处理。

void hold(Call call) {
......
mServiceInterface.hold(callId); // mServiceInterface为IConnectionService对象
......
}

Telephony

又通过AIDL调用ConnectionService。
通过Handler机制最终调用ConnectionService的hold()方法。
private final IBinder mBinder = new IConnectionService.Stub() {

public void hold(String callId) {
mHandler.obtainMessage(MSG_HOLD, callId).sendToTarget();
}
}

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
......
case MSG_HOLD:
hold((String) msg.obj);
break;
......
}
}

private void hold(String callId) {
......
findConnectionForAction(callId, "hold").onHold();
......
}

ConnectionService中根据传入的callId找到对应的Connection,然后再调用其onHold()方法。
如果当前call为active,且新的来电不是waiting状态,则hold当前call。

TelephonyConnection

public void onHold() {
performHold();
}

public void performHold() {
......
if (Call.State.ACTIVE == mConnectionState) {
......
if (ringingCall.getState() != Call.State.WAITING) {
phone.switchHoldingAndActive();
}
......
}

接下来就到了GsmCdmaPhone中。

public void switchHoldingAndActive() throws CallStateException {
mCT.switchWaitingOrHoldingAndActive();
}

直接调用GsmCdmaCallTracker。
对于原生代码,走到这里会报异常,因为底层传上来的第二路通话为INCOMING状态。
不过,即使是这样,在AP侧发送ANSWER请求后,再从底层获取call状态时,原来active的call也已经变为hold状态了。

public void switchWaitingOrHoldingAndActive() throws CallStateException {
if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) {
throw new CallStateException("cannot be in the incoming state");
} else {
if (isPhoneTypeGsm()) {
mCi.switchWaitingOrHoldingAndActive(
obtainCompleteMessage(EVENT_SWITCH_RESULT));
} else {
......
}
}

现在我们再次回到CallsManager中来看看answer的流程。

public void answerCall(Call call, int videoState) {
......
if (foregroundCall != null && foregroundCall != call &&
(foregroundCall.isActive() ||
foregroundCall.getState() == CallState.DIALING ||
foregroundCall.getState() == CallState.PULLING)) {
......
foregroundCall.hold();
......
}
......
call.answer(videoState);
......
}
}

没啥处理,继续调用。
com.android.server.telecom.Call.java

public void answer(int videoState) {
......
mConnectionService.answer(this, videoState);
......
}

然后在ConnectionServiceWrapper中会通过AIDL调用到Telephony中。
ConnectionServiceWrapper.java

void answer(Call call, int videoState) {
......
mServiceInterface.answer(callId); // mServiceInterface为IConnectionService对象
......
}

Telephony

这里的流程跟hold差不多,通过Handler机制最终调用ConnectionService的answer()方法。
再根据传入的callId找到对应的Connection,然后再调用其onAnswer()方法。
ConnectionService.java

private final IBinder mBinder = new IConnectionService.Stub() {
public void answer(String callId) {
mHandler.obtainMessage(MSG_ANSWER, callId).sendToTarget();
}
}

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
......
case MSG_ANSWER:
answer((String) msg.obj);
break;
......
}
}

private void answer(String callId) {
Log.d(this, "answer %s", callId);
findConnectionForAction(callId, "answer").onAnswer();
}

TelephonyConnection.java

public void onAnswer(int videoState) {
......
getPhone().acceptCall(videoState);
......
}

接下来又到了GsmCdmaPhone中。如果是IMS的call,则会转到ImsPhone去,我们这里仅看CS call。

public void acceptCall(int videoState) throws CallStateException {
Phone imsPhone = mImsPhone;
if ( imsPhone != null && imsPhone.getRingingCall().isRinging() ) {
imsPhone.acceptCall(videoState);
} else {
mCT.acceptCall();
}
}

进入GsmCdmaCallTracker中处理。
由于第二路来电的状态为INCOMING,所以调用RIL.acceptCall()接听来电。
public void acceptCall() throws CallStateException {
......
if (mRingingCall.getState() == GsmCdmaCall.State.INCOMING) {
......
if (isPhoneTypeGsm() == true) {
mCi.acceptCall(obtainCompleteMessage());
......
}

此时,第一路通话为hold状态,而第二路通话为active状态。
接下来就准备将两路通话合并为Conference了。

Dialer

用户点击合并通话按钮后,会出发CallButtonFragment的onClick()。

public void onClick(View view) {
......
} else if (id == R.id.mergeButton) {
getPresenter().mergeClicked();
......
}

然后再到CallButtonPresenter,会获取TelecomAdapter的实例并调用其merge()方法。
CallButtonPresenter.java

public void mergeClicked() {
TelecomAdapter.getInstance().merge(mCall.getId());
}

在TelecomAdapter中,通过传入的callId获取Call对象,然后再取得可合并为conference的call,进行合并操作。
TelecomAdapter.java

void merge(String callId) {
android.telecom.Call call = getTelecomCallById(callId);
if (call != null) {
List<android.telecom.Call> conferenceable = call.getConferenceableCalls();
if (!conferenceable.isEmpty()) { // 为什么不为空???
call.conference(conferenceable.get(0));
} else {
if (call.getDetails().can(android.telecom.Call.Details.CAPABILITY_MERGE_CONFERENCE)) {
call.mergeConference();
}
}
} else {
Log.e(this, "error merge, call not in call list " + callId);
}
}

android.telecom.Call中直接调用android.telecom.InCallAdapter进行处理。

public void conference(Call callToConferenceWith) {
if (callToConferenceWith != null) {
mInCallAdapter.conference(mTelecomCallId, callToConferenceWith.mTelecomCallId);
}
}

android.telecom.InCallAdapter中通过AIDL调用Telecom的com.android.server.telecom.InCallAdapter,从而进入Telecom的流程。

public void conference(String callId, String otherCallId) {
try {
mAdapter.conference(callId, otherCallId);
} catch (RemoteException ignored) {
}
}

Telecom

通过callId获取Call对象,并调用CallsManager处理。
com.android.server.telecom.InCallAdapter.java

public void conference(String callId, String otherCallId) {
......
Call call = mCallIdMapper.getCall(callId);
Call otherCall = mCallIdMapper.getCall(otherCallId);
if (call != null && otherCall != null) {
mCallsManager.conference(call, otherCall);
......
}

CallsManager并没有啥处理,直接到com.android.server.telecom.Call。
CallsManager.java

public void conference(Call call, Call otherCall) {
call.conferenceWith(otherCall);
}

com.android.server.telecom.Call.java

void conferenceWith(Call otherCall) {
......
mConnectionService.conference(this, otherCall);
......
}

最终在ConnectionServiceWrapper中又通过AIDL调用到ConnectionService的IConnectionService,进入Telephony的流程。
ConnectionServiceWrapper.java

void conference(final Call call, Call otherCall) {
......
mServiceInterface.conference(callId, otherCallId);
......
}

Telephony

在ConnectionService的IConnectionService中,通过Handler机制最终调用ConnectionService的conference()方法。

private final IBinder mBinder = new IConnectionService.Stub() {
public void conference(String callId1, String callId2) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId1;
args.arg2 = callId2;
mHandler.obtainMessage(MSG_CONFERENCE, args).sendToTarget();
}
}

private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
......
case MSG_CONFERENCE: {
......
conference(callId1, callId2);
......
}

private void conference(String callId1, String callId2) {
......
onConference(connection1, connection2);
......
}

在上面的conference()方法中,会先获取2个call的Connection,当2个Connection都不为空时,就调用onConference()方法进行合并。
如果有一个已经是conference,就把另一路合并进来,这种情况暂不考虑。

调用其子类TelephonyConnectionService的onConference()方法。
TelephonyConnectionService.java

public void onConference(Connection connection1, Connection connection2) {
if (connection1 instanceof TelephonyConnection) {
((TelephonyConnection) connection1).performConference(connection2);
} else if (connection2 instanceof TelephonyConnection) {
((TelephonyConnection) connection2).performConference(connection1);
} else {
Log.w(this, "onConference - cannot merge connections " +
"Connection1: %s, Connection2: %2", connection1, connection2);
}
}

到TelephonyConnection进行处理时,并没有用到传入的Connection参数,这是因为
TelephonyConnection.java

public void performConference(Connection otherConnection) {
......
// We dont use the "other" connection because there is no concept of that in the
// implementation of calls inside telephony. Basically, you can "conference" and it
// will conference with the background call. We know that otherConnection is the
// background call because it would never have called setConferenceableConnections()
// otherwise.
getPhone().conference();
......
}

接下来就到了GsmCdmaPhone。如果是ims,会到ImsPhone进行处理,CS的话就直接调用GsmCdmaCallTracker。

public void conference() {
......
if (isPhoneTypeGsm()) {
mCT.conference();
} else {
......
}
}

GsmCdmaCallTracker中也没啥处理,调用RIL发送CONFERENCE请求。
GsmCdmaCallTracker.java

public void conference() {
if (isPhoneTypeGsm()) {
mCi.conference(obtainCompleteMessage(EVENT_CONFERENCE_RESULT));
} else {
......
}
}

接下来是modem返回CONFERENCE请求的响应后的处理,会向modem获取当前的call。

public void handleMessage(Message msg) {
......
case EVENT_CONFERENCE_RESULT:
......
operationComplete();
......
}

private void operationComplete() {
......
mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
mCi.getCurrentCalls(mLastRelevantPoll);
......
}

public void handleMessage(Message msg) {
......
switch (msg.what) {
case EVENT_POLL_CALLS_RESULT:
......
handlePollCalls((AsyncResult)msg.obj);
......
}

处理获取到的call,会和当前维护的connection进行比较,conference的情况下,当前connection和获取到的call是相同的,
所以会用获取到的call对当前connection进行更新。

protected synchronized void handlePollCalls(AsyncResult ar) {
......
for (int i = 0, curDC = 0, dcSize = polledCalls.size()
; i < mConnections.length; i++) {
......
if (conn == null && dc != null) {
......
} else if (conn != null && dc == null) {
......
} else if (conn != null && dc != null && !conn.compareTo(dc) && isPhoneTypeGsm()) {
......
} else if (conn != null && dc != null) { /* implicit conn.compareTo(dc) */
// Call collision case
if (!isPhoneTypeGsm() && conn.isIncoming() != dc.isMT) {
......
} else {
boolean changed;
// 会将之前hold的connection的parent也设置为foreground call
// Call的mState也会设置为ACTIVE
changed = conn.update(dc);
hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
}
}
......
}
......
updatePhoneState(); // 这里面会把mState设置为OFFHOOK状态,除此之外并无其它操作
......
if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
mPhone.notifyPreciseCallStateChanged();
}
}

GsmCdmaPhone.java

public void notifyPreciseCallStateChanged() {
/* we'd love it if this was package-scoped*/
super.notifyPreciseCallStateChangedP();
}

Phone.java

protected void notifyPreciseCallStateChangedP() {
AsyncResult ar = new AsyncResult(null, this, null);
mPreciseCallStateRegistrants.notifyRegistrants(ar);

mNotifier.notifyPreciseCallState(this);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ start

mPreciseCallStateRegistrants.notifyRegistrants(ar)的通知会到TelephonyConnection进行处理。

TelephonyConnection.java

private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PRECISE_CALL_STATE_CHANGED:
Log.v(TelephonyConnection.this, "MSG_PRECISE_CALL_STATE_CHANGED");
updateState();
break;
......
}
}

void updateState() {
if (mOriginalConnection == null) {
return;
}

updateStateInternal();
updateStatusHints();
updateConnectionCapabilities();
updateConnectionProperties();
updateAddress();
updateMultiparty();
}

对于第一路Connection,其状态是从HOLDING变为ACTIVE,所以会调用setActiveInternal()。而第二路Connection则不会,因为其状态未变。

void updateStateInternal() {
......
if (mConnectionState != newState) {
mConnectionState = newState;
switch (newState) {
......
case ACTIVE:
setActiveInternal();
break;
......
}

private void setActiveInternal() {
......
setActive();
}

再调用其父类Connection的setActive()方法。

public final void setActive() {
checkImmutable();
setRingbackRequested(false);
setState(STATE_ACTIVE);
}

设置第一路Connection为ACTIVE状态。

private void setState(int state) {
......
if (mState != state) {
Log.d(this, "setState: %s", stateToString(state));
mState = state; // 第一路connection的状态从此变为ACTIVE
onStateChanged(state); // 设置incall ui的显示
for (Listener l : mListeners) {
l.onStateChanged(this, state);
}
}
}

l.onStateChanged()会触发多个地方的回调,其中一个就是TelephonyConferenceController里面的。
TelephonyConferenceController.java

private final Connection.Listener mConnectionListener = new Connection.Listener() {
@Override
public void onStateChanged(Connection c, int state) {
......
recalculate();
}
}

void recalculate() {
recalculateConference();
recalculateConferenceable(); // 感觉没做啥
}

private void recalculateConference() {
......
for (TelephonyConnection connection : mTelephonyConnections) {
com.android.internal.telephony.Connection radioConnection =
connection.getOriginalConnection();

if (radioConnection != null) {
// 找出call中的connection,以便加入conference中
Call.State state = radioConnection.getState();
Call call = radioConnection.getCall();
if ((state == Call.State.ACTIVE || state == Call.State.HOLDING) &&
(call != null && call.isMultiparty())) {

numGsmConnections++;
conferencedConnections.add(connection);
}
}
}
......
// 至少要有2个connection才能建立conference
if (numGsmConnections < 2) {
......
} else {
if (mTelephonyConference != null) { // 此时还没有建立TelephonyConference对象
......
} else {
if (allConnInService) {
......
// 新建TelephonyConference,并將要合并的Connection加进去
mTelephonyConference = new TelephonyConference(phoneAccountHandle);
for (Connection connection : conferencedConnections) {
Log.d(this, "Adding a connection to a conference call: %s %s",
mTelephonyConference, connection);
mTelephonyConference.addConnection(connection);
}
mConnectionService.addConference(mTelephonyConference);
......
}
if (mTelephonyConference != null) { // 通过PrimaryConnection的状态来设置Conference的状态
Connection conferencedConnection = mTelephonyConference.getPrimaryConnection();
Log.v(this, "Primary Conferenced connection is " + conferencedConnection);
if (conferencedConnection != null) {
switch (conferencedConnection.getState()) {
case Connection.STATE_ACTIVE:
Log.v(this, "Setting conference to active");
mTelephonyConference.setActive();
break;
case Connection.STATE_HOLDING:
Log.v(this, "Setting conference to hold");
mTelephonyConference.setOnHold();
break;
}
}
}
}
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ end

mNotifier.notifyPreciseCallState(this)

DefaultPhoneNotifier.java

public void notifyPreciseCallState(Phone sender) {
......
mRegistry.notifyPreciseCallState(
convertPreciseCallState(ringingCall.getState()),
convertPreciseCallState(foregroundCall.getState()), // foregroundCall为ACTIVE
convertPreciseCallState(backgroundCall.getState()));
......
}

AIDL调用,进入哪个进程???

TelephonyRegistry.java

public void notifyPreciseCallState(int ringingCallState, int foregroundCallState,
int backgroundCallState) {
if (!checkNotifyPermission("notifyPreciseCallState()")) {
return;
}
synchronized (mRecords) {
mRingingCallState = ringingCallState;
mForegroundCallState = foregroundCallState;
mBackgroundCallState = backgroundCallState;
mPreciseCallState = new PreciseCallState(ringingCallState, foregroundCallState,
backgroundCallState,
DisconnectCause.NOT_VALID,
PreciseDisconnectCause.NOT_VALID);
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)) {
try {
r.callback.onPreciseCallStateChanged(mPreciseCallState);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
}
}
handleRemoveListLocked();
}
broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
DisconnectCause.NOT_VALID,
PreciseDisconnectCause.NOT_VALID);
}

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

转载于:https://my.oschina.net/igiantpanda/blog/2222413

[CS] 发起Conference Call相关推荐

  1. AndroidTelephony学习大纲

    2019独角兽企业重金招聘Python工程师标准>>> 移动通信基础知识     3gpp Phone     Phone状态监听     Phone相关类:CallNotifier ...

  2. Conference Call流程介绍

    1.Conference call 基本介绍 Conference call 就是常说的电话会议,简单的说就是多台设备同时参与某一路通话(假设为A,B,C三台设备),不是常见的两方对话.详细的解释可以 ...

  3. Android call 流程以及其他case整理--Conference Call

    Conference Call在项目中的场景不是很多,在日常生活应用也不是很多,基本上项目上的需求原生的代码已经足够.我所接触的只是之前在一个STK 运营商项目上时,对Conference call ...

  4. 【征稿进行时】计算机与智能控制主题征稿,ICCEIC 2020持续征稿中!

    2020年计算机工程与智能控制国际学术会议 ICCEIC 2020 重要信息 大会官网:www.icceic.org/ 大会时间:2020年11月6日-8日 大会地点:中国·重庆 截稿日期:2020年 ...

  5. 【IEEE出版】计算机多主题征稿,ICBASE 2020诚邀您投稿参会!

     2020 年大数据.人工智能与软件工程 国际会议(ICBASE2020) 重要信息 大会官网: http://www.icbase2020.org/ 大会时间:2020年10月23-25日 大会地点 ...

  6. 【CLAA系列】CLAA 通讯过程

    名词: MSP:中兴服务器 CS:客户服务器,也就是我们的服务器 GW:网关,这里默认是中兴的网关 Chip:芯片,这里特指包含了Lora标准通讯模块,且针对CLAA做过特殊优化的芯片. Lora:L ...

  7. 计算机通信与网络安全国际会议,第一届计算机通信与网络安全国际学术会议(CCNS2020)...

    一. 重要信息 会议时间:2020年8月21-23日 会议地点:中国-西安 一轮截稿:2020年6月28日 接受/拒稿通知:投稿后1-2周内 收录检索:EI Compendex, Scopus, In ...

  8. 【学术期刊】2023CCF推荐的A,B,C类英文科技期刊目录最新发布

    中国计算机学会(China Computer Federation )2023推荐国际学术会议和期刊目录正式发布,相较于上一版目录,此次<目录>新增期刊19个,会议19个:升级期刊3个,会 ...

  9. 计算机与信息科学书刊,第五届信息科学、计算机技术与交通运输国际学术会议(ISCTT 2020)...

    大会时间:2020年11月13-15日 大会地点:中国-沈阳 截稿日期:2020年11月9日 接受/拒稿通知:投稿后1-2周内 收录检索:SCI.IEEE Xplore.EI Compendex.Sc ...

最新文章

  1. java判断44数组是否是4阶幻方_2015蓝桥杯决赛Java A组 第二题--四阶幻方
  2. java set泛型_Java 集合二 泛型、Set相关
  3. java arraybound out_java – 获取ArrayIndexOutOfBound:1异常
  4. DataSet.GetBookMark内存泄漏
  5. TIOBE 6 月编程语言排行榜:Perl 成为 Python 过分炒作的牺牲品?
  6. 7号团队-团队任务5:项目总结
  7. Win32汇编——文件操作
  8. Spring事务@Transactional注解原理
  9. rose oracle双机切换故障,oracle 审计引起的问题 (双机软件roseha)
  10. nmap和masscan
  11. 酒店管理系统(功能结构图、流程图)
  12. mac开机启动项怎么设置,苹果电脑开机启动项在哪里设置
  13. 数据库概述05(数据库查询及修改操作)
  14. 在github上写个人简历——最简单却又不容易的内容罗列
  15. Java开发,需要学习什么内容?
  16. 在office中插入特殊符号方框带√
  17. 神经网络机器翻译技术及应用(上)
  18. SystemInit()时钟系统初始化函数解析
  19. android10系统是平板电脑吗,买平板电脑应该选win10还是安卓系统?
  20. 细聊 JavaScript 的事件执行机制

热门文章

  1. SQL批量更新 关系表更新
  2. fft_fft_control
  3. 20150917-html第二次课
  4. 安装不上vc++环境,导致部分游戏和qq不能用的解决方案
  5. 已知一个函数f可以等概率的得到1-5间的随机数,问怎么等概率的得到1-7的随机数...
  6. [转载]Informix平安特征庇护数据的详细方法
  7. 2010年年终“飞”的总结
  8. 【RobotStudio学习笔记】(九)坐标偏移设置
  9. 数据结构笔记(九)-- 单链队列
  10. 考会计中级职称能用计算机,2019年中级会计职称计算机考试操作常见问题答疑...