1、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的处理实际上是对状态转换上的处
2、理。对这九中状态所对应的含义和call此时的现状要很熟。 Call的继承关系图: 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的处理核心,而且是整个Telep
3、hony处理的核心。Phone是一个最基本的概念,用来控制Phone系统相关(即无线相关的)模块的处理:simcard,call,message,datacall等。 Phone的继承关系如下: 在Phone的继承关系中可知,Phone只是一个接口,它被PhoneBase和PhoneProxy实现,而PhoneBase是抽象类,它被GsmPhone和CdmaPhone继承。所以有此可知Phone分为两类:GsmPhone和CdmaPhone。 PhoneBase还有另外一个继承关系:继承自Handler。这就说明GsmPhone和CdmaPhone其实都是一个Handler。
4、所以PhoneBase的子类是可以进行事件处理的。 3. Connection Connection用来处理一个真正的通话通路,包含通话过程中call的数据,包括号码、通话时间、MT还是MO、是第几路通话、挂断原因等信息。 Connection类关系图: GsmConnection中有个成员变量:GsmCall parent,这个成员变量是用来表示该connection是属于哪个Call的。由变量名(parent)可以看出Call与Connection的关系:父与子的关系,一个Call可以有多个Connection(3gpp中规定最多5个),但一个Connection只
5、能属于一个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的Conne
6、ctions变量进行管理。 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。这两个方法都是把一个conn
7、ection加入到Call的Connections成员变量中进行管理的。Call中还有一个方法detach(GsmConnection conn),这个方法是用来把connection从Call中移除的。 其中还有一个方法:/*package*/ boolean update (DriverCall dc)。这个是用来更新connection的。 4. DriverCall 是与ril层通信时的一个中间处理类,主要用来接收到ril的call数据后转到到java层上来。 DriverCall中包含了协议中规定的有关call的相关参数,具体如下: public int index
8、 //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 isVoi
9、ce;// 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;//
10、 Remote party name public int namePresentation;// 0=Allowed, 1=Restricted, 2=Not Specified/Unknown 3=Payphone 5. CallTracker CallTracker的类关系图: 从上图中可以看到,CallTracker 在本质上是一个Handler。 CallTracker是一个抽象类,所以其实际的操作对象是其子类:GSMCallTracker和CdmaCallTracker。 下面以GSMCallTracker为例介绍CallTracker的相关处理,CdmaCallT
11、racker处理基本与之相同。 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
12、 一个手机系统只允许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
13、的呢?前面已经讲过了Call和Connection及它们之间的关系,其实系统中的Call是已经存在的,就是上面的3个,其实主要的是对于一个connection,它需要依附于那个call,由connection的构造方法知: 1) 在 从RIL获取的Calls列表的时候,通过parentFromDCState来获取相应的Call: private GsmCall parentFromDCState (DriverCall.State state) { switch (state) { case ACTIVE:
14、 case DIALING: case ALERTING: return owner.foregroundCall; //break; case HOLDING: return owner.backgroundCall; //break; case INCOMING: case WAITING: return o
15、wner.ringingCall; //break; default: throw new RuntimeException("illegal call state: " + state); } } 2) 在其他情况下,一般根据上面的状态直接传入对应的call。 3) GsmCallTracker中的事件处理机制 Call的事件处理基本上是请求应答模式,具体如下(以dail为例,其他类同): 其中涉及到3个变量的处理: protected i
16、nt pendingOperations; protected boolean needsPoll; protected Message lastRelevantPoll; pendingOperations:顾名思义,这个变量是在发生请求的时候会++,在处理应答的时候会--。 needsPoll:该变量是用来配合pendingOperations处理是否需要从RIL获取当前calls列表,并更新connections列表。 lastRelevantPoll:在发送RIL_REQUEST_GET_CURRENT_CALLS的时候记录最近一次请求的message,在response
17、的时候只对最近一次请求的response做出响应,更新connections列表。 上面的3个变量主要是用来判断是否需要发送RIL_REQUEST_GET_CURRENT_CALLS请求来从RIL获取当前的calls列表,并更新connections列表。 GsmCallTracker事件处理的核心是一个异步事件的处理,在发送请求后等待回应的时候,用户有可能会继续的发送对应的请求,然后会有多个response同时上报过来,上面的3个变量主要是为了更好的处理这种情况,不管用户发送了多少次请求,pendingOperations都会记录下来;而且只要用户发送了一次
18、请求,needsPoll就为true,即就需要从RIL获取最新的call列表。所以只有当pendingOperations==0 并且 needsPoll == true的时候才发送RIL_REQUEST_GET_CURRENT_CALLS请求去获取Calls列表。 if (pendingOperations == 0 && needsPoll) { lastRelevantPoll= obtainMessage(EVENT_POLL_CALLS_RESULT); cm.getCurrentCalls(lastRelevant
19、Poll); } 由于中间可能发送了多次RIL_REQUEST_GET_CURRENT_CALLS请求,在多次发送此请求的过程中却没有一次response回来,最后的结果是到最后肯定会有多个response上来,而此时我们只需要更新一次就够了,lastRelevantPoll就是用来处理这个事情的:lastRelevantPoll记录了最近一次发送RIL_REQUEST_GET_CURRENT_CALLS请求时的response message,当返回的response message是最后一个时就更新Connections列表。 4) GsmCallTra
20、cker的核心处理方法----更新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 (
21、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();
22、 return; } Connection newRinging = null; //or waiting boolean hasNonHangupStateChanged = false; // Any change besides // a dropped connection boolean needsPollDelay = false; boolean unknownConnectionAppeared = false;
23、 //遍历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) {
24、 dc = (DriverCall) polledCalls.get(curDC); //此处说明connections数组的index+1就是callId,也即dc.index。 if (dc.index == i+1) { curDC++; } else { dc = null; } } if (conn == null && dc != n
25、ull) { //conn为null,说明CallTracker中是没有对应的call的,这只有两种情况可以//发生:MO的dialing状态和MT的incoming/waiting状态。打电话和来电的时候//会执行到此处。 } else if (conn != null && dc == null) { //说明在底层已经挂掉了,CallTracker需要drop该connection。对方挂断电话//的时候就会执行到此处。 } else if (conn != null && dc != null && !pare
26、To(dc)) { //此处说明对应的connection和dc并不是同一个call,这种情况下就需要drop //当前的connection并根据dc重新构建一个。此处应该是一个异常的处理情况。 } else if (conn != null && dc != null) { //此处表明connection和dc是同一个call,随后根据dc来更新connection。 } if (REPEAT_POLLING) {} } if (pending
27、MO != null) { //此时dial请求已经发送给ril但是对应的response还没有上来的情况。正常情况//下,到此时pengdingMO应该为null了,但是假如在dial后的response还没有上//来的时候做了其他动作,如hangup call,则此时很可能就执行到此处。 droppedDuringPoll.add(pendingMO); pendingMO = null; hangupPendingMO = false; } if (newRinging != null) {
28、 //表示是一个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;
29、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) {
30、 cause = Connection.DisconnectCause.INCOMING_REJECTED; } else { cause = Connection.DisconnectCause.INCOMING_MISSED; } droppedDuringPoll.remove(i); conn.onDisconnect(cause); } else if (conn.cause == Connection.Disco
31、nnectCause.LOCAL) { // Local hangup droppedDuringPoll.remove(i); conn.onDisconnect(Connection.DisconnectCause.LOCAL); } else if (conn.cause == Connection.DisconnectCause.INVALID_NUMBER) { droppedDuringPoll.remove(i);
32、 conn.onDisconnect(Connection.DisconnectCause.INVALID_NUMBER); } } // Any non-local disconnects: determine cause if (droppedDuringPoll.size() > 0) { //本地挂断都是DisconnectCause.LOCAL,此处表明是对方或网络端的挂断,具体//挂断原因需要从RIL处获得。 cm.getLastCallFailCause( obtainNoPo
33、llCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE)); } 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
34、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) { p
35、hone.notifyUnknownConnection();
}
if (hasNonHangupStateChanged || newRinging != null) {
//通知用户callstatechange。
phone.notifyPreciseCallStateChanged();
}
if(DBG_POLL) log(" 36、rolState(int)
answerCall(Phone)
hangup(Phone)
hangupRingingCall(Phone)
hangupActiveCall(Phone)
hangupHoldingCall(Phone)
hangupRingingAndActive(Phone)
hangupAllCalls(Phone)
hangup(Call)
hangupAllConnections(Call)
hangup(Connection)
answerAndEndHolding(Phone)
answerAndEndActive(Phone)
pla 37、ceCall(Phone, String, Uri)
placeCallVia(Context, Phone, String, Uri, Uri)
switchHoldingAndActive(Phone)
mergeCalls(Phone)
isConferenceCall(Call)
showIncomingCallUi(int)
turnOnSpeaker(Context, boolean, boolean)
restoreSpeakerMode(Context)
isSpeakerOn(Context)
turnOnNoiseSuppression(Context, 38、boolean, boolean)
restoreNoiseSuppression(Context)
setMute(Phone, boolean)
getMute(Phone)
setAudioMode(Context, int)
okToS(Phone)
okToMergeCalls(Phone)
okToAddCall(Phone)
外部进程或应用需要使用call时,如hardware键接听和挂断电话等,此时的接口是PhoneInterfaceManager类所提供,此类继承自ITelephony.Stub,是aidl接口。
PhoneUtils中有一个static子类:ConnectionHandler,其主要是用来处理Mute设置相关的,其中主要是更新sConnectionMuteTable并根据当前phone的状态来控制mute设置。






