[CS] 发起Conference Call

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值