Android Call分析(一) ---- Call对象详解

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


一、Dialer(InCallUI)中的Call

在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对象
Android Call分析(一) ---- Call对象详解_第1张图片
所以通过CallList获取到的Call对象都是InCallUI Call。
总结:在Dialer(InCallUI)模块中会见到两种Call对象,基本都是InCallUI Call;另外一种Call就是Telecom Call(Telecom Framework中的Call),Telecom Call会很明显地写成android.telecom.Call,所以也是很好地分辨的。


二、Telecom Framework中的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

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的:
Android Call分析(一) ---- Call对象详解_第2张图片
不管是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

Telephony Framework中的Call,
/frameworks/opt/telephony/src/java/com/android/internal/telephony/Call.java

public abstract class Call {

它是一个抽象类,它的继承关系如下:
Android Call分析(一) ---- Call对象详解_第3张图片
我们需要关注的是GsmCdmaCall和ImsPhoneCall。
GsmCdmaPhone,GsmCdmaCallTracker,GsmCdmaConnection,GsmCdmaCall四者关系紧密,如下图:
Android Call分析(一) ---- Call对象详解_第4张图片
在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绑定。
整个过程时序图如下:
Android Call分析(一) ---- Call对象详解_第5张图片

五、其他Call

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的出发点。

你可能感兴趣的:(Phone,Call)