Call(通话)相关的内容也是属于Telephony模块,Call整体上可以分成两类:
1. CS call,其中CS全称是Circuit Switch,我们平常打电话走的就是CS的流程。
2. IMS PS call,其中PS全称是Packet Switch,走IMS流程的Call有4类,分别是VoLTE(voice over LTE),ViLTE(video over LTE),VoWiFi(voice over wifi),ViWiFi(video over wifi)。
在分析具体的MT(Mobile Termination Call 被叫)/MO(Mobile Origination Call 主叫)流程之前,需要了解更多Call相关的基础知识,日后才可以更好地理解各种通话的流程。
而本文要讲解的就是Call.java对象。在Android源码中一共有5个“Call.java”文件,所以理清它们之间的联系以及学会如何分辨不同的Call对象,这也是分析通话流程的前提之一。
我们需要重点关注的有4个:
1. /packages/apps/Dialer/InCallUI/src/com/android/incallui/Call.java
2. /packages/services/Telecomm/src/com/android/server/telecom/Call.java
3. /frameworks/base/telecomm/java/android/telecom/Call.java
4. /frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java
整个通话流程可以细分成Dialer(InCallUI),Telecomm Framework,Telecomm Service,Telephony Service,Telephony Framework这5个模块,除了Telephony Service,其他4个模块都有一个自己的Call.java,可想而知,整个通话的过程都会伴随着对Call对象的处理。
本文来自 http://blog.csdn.net/linyongan ,转载请务必注明出处。
在Android N中,InCallUI已经被放置到Dialer目录之下,也许是为了更好地提醒开发者,要编译InCallUI的话,直接编译Dialer就行了。
对于InCallUI Call,
/packages/apps/Dialer/InCallUI/src/com/android/incallui/Call.java
public class Call {
它只是一个普通的类,没有父类也没有实现接口。
从它的构造方法来看,InCallUI Call是直接依赖Telecom Call(Telecom Framework中的Call)的
public Call(android.telecom.Call telecomCall) {
mTelecomCall = telecomCall;
mId = ID_PREFIX + Integer.toString(sIdCounter++);
//依据Telecom Call来更新自己的信息
updateFromTelecomCall();
mTelecomCall.registerCallback(mTelecomCallCallback);
mTimeAddedMs = System.currentTimeMillis();
}
InCallUI Call中的State、Phone Account、ChildNumber等信息都来源于Telecom Call。
InCallUI Call被CallList管理着,CallList中有一个Map来存储InCallUI Call
//String存储的是Call ID
private final HashMap mCallById = new HashMap<>();
InCallUI Call是在CallList的onCallAdded()方法被调用的时候创建的
public void onCallAdded(final android.telecom.Call telecomCall) {
final Call call = new Call(telecomCall);
Log.d(this, "onCallAdded: callState=" + call.getState());
if (call.getState() == Call.State.INCOMING ||
call.getState() == Call.State.CALL_WAITING) {
onIncoming(call, call.getCannedSmsResponses());
} else {
onUpdate(call);
}
}
CallList还依据Call的状态,将InCallUI Call进行分类,对外提供不同类型的Call对象
所以通过CallList获取到的Call对象都是InCallUI Call。
总结:在Dialer(InCallUI)模块中会见到两种Call对象,基本都是InCallUI Call;另外一种Call就是Telecom Call(Telecom Framework中的Call),Telecom Call会很明显地写成android.telecom.Call
,所以也是很好地分辨的。
Telecom Call,
/frameworks/base/telecomm/java/android/telecom/Call.java
public final class Call {
它是一个被定义成final类型的类,它是在Phone.java (framework\base\telecomm\java\android\telecom) 的internalAddCall()方法中被创建的
private final List mCalls = new CopyOnWriteArrayList<>();
final void internalAddCall(ParcelableCall parcelableCall) {
//从ParcelableCall中取出信息用于new Telecom Call
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
parcelableCall.getState());
mCallByTelecomCallId.put(parcelableCall.getId(), call);
//把Telecom Call加入List集合中
mCalls.add(call);
checkCallTree(parcelableCall);
call.internalUpdate(parcelableCall, mCallByTelecomCallId);
fireCallAdded(call);
}
而ParcelableCall是一个中间者的角色,在InCallController.java中先将Telecom Service中的Call转换成ParcelableCall
ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall
(call,videoProviderChanged /* includeVideoProvider */,
mCallsManager.getPhoneAccountRegistrar());
然后再把ParcelableCall转换成Telecom Call,这样子就实现了Telecom Service Call向Telecom Call的转变。
Telecom Service中的Call,
/packages/services/Telecomm/src/com/android/server/telecom/Call.java
public class Call implements CreateConnectionResponse {
它实现了CreateConnectionResponse接口,说明它也负责Connection创建之后的一些事情的处理,比如成功创建Connection之后就需要通知UI界面刷新以及状态的更新。
Telecom Service Call是通话流程中最重要的Call对象,它拥有管理一通电话的能力(answer,reject,hold,disconnect等等),它由CallsManager创建和管理。
在通话过程中,CallsManager是这样管理Call的:
不管是MO/MT Call,都是在创建Telecom Service Call之后调用它自身的startCreateConnection()方法来发起创建Connection的动作,以及成功创建Connection之后通知UI刷新界面的处理。
Telecom Service Call每执行一项重要的动作都会输出相应的标志性log
public void answer(int videoState) {
Preconditions.checkNotNull(mConnectionService);
if (isRinging("answer")) {
mConnectionService.answer(this, videoState);
//打印log
Log.event(this, Log.Events.REQUEST_ACCEPT);
}
}
log如下,以”Event: Call “为关键字:
14:16:35.776 I/Telecom (24667): Event: Call 8: REQUEST_ACCEPT, null
Telecom Service Call的状态信息源自Telephony Framework Call的State,大致流程是这样的:
1. 先由Connection获取到Telephony Framework Call的State,
@Connection.java
public Call.State getState() {
Call c;
c = getCall();
if (c == null) {
return Call.State.IDLE;
} else {
return c.getState();
}
}
2.接着将Connection State转换成CallState
@Call.java(/packages/services/Telecomm/)
static int getStateFromConnectionState(int state) {
switch (state) {
case Connection.STATE_INITIALIZING:
return CallState.CONNECTING;
case Connection.STATE_ACTIVE:
return CallState.ACTIVE;
case Connection.STATE_DIALING:
return CallState.DIALING;
case Connection.STATE_DISCONNECTED:
return CallState.DISCONNECTED;
case Connection.STATE_HOLDING:
return CallState.ON_HOLD;
case Connection.STATE_NEW:
return CallState.NEW;
case Connection.STATE_RINGING:
return CallState.RINGING;
}
return CallState.DISCONNECTED;
}
3.最后由CallsManager调用setCallState()方法把Call的状态set到对应的Telecom Service Call中。
总结:这4个Call的状态转换过程就是这样子的:
Telephony Framework Call—>Telecom Service Call—>Telecom Call—> InCallUI Call.
Telephony Framework中的Call,
/frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java
public abstract class Call {
它是一个抽象类,它的继承关系如下:
我们需要关注的是GsmCdmaCall和ImsPhoneCall。
GsmCdmaPhone,GsmCdmaCallTracker,GsmCdmaConnection,GsmCdmaCall四者关系紧密,如下图:
在GsmCdmaPhone初始化的时候会创建GsmCdmaCallTracker,GsmCdmaCallTracker负责管理GsmCdmaConnection和GsmCdmaCall,GsmCdmaCallTracker的内部有一个GsmCdmaConnection的数组:
private GsmCdmaConnection mConnections[];
并且有常量控制着mConnections数组数组的大小,一个GsmCdmaConnection代表着一通电话,说明GSM最大允许同时存在19通,CDMA最大同时存在8通。
public static final int MAX_CONNECTIONS_GSM = 19; //7 allowed in GSM + 12 from IMS for SRVCC
private static final int MAX_CONNECTIONS_PER_CALL_GSM = 5; //only 5 connections allowed per call
private static final int MAX_CONNECTIONS_CDMA = 8;
private static final int MAX_CONNECTIONS_PER_CALL_CDMA = 1; //only 1 connection allowed per call
同时,GsmCdmaCallTracker的内部也会创建三个GsmCdmaCall(GsmCdmaCall仅仅会在GsmCdmaCallTracker中被创建,创建之后不会再被重新赋值):
public GsmCdmaCall mRingingCall = new GsmCdmaCall(this);
// A call that is ringing or (call) waiting
public GsmCdmaCall mForegroundCall = new GsmCdmaCall(this);
public GsmCdmaCall mBackgroundCall = new GsmCdmaCall(this);
Telephony Framework Call的状态有9种:
public enum State {
IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;
}
问题1:那么mRingingCall,mForegroundCall,mBackgroundCall分别对应Call的什么状态呢?
由于Telephony Framework Call的”ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING”这六种状态跟DriverCall.State是一一对应的
public static State
stateFromDCState (DriverCall.State dcState) {
switch (dcState) {
case ACTIVE: return State.ACTIVE;
case HOLDING: return State.HOLDING;
case DIALING: return State.DIALING;
case ALERTING: return State.ALERTING;
case INCOMING: return State.INCOMING;
case WAITING: return State.WAITING;
default: throw new RuntimeException ("illegal call state:" + dcState);
}
}
在GsmCdmaConnection中有依据DriverCall.State将GsmCdmaCall分类的方法
private GsmCdmaCall
parentFromDCState (DriverCall.State state) {
switch (state) {
case ACTIVE:
case DIALING:
case ALERTING:
return mOwner.mForegroundCall;
//break;
case HOLDING:
return mOwner.mBackgroundCall;
//break;
case INCOMING:
case WAITING:
return mOwner.mRingingCall;
//break;
default:
throw new RuntimeException("illegal call state: " + state);
}
}
所以mRingingCall,mForegroundCall,mBackgroundCall与GsmCdmaCall.mState的关系如下:
GsmCdmaCall | GsmCdmaCall.mState |
---|---|
mRingingCall | INCOMING,WAITING |
mForegroundCall | ACTIVE,DIALING,ALERTING |
mBackgroundCall | HOLDING |
GsmCdmaCallTracker在初始化的时候就注册监听了Call状态变化的消息,
public GsmCdmaCallTracker (GsmCdmaPhone phone) {
mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
}
所以当modem中Call状态发生变化后,便会通知到GsmCdmaCallTracker,GsmCdmaCallTracker通过调用RILJ的getCurrentCalls()方法发起查询modem当前的Call状态列表,modem返回来的结果是DriverCall 集合。
再由GsmCdmaCallTracker的handlePollCalls()方法来对比自身mConnections集合与DriverCall 集合的差异,进而依据DriverCall的信息跟新这对应GsmCdmaCall(mRingingCall,mForegroundCall,mBackgroundCall)的状态,同时将当前GsmCdmaConnection与对应的GsmCdmaCall绑定。
整个过程时序图如下:
ImsCall在/frameworks/opt/net/ims/src/java/com/android/ims/目录下,与之前介绍的Call不一样,ImsCall继承自IMS call专属的接口ICall,
public class ImsCall implements ICall {
ImsCall拥有处理IMS voice / video call的能力。ImsCall由ImsManager来创建,ImsManager是任意IMS actions的出发点。