Android通话过程分析.docx
《Android通话过程分析.docx》由会员分享,可在线阅读,更多相关《Android通话过程分析.docx(20页珍藏版)》请在冰豆网上搜索。
![Android通话过程分析.docx](https://file1.bdocx.com/fileroot1/2023-2/24/26c2fa07-fc03-441d-bdf0-b8e19b16797b/26c2fa07-fc03-441d-bdf0-b8e19b16797b1.gif)
Android通话过程分析
Android 通话过程分析
本文档主要对 android 平台下的 call 的实现做详细分析。
Call 处理的五大核心分别是:
Call,Phone,
CallTracker,DriverCall,Connection
1.Call
Call 是 Call 应用中的最基本的单位,其主要是用来管理 Connection 的。
Call 中非常重要
的是其状态,Call 中共有九种状态:
IDLE, ACTIVE, HOLDING, DIALING, ALERTING, INCOMING, WAITING, DISCONNECTED,
DISCONNECTING;
对 call 的处理实际上是对状态转换上的处理。
对这九中状态所对应的含义和 call 此时的
现状要很熟。
Call 的继承关系图:
Call
+hangup()
+hangupAllCalls()
+getConnections()
GsmCall
#connections
+attach()
+attachFake()
+detach()
+isFull() :
bool
+clearDisconnected()
CdmaCall
#connections
+attach()
+attachFake()
+detach()
+isFull() :
bool
+clearDisconnected()
Call 是一个抽象类,从图中可知,实际操作是在其子类 GsmCall 和 CdmaCall。
在
GsmCall 中,有个成员变量:
connections,这个变量是用来管理 Call 中的 connection 的,
一个 Call 最大允许有 5 个 connections:
static final int MAX_CONNECTIONS_PER_CALL = 5; // only 5 connections allowed per call
2.Phone
Phone 不仅是 call 的处理核心,而且是整个 Telephony 处理的核心。
Phone 是一个最基
本的概念,用来控制 Phone 系统相关(即无线相关的)模块的处理:
simcard,call,message,datacall 等。
Phone 的继承关系如下:
<<接口>>
Phone
PhoneBase
#mPhoneId :
int
GsmPhone
Hander
+handleMessage()
PhoneProxy
-mActivePhone :
Phone
CdmaPhone
#GsmCallTracker mCT
+dial()
+rejectCall()
+acceptCall()
+switchHoldingAndActive()
#CdmaCallTracker mCT
+dial()
+rejectCall()
+acceptCall()
+switchHoldingAndActive()
在 Phone 的继承关系中可知,Phone 只是一个接口,它被 PhoneBase 和 PhoneProxy 实
现,而 PhoneBase 是抽象类,它被 GsmPhone 和 CdmaPhone 继承。
所以有此可知
Phone 分为两类:
GsmPhone 和 CdmaPhone。
PhoneBase 还有另外一个继承关系:
继承自 Handler。
这就说明 GsmPhone 和
CdmaPhone 其实都是一个 Handler。
所以 PhoneBase 的子类是可以进行事件处理的。
3.Connection
Connection 用来处理一个真正的通话通路,包含通话过程中 call 的数据,包括号码、通
话时间、MT 还是 MO、是第几路通话、挂断原因等信息。
Connection 类关系图:
Connection
GsmConnectionCdmaConnection
CallTracker
GsmConnection 中有个成员变量:
GsmCallparent,这个成员变量是用来表示该
connection 是属于哪个 Call 的。
由变量名(parent)可以看出 Call 与 Connection 的关系:
父与子的关系,一个 Call 可以有多个 Connection(3gpp 中规定最多 5 个),但一个
Connection 只能属于一个 Call。
所以一个 Connection 必定要依附于一个 Call。
Connection 是怎样依附于一个 Call 的呢?
从 Connection 的构造方法中就可以知道:
a.GsmConnection (Context context, DriverCall dc, GsmCallTracker ct, int index)
这个构造方法是在 MT 的时候使用的,因为它有一个 DriverCall 的参数。
它通过
parentFromDCState 方法来获得对应的 parent(Call)且通过
parent.attach(this, dc);把 connection 加入到 Call 的 Connections 变量
进行管理。
b.GsmConnection (Context context, String dialString, GsmCallTracker ct, GsmCall parent)
这个构造方法是在 MO 的时候使用的,它会传入一个指定的 parent(Call)且通过
parent.attachFake(this,GsmCall.State.DIALING);调用把 Connection 加入到 Call 的
Connections 变量进行管理。
从上面知道 Connection 调用了 Call 的 2 个重要的方法:
Attach 和 attachFake。
这
两个方法都是把一个 connection 加入到 Call 的 Connections 成员变量中进行管理的。
Call 中还有一个方法 detach(GsmConnectionconn),这个方法是用来把 connection
从 Call 中移除的。
其中还有一个方法:
/*package*/booleanupdate(DriverCalldc)。
这个是用来更新
connection 的。
4.DriverCall
是与 ril 层通信时的一个中间处理类,主要用来接收到 ril 的 call 数据后转到到 java 层上
来。
DriverCall 中包含了协议中规定的有关 call 的相关参数,具体如下:
public int index; //Connection Index for use with, eg, AT+CHLD
public boolean isMT; //是 incoming 还是 outgoing
public State state; //connection state。
public boolean isMpty;// nonzero if is mpty call。
public String number;// Remote party number
public int TOA; //type of address, eg 145 = intl
public boolean isVoice;// nonzero if this is is a voice call
public boolean isVoicePrivacy;// nonzero if CDMA voice privacy mode is active
public int als;// ALS line indicator if available (0 = line 1)
public int numberPresentation;// 0=Allowed, 1=Restricted, 2=Not Specified/Unknown
3=Payphone
public String name;// Remote party name
public int namePresentation;// 0=Allowed, 1=Restricted, 2=Not Specified/Unknown
3=Payphone
5.CallTracker
CallTracker 的类关系图:
GsmConnection
Handler
CallTracker
#pendingOperations :
int
#needsPoll :
bool
-Message lastRelevantPoll
<<接口>>
CommandsInterface
GsmPhone
GsmCall
GsmCallTracker
#ringingCall :
GsmCall
#foregroundCall :
GsmCall
#backgroundCall :
GsmCall
#connections[] :
GsmConnection
CdmaCallTracker
#ringingCall :
CdmaCall
#foregroundCall :
CdmaCall
#backgroundCall :
CdmaCall
#connections[] :
CdmaConnection
CdmaPhone
CdmaCall
CdmaConnection
从上图中可以看到,CallTracker 在本质上是一个 Handler。
CallTracker 是一个抽象类,所以其实际的操作对象是其子类:
GSMCallTracker 和
CdmaCallTracker。
下面以 GSMCallTracker 为例介绍 CallTracker 的相关处理,CdmaCallTracker 处理基本与
之相同。
GSMCallTracker 是 Android 的通话管理层。
GSMCallTracker 建立了 ConnectionList 来管
理现行的通话连接,并向上层提供电话调用接口。
1) Connections
Connections 是 GSMCallTracker 用来维护所有的通话的列表,最大可维护 7 路通话。
static final int MAX_CONNECTIONS = 7;// only 7 connections allowed in GSM
2) ringingCall、foregroundCall、backgroundCall
一个手机系统只允许 3 个 Call 同时存在,即 ring call、active call 和 held call,所以
GSMCallTracker 用 ringingCall、foregroundCall、backgroundCall 来管理。
ringingCall:
用来管理 INCOMING 和 WAITING 的通话
foregroundCall:
用来管理 DAILING、ALERTING、ACTIVE 的通话
backgroundCall:
用来管理 HOLD 的通话
那么,ringingCall、foregroundCall、backgroundCall 是如何来管理对应的 call 和
connection 的呢?
前面已经讲过了 Call 和 Connection 及它们之间的关系,其实系统
中的 Call 是已经存在的,就是上面的 3 个,其实主要的是对于一个 connection,它
需要依附于那个 call,由 connection 的构造方法知:
1) 在从 RIL 获取的 Calls 列表的时候,通过 parentFromDCState 来获取相应的
Call:
private GsmCall
parentFromDCState (DriverCall.State state) {
switch (state) {
case ACTIVE:
case DIALING:
case ALERTING:
return owner.foregroundCall;
//break;
case HOLDING:
return owner.backgroundCall;
//break;
case INCOMING:
case WAITING:
return owner.ringingCall;
//break;
default:
throw new RuntimeException("illegal call state:
" +
state);
}
}
2) 在其他情况下,一般根据上面的状态直接传入对应的 call。
3) GsmCallTracker 中的事件处理机制
Call 的事件处理基本上是请求应答模式,具体如下(以 dail 为例,其他类同):
其中涉及到 3 个变量的处理:
protected int pendingOperations;
protected boolean needsPoll;
protected Message lastRelevantPoll;
pendingOperations:
顾名思义,这个变量是在发生请求的时候会++,在处理应答
的时候会--。
needsPoll:
该变量是用来配合pendingOperations处理是否需要从RIL获取当前
calls列表,并更新connections列表。
lastRelevantPoll:
在发送RIL_REQUEST_GET_CURRENT_CALLS的时候记录最近
一次请求的message,在response的时候只对最近一次请求的response做出响应,
更新connections列表。
上面的3个变量主要是用来判断是否需要发送RIL_REQUEST_GET_CURRENT_CALLS请
求来从RIL获取当前的calls列表,并更新connections列表。
if (pendingOperations == 0 && needsPoll) {
lastRelevantPoll= obtainMessage(EVENT_POLL_CALLS_RESULT);
cm.getCurrentCalls(lastRelevantPoll);
4) GsmCallTracker 的核心处理方法----更新 connections 列表处理方法
protected void handlePollCalls(AsyncResult ar)的处理分析
handlePollCalls(AsyncResult ar) {
List polledCalls;
if(DBG_POLL) log(">handlePollCalls");
if (ar.exception == null) {
//没有异常,取得calllists列表
polledCalls = (List)ar.result;
} else if (isCommandExceptionRadioNotAvailable(ar.exception)) {
//如果radio是RADIO_NOT_AVAILABLE,则挂断所有的calls,这里的意思是所有
的DriverCall是null。
polledCalls = new ArrayList();
} else {
//其他的异常的话,推迟POLL_DELAY_MSEC = 250后再发送
//RIL_REQUEST_GET_CURRENT_CALLS去获取DriverCall列表。
pollCallsAfterDelay();
return;
}
Connection newRinging = null; //or waiting
boolean hasNonHangupStateChanged = false;// Any change besides
// a dropped connection
boolean needsPollDelay = false;
boolean unknownConnectionAppeared = false;
//遍历DriverCall和Connections,根据不同情况做相应处理。
for (int i = 0, curDC = 0, dcSize = polledCalls.size()
; i < connections.length; i++) {
GsmConnection conn = connections[i];
DriverCall dc = null;
// polledCall list is sparse
if (curDC < dcSize) {
dc = (DriverCall) polledCalls.get(curDC);
//此处说明connections数组的index+1就是callId,也即dc.index。
if (dc.index == i+1) {
curDC++;
} else {
dc = null;
}
}
if (conn == null && dc !
= null) {
} else if (conn !
= null && dc == null) {
} else if (conn !
= null && dc !
= null && !
pareTo(dc))
{
} else if (conn !
= null && dc !
= null) {
//此处表明connection和dc是同一个call,随后根据dc来更新connection。
}
if (REPEAT_POLLING) {}
}
if (pendingMO !
= null) {
droppedDuringPoll.add(pendingMO);
pendingMO = null;
hangupPendingMO = false;
}
if (newRinging !
= null) {
//表示是一个MT call,需要通知用户。
phone.notifyNewRingingConnection(newRinging);
}
// clear the "local hangup" and "missed/rejected call"
// cases from the "dropped during poll" list
// These cases need no "last call fail" reason
for (int i = droppedDuringPoll.size() - 1; i >= 0 ; i--) {
GsmConnection conn = droppedDuringPoll.get(i);
if (conn.isIncoming() && conn.getConnectTime() == 0) {
// Missed or rejected call
Connection.DisconnectCause cause;
if (conn.cause == Connection.DisconnectCause.LOCAL) {
cause = Connection.DisconnectCause.INCOMING_REJECTED;
} else {
cause = Connection.DisconnectCause.INCOMING_MISSED;
}
droppedDuringPoll.remove(i);
conn.onDisconnect(cause);
} else if (conn.cause == Connection.DisconnectCause.LOCAL) {
// Local hangup
droppedDuringPoll.remove(i);
conn.onDisconnect(Connection.DisconnectCause.LOCAL);
} else if (conn.cause ==
Connection.DisconnectCause.INVALID_NUMBER) {
droppedDuringPoll.remove(i);
}
}
// Any non-local disconnects:
determine cause
if (droppedDuringPoll.size() > 0) {
//本地挂断都是DisconnectCause.LOCAL,此处表明是对方或网络端的挂断,具
体//挂断原因需要从RIL处获得。
cm.getLastCallFailCause(
}
if (needsPollDelay) {
pollCallsAfterDelay();
}
// Cases when we can no longer keep disconnected Connection's
// with their previous calls
// 1) the phone has started to ring
// 2) A Call/Connection object has changed state...
//we may have switched or held or answered (but not hung up)
if (newRinging !
= null || hasNonHangupStateChanged) {
internalClearDisconnected();
}
更新Phone的状态。
updatePhoneState();
if (unknownConnectionAppeared) {
phone.notifyUnknownConnection();
}
if (hasNonHangupStateChanged || newRinging !
= null) {
//通知用户callstatechange。
phone.notifyPreciseCallStateChanged();
}
if(DBG_POLL) log("//dumpState();
}
Call 处理中的内部接口就是 PhoneUtils,它提供的主要接口如下:
setAudioControlState(int)
answerCall(Phone)
hangup(Phone)
hangupRingingCall(Phone)
hangupActiveCall(Phone)
hangupHoldingCall(Phone)
hangupRingingAndActive(Phone)
hangupAllC