在之前的讨论中我们曾经看到,GsmCallTracker所维护的每一路GsmConnection都需要从modem侧及时更新当前通话状态。那么,这个CLCC是如何从framework侧发送的呢?
在ril.java中有方法getCurrentCalls(Messageresult),该方法即是将GET_CURRENT_CALLS的消息打包以socket方式发到cp侧,再转换为AT+CLCC指令发送到网络端的。
再往上看,会发现在GsmCallTracker中有方法operationComplete(),是它调用了getCureentCalls方法。有趣的是,这个方法中还有个实例变量pendingOperations用来计数,具体是做什么用的,后面会说到。
统计了一下,调用这个operationComplete()方法的,都是在framework侧收到这些消息的时候:
EVENT_OPERATION_COMPLETE
EVENT_SWITCH_RESULT
EVENT_CONFERENCE_RESULT
EVENT_SEPARATE_RESULT
EVENT_ECT_RESULT
EVENT_GET_LAST_CALL_FAIL_CAUSE
对于前五种消息,相信经常折腾framework侧Call业务的童鞋都不会陌生。像dial,reject,acceptCall,以及切换前后台通话,合并通话等等业务操作,都会将这几个对应的消息用obtain系列的方法包成Message参数发下去,等待指令操作完之后再带着消息回传上来,以此解决异步消息应答消息不同步的问题。而消息EVENT_GET_LAST_CALL_FAIL_CAUSE,则是专门处理上述列出的各项业务异常的情况,在该种情况下,framework层也认为本次业务已经处理完成了。
简单地说,这个operationComplete()方法就像字面上所写的那样,标志着一次业务操作的结束,而实例变量pendingOperations就是专门用来计量当前待处理的业务数目的。可以看到,在obtain相关的方法中,该变量自加1,而framework收到底层上报上来的对应的完成消息,并调用operationComplete()方法时,这个变量又自减1了。
这种类似于PV操作的机制是专门用来保护异步消息处理的流程正常化。如果一次业务譬如说dial,由于网络侧timeout的缘故,迟迟收不到ATD执行完毕的消息回执,那么显然这个pendingOperations就不可能被清为零。
这是非常糟糕的事情。因为我们知道,网络侧会根据当前的通话流程(拨号、振铃、接通等等)不断主动上报消息UNSOL_RESPONSE_CALL_STATE_CHANGED,告知framework底层状态已经有了变化,请查询更新当前通话状态。
如何更新当前的通话状态?显然需要通过GET_CURRENT_CALLS这样的动作去查询。当UNSOL_RESPONSE_CALL_STATE_CHANGED消息主动上报后,GsmCallTracker的handleMessage就会执行之前已经注册好的消息EVENT_CALL_STATE_CHANGE对应的方法——pollCallsWhenSafe()。在这个方法中,我们又会注册消息EVENT_POLL_CALLS_RESULT,并调用getCurrentCalls方法,从而发送AT+CLCC指令,更新当前通话状态,完成一次通话状态的获取过程。
但是这个流程顺利完成的前提是pendingOperations已经清为零。
protected void pollCallsWhenSafe() { needsPoll = true; if (checkNoOperationsPending()) { lastRelevantPoll =obtainMessage(EVENT_POLL_CALLS_RESULT); cm.getCurrentCalls(lastRelevantPoll); } } private boolean checkNoOperationsPending() { if (DBG_POLL) log("checkNoOperationsPending: pendingOperations=" + pendingOperations); return pendingOperations == 0; }
换句话说,只有当上一次业务操作执行完成之后,接下来AT+CLCC指令才会被正常发送。这期间,即使网络侧已经有了变化,发送了主动上报的消息UNSOL_RESPONSE_CALL_STATE_CHANGED也没有用。这个机制有效地保护了各个处理流程之间逻辑上的正常,但是在某种异常条件下,比如说由于网络延迟引起了消息回执迟迟不能收到,导致framework无法进一步更新通话状态。这里是否应该设置某种保护机制,我得好好考虑一下。