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

来源:互联网 发布:淘宝卖面膜名字店面 编辑:程序博客网 时间:2024/06/07 08:15

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 {
  • 1
  • 1

它只是一个普通的类,没有父类也没有实现接口。 
从它的构造方法来看,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();    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

InCallUI Call中的State、Phone Account、ChildNumber等信息都来源于Telecom Call。 
InCallUI Call被CallList管理着,CallList中有一个Map来存储InCallUI Call

//String存储的是Call IDprivate final HashMap<String, Call> mCallById = new HashMap<>();
  • 1
  • 2
  • 1
  • 2

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);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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 Framework中的Call

Telecom Call, 
/frameworks/base/telecomm/java/android/telecom/Call.java

public final class Call {
  • 1
  • 1

它是一个被定义成final类型的类,它是在Phone.java (framework\base\telecomm\java\android\telecom) 的internalAddCall()方法中被创建的

    private final List<Call> 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);     }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

而ParcelableCall是一个中间者的角色,在InCallController.java中先将Telecom Service中的Call转换成ParcelableCall

ParcelableCall parcelableCall = ParcelableCallUtils.toParcelableCall    (call,videoProviderChanged /* includeVideoProvider */,    mCallsManager.getPhoneAccountRegistrar());
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

然后再把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 {
  • 1
  • 1

它实现了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);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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();        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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;    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

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 {
  • 1
  • 1

它是一个抽象类,它的继承关系如下: 
 
我们需要关注的是GsmCdmaCall和ImsPhoneCall。 
GsmCdmaPhone,GsmCdmaCallTracker,GsmCdmaConnection,GsmCdmaCall四者关系紧密,如下图: 
这里写图片描述
在GsmCdmaPhone初始化的时候会创建GsmCdmaCallTracker,GsmCdmaCallTracker负责管理GsmCdmaConnection和GsmCdmaCall,GsmCdmaCallTracker的内部有一个GsmCdmaConnection的数组:

private GsmCdmaConnection mConnections[];
  • 1
  • 1

并且有常量控制着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
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

同时,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);
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

Telephony Framework Call的状态有9种:

    public enum State {        IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED, DISCONNECTING;    }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

问题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);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在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);        }    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

所以mRingingCall,mForegroundCall,mBackgroundCall与GsmCdmaCall.mState的关系如下:

GsmCdmaCallGsmCdmaCall.mStatemRingingCallINCOMING,WAITINGmForegroundCallACTIVE,DIALING,ALERTINGmBackgroundCallHOLDING

GsmCdmaCallTracker在初始化的时候就注册监听了Call状态变化的消息,

    public GsmCdmaCallTracker (GsmCdmaPhone phone) {        mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);    }
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

所以当modem中Call状态发生变化后,便会通知到GsmCdmaCallTracker,GsmCdmaCallTracker通过调用RILJ的getCurrentCalls()方法发起查询modem当前的Call状态列表,modem返回来的结果是DriverCall 集合。 
再由GsmCdmaCallTracker的handlePollCalls()方法来对比自身mConnections集合与DriverCall 集合的差异,进而依据DriverCall的信息跟新这对应GsmCdmaCall(mRingingCall,mForegroundCall,mBackgroundCall)的状态,同时将当前GsmCdmaConnection与对应的GsmCdmaCall绑定。 
整个过程时序图如下: 
这里写图片描述

五、其他Call

ImsCall在/frameworks/opt/net/ims/src/java/com/android/ims/目录下,与之前介绍的Call不一样,ImsCall继承自IMS call专属的接口ICall,

public class ImsCall implements ICall {
  • 1
  • 1

ImsCall拥有处理IMS voice / video call的能力。ImsCall由ImsManager来创建,ImsManager是任意IMS actions的出发点。

原文地址: http://blog.csdn.net/linyongan/article/details/52551248

0 0
原创粉丝点击