在前一篇《
Telephony之进程与实体》中我们分析了,Application如果要发起通话相关的动作,可以通过Telephony的实体对象,也就是Phone对象来发起请求,而Phone对象就会通话相关的请求通过GsmCallTracker转发给RILJ,然后传递给Modem。
从这些方法可以看出,GsmCallTracker的作用主要包括两方面:
1、对通话线路进行操作,包括接听、挂断、切换、设置静音等;
2、对当前的通话状态进行通知(IDLE, RINGING, OFFHOOK);
然后来看他的初始化过程。
他的创建过程是在GSMPhone的初始化中完成的:
然后看GsmCallTracker的构造方法:
拨号动作:
接听动作:
拒接动作:
比如接听电话时,当发送了mCi.dial()的请求之后,就会立刻调用updatePhoneState()进行状态更新:
在GsmCallTracker的成员变量中,创建了GsmConnection类型的数组变量来维护所有的通话线路:
GsmCall将一个通话线路的状态分为以下9种:
IDLE 没有通话
ACTIVE 被激活状态
HOLDING 被保持状态
DIALING 正在呼出状态
ALERTING 正在呼出已经处于响铃的状态
INCOMING 正在来电状态
WAITING 已经通话中,又有新的来电
DISCONNECTED 被挂断
DISCONNECTING 正在挂断
在GsmCallTracker中,又将不同的线路状态分为3种:
ForegroundCall
BackgroundCall
RingingCall
然后创建了三个GsmCall对象:mRingingCall、mBackgroundCall、mRingingCall,这三种GsmCall所对应的状态如下:
mForegroundCall ----ACTIVE DIALING ALERTING
mBackgroundCall ----HOLDING
mRingingCall ----INCOMING WAITING
这样做的好处是, GsmCall不再面对具体的线路,而是面对当前Phone的状态,被激活的线路就是mForegroundCall,被保持的线路就是mBackgroundCall,而正处于响铃状态的线路就是mRingingCall,从这里我们可以想到,他和GsmConnection的区别在于,一个GsmCall可能包含多个GsmConnection对象(比如同时有两通电话处于被保持状态)。
现在回到GsmCallTracker的构造方法中,刚才我们看到,GsmCallTracker在构造方法的最后注册了对通话和Radio状态的监听器,下面我们从这些监听器入手分析GsmCallTracker的运行机制。
我们先来分析第一个监听器:
这个监听器监听的是RIL层通话的状态,当有新的状态到来时(比如新的来电),就会通过EVENT_CALL_STATE_CHANGE消息通知到GsmCallTracker,然后就会在handleMessage中进行处理:
然后就会调用pollCallsWhenSafe()方法去获取当前最新的通话状态,我们暂时不去分析pollCallsWhenSafe的流程,先来看看其他两个监听器的流程。
第二个监听器监听的是Radio的可用状态:
当Radio的状态上来后,就会在handleMessage中处理:
然后进入handleRadioAvailable()中处理:
到这里我们发现,接下来的流程和第一个监听器的流程相同。
然后我们看第三个监听器:
监听的是Radio的不可用状态,当监听的消息上来后,在handleMessage中处理:
然后会进入handleRadioNotAvailable()的流程:
接下来又是pollCallsWhenSafe()的操作,到这里我们发现,在GsmCallTracker构造函数中注册的三个监听器,无论哪一个被触发都会进入pollCallsWhenSafe的流程,接下来的分析我们将会看到,GsmCallTracker将会主动请求最新的通话状态,然后根据当前状态去更新GsmConnection和GsmCall对象。
我们现在来分析pollCallsWhenSafe()的流程,请注意,这个pollCallsWhenSafe()方法是在父类CallTracker中实现的:
这里看到,在pollCallsWhenSafe中通过RILJ(也就是mCi)去向Modem查询当前的通话状态,并注册了回调的消息EVENT_POLL_CALLS_RESULT,当拿到Modem返回值后,就会再次通过handleMessage()来处理最新的通话状态:
然后将数据拿到后,交由handlePollCalls()来处理,在这个方法里,就需要将当前的Modem通话状态数据进行解析,更新GsmConnection和GsmCall对象:
上面的更新过程中,用从Modem获取到的通话线路信息与mConnections中存储的信息做对比,从而更新mConnections中线路的状态,比如:
1、Modem中存在,而mConnections中不存在,则说明是新来电,或者新的去电,需要在mConnections中创建新的GsmConnection对象;
2、Modem中不存在,而mConnections中存在,说明该线路已经被挂断,需要从mConnections中删除该线路的GsmConnection对象;
3、Modem中和mConnections都存在,但是信息不匹配,则说明该线路的状态有改变,需要在mConnections中更新信息;
更新线路之后,对于最新挂断的线路,还需要更新挂断的原因,比如是被对方拒接还是未接的来电,然后在更新的最后,通知所有监听者,Radio状态已经改变。我们简单看一下通知的过程:
其实通知的过程就是通过mPhone的notifyPhoneStateChanged()方法来实现,这里的mPhone,也就是GSMPhone对象,会把该广播发送给监听者们。
以上就是GsmCallTracker的更新机制。
所以,GsmCallTracker是Phone对象和RILJ之间通话相关事务的接力者。
一、GsmCallTracker的作用及创建过程
- synchronized Connection dial (){}
- void acceptCall () throws CallStateException {}
- void rejectCall () throws CallStateException {}
- void switchWaitingOrHoldingAndActive() throws CallStateException {}
- void clearDisconnected() {}
- boolean canDial() {}
- private void updatePhoneState() {}
- void hangup (GsmConnection conn) throws CallStateException {}
- void hangup (GsmCall call) throws CallStateException {}
1、对通话线路进行操作,包括接听、挂断、切换、设置静音等;
2、对当前的通话状态进行通知(IDLE, RINGING, OFFHOOK);
然后来看他的初始化过程。
他的创建过程是在GSMPhone的初始化中完成的:
- public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {
- mCT = new GsmCallTracker(this);
- }
- GsmCallTracker (GSMPhone phone) {
- this.mPhone = phone;
- //拿到RILJ
- mCi = phone.mCi;
- //监听通话、Radio状态
- mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
- mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
- mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
- }
我们看到,在构造函数中GsmCallTracker拿到了RILJ对象,当需要对当前通话连接操作时,就会直接调用RILJ去实现。同时在构造方法中又注册了通话状态和Radio的状态监听器,用于向其他对象通知当前Radio状态的改变。
二、GsmCallTracker对通话动作的处理
拨号动作:
- synchronized Connection dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {
- //清除链接
- clearDisconnected();
- //条件判断
- if (!canDial()) {
- throw new CallStateException("cannot dial in current state");
- }
- //是否需要切换通话
- if (mForegroundCall.getState() == GsmCall.State.ACTIVE) {
- switchWaitingOrHoldingAndActive();
- fakeHoldForegroundBeforeDial();
- }
- //准备新的通话连接
- mPendingMO = new GsmConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString), this, mForegroundCall);
- mHangupPendingMO = false;
- if (mPendingMO.mAddress == null || mPendingMO.mAddress.length() == 0
- || mPendingMO.mAddress.indexOf(PhoneNumberUtils.WILD) >= 0
- ) {
- mPendingMO.mCause = Connection.DisconnectCause.INVALID_NUMBER;
- pollCallsWhenSafe();
- } else {
- //设置非静音模式
- setMute(false);
- //向RIL层发送拨号请求
- mCi.dial(mPendingMO.mAddress, clirMode, uusInfo, obtainCompleteMessage());
- }
- //更新通话状态
- updatePhoneState();
- mPhone.notifyPreciseCallStateChanged();
- return mPendingMO;
- }
- void acceptCall () throws CallStateException {
- if (mRingingCall.getState() == GsmCall.State.INCOMING) {
- //向RIL层发送接听的请求
- setMute(false);
- mCi.acceptCall(obtainCompleteMessage());
- } else if (mRingingCall.getState() == GsmCall.State.WAITING) {
- //切换通话
- setMute(false);
- switchWaitingOrHoldingAndActive();
- } else {
- throw new CallStateException("phone not ringing");
- }
- }
- void rejectCall () throws CallStateException {
- if (mRingingCall.getState().isRinging()) {
- //拒接
- mCi.rejectCall(obtainCompleteMessage());
- } else {
- throw new CallStateException("phone not ringing");
- }
- }
以上动作最后都要调用mCi对象来处理,这个对象就是RILJ,他会把请求发送给RIL层来处理。
三、GsmCallTracker对通话状态的处理
比如接听电话时,当发送了mCi.dial()的请求之后,就会立刻调用updatePhoneState()进行状态更新:
- private void updatePhoneState() {
- PhoneConstants.State oldState = mState;
- //获取当前状态
- if (mRingingCall.isRinging()) {
- mState = PhoneConstants.State.RINGING;
- } else if (mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
- mState = PhoneConstants.State.OFFHOOK;
- } else {
- mState = PhoneConstants.State.IDLE;
- }
- if (mState == PhoneConstants.State.IDLE && oldState != mState) {
- mVoiceCallEndedRegistrants.notifyRegistrants( new AsyncResult(null, null, null));
- } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
- mVoiceCallStartedRegistrants.notifyRegistrants ( new AsyncResult(null, null, null));
- }
- if (mState != oldState) {
- //通知GSMPhone进行状态广播
- mPhone.notifyPhoneStateChanged();
- }
- }
在这个过程中,最后要通过GSMPhone的notifyPhoneStateChanged()方法来通知其他对象,具体的通知过程将会在接下来的TelephonyRegistry中介绍。
四、GsmCallTracker的更新机制
而GsmCallTracker的更新机制核心任务就是维护这些不同线路,包括对各个线路的操作(比如接听、挂断、保持),以及各个线路状态的维护。为了达到这个目的,GsmCallTracker内部创建了两个非常重要的对象:GsmConnection和GsmCall。
4.1、GsmConnection
在GsmCallTracker的成员变量中,创建了GsmConnection类型的数组变量来维护所有的通话线路:
- GsmConnection mConnections[] = new GsmConnection[MAX_CONNECTIONS];
从这里可以看到,最多可以同时存在MAX_CONNECTIONS,也就是7条线路。
4.2、GsmCall
GsmCall将一个通话线路的状态分为以下9种:
IDLE 没有通话
ACTIVE 被激活状态
HOLDING 被保持状态
DIALING 正在呼出状态
ALERTING 正在呼出已经处于响铃的状态
INCOMING 正在来电状态
WAITING 已经通话中,又有新的来电
DISCONNECTED 被挂断
DISCONNECTING 正在挂断
在GsmCallTracker中,又将不同的线路状态分为3种:
ForegroundCall
BackgroundCall
RingingCall
然后创建了三个GsmCall对象:mRingingCall、mBackgroundCall、mRingingCall,这三种GsmCall所对应的状态如下:
mForegroundCall ----ACTIVE DIALING ALERTING
mBackgroundCall ----HOLDING
mRingingCall ----INCOMING WAITING
这样做的好处是, GsmCall不再面对具体的线路,而是面对当前Phone的状态,被激活的线路就是mForegroundCall,被保持的线路就是mBackgroundCall,而正处于响铃状态的线路就是mRingingCall,从这里我们可以想到,他和GsmConnection的区别在于,一个GsmCall可能包含多个GsmConnection对象(比如同时有两通电话处于被保持状态)。
而GsmCall要做的主要功能就是维护不同GsmCall的状态。
4.3、GsmCallTracker的更新机制
现在回到GsmCallTracker的构造方法中,刚才我们看到,GsmCallTracker在构造方法的最后注册了对通话和Radio状态的监听器,下面我们从这些监听器入手分析GsmCallTracker的运行机制。
我们先来分析第一个监听器:
- mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);
- public void handleMessage (Message msg) {
- AsyncResult ar;
- switch (msg.what) {
- case EVENT_REPOLL_AFTER_DELAY:
- case EVENT_CALL_STATE_CHANGE:
- //得到RIL层消息,通话状态有变
- pollCallsWhenSafe();
- break;
- }
- }
第二个监听器监听的是Radio的可用状态:
- mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);
- public void handleMessage (Message msg) {
- AsyncResult ar;
- switch (msg.what) {
- case EVENT_RADIO_AVAILABLE:
- handleRadioAvailable();
- break;
- }
- }
- protected void handleRadioAvailable() {
- pollCallsWhenSafe();
- }
然后我们看第三个监听器:
- mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);
- public void handleMessage (Message msg) {
- AsyncResult ar;
- switch (msg.what) {
- case EVENT_RADIO_NOT_AVAILABLE:
- handleRadioNotAvailable();
- break;
- }
- }
- private void handleRadioNotAvailable() {
- pollCallsWhenSafe();
- }
我们现在来分析pollCallsWhenSafe()的流程,请注意,这个pollCallsWhenSafe()方法是在父类CallTracker中实现的:
- @CallTracker.java
- protected void pollCallsWhenSafe() {
- mNeedsPoll = true;
- if (checkNoOperationsPending()) {
- //通过RILJ获取当前的最新通话状态
- mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);
- mCi.getCurrentCalls(mLastRelevantPoll);
- }
- }
- public void handleMessage (Message msg) {
- AsyncResult ar;
- switch (msg.what) {
- case EVENT_POLL_CALLS_RESULT:
- //拿到最新通话状态
- ar = (AsyncResult)msg.obj;
- if (msg == mLastRelevantPoll) {
- mNeedsPoll = false;
- mLastRelevantPoll = null;
- handlePollCalls((AsyncResult)msg.obj);
- }
- break;
- }
- }
- protected synchronized void handlePollCalls(AsyncResult ar) {
- List polledCalls;
- Connection newRinging = null; //or waiting
- boolean hasNonHangupStateChanged = false; // Any change besides
- boolean hasAnyCallDisconnected = false;
- boolean needsPollDelay = false;
- boolean unknownConnectionAppeared = false;
- for (int i = 0, curDC = 0, dcSize = polledCalls.size() ; i < mConnections.length; i++) {
- //拿到当前GsmCallTracker中的通话线路
- GsmConnection conn = mConnections[i];
- DriverCall dc = null;
- if (curDC < dcSize) {
- //拿到当前的Modem中的通话线路状态
- dc = (DriverCall) polledCalls.get(curDC);
- if (dc.index == i+1) {
- curDC++;
- } else {
- dc = null;
- }
- }
- if (conn == null && dc != null) {
- if (mPendingMO != null && mPendingMO.compareTo(dc)) {
- //mConnections中没有当前线路,而且当前线路是匹配mPendingMO的,说明是最新发起的呼出线路
- mConnections[i] = mPendingMO;
- mPendingMO.mIndex = i;
- mPendingMO.update(dc);
- mPendingMO = null;
- if (mHangupPendingMO) {
- //是否在呼出之后用户立刻挂断了线路
- mHangupPendingMO = false;
- try {
- //挂断这通线路
- hangup(mConnections[i]);
- } catch (CallStateException ex) {
- }
- return;
- }
- } else {
- //Modem中有该线路,而GsmConnection中没有该线路,说明有新的通话来临,需要创建新的线路连接
- mConnections[i] = new GsmConnection(mPhone.getContext(), dc, this, i);
- if (mConnections[i].getCall() == mRingingCall) {
- //新来电
- newRinging = mConnections[i];
- } else {
- //异常通话线路
- if (dc.state != DriverCall.State.ALERTING && dc.state != DriverCall.State.DIALING) {
- mConnections[i].onConnectedInOrOut();
- if (dc.state == DriverCall.State.HOLDING) {
- mConnections[i].onStartedHolding();
- }
- }
- unknownConnectionAppeared = true;
- }
- }
- hasNonHangupStateChanged = true;
- } else if (conn != null && dc == null) {
- //Modem中已经没有当前的链接,说明该线路已经被挂断,需要从mConnections中删除(置为null)
- mDroppedDuringPoll.add(conn);
- mConnections[i] = null;
- } else if (conn != null && dc != null && !conn.compareTo(dc)) {
- //Modem中的链接信息与当前的不匹配,可能发生了掉话或者新的通话
- mDroppedDuringPoll.add(conn);
- //需要创建新的链接
- mConnections[i] = new GsmConnection (mPhone.getContext(), dc, this, i);
- if (mConnections[i].getCall() == mRingingCall) {
- newRinging = mConnections[i];
- }
- hasNonHangupStateChanged = true;
- } else if (conn != null && dc != null) {
- //当前线路与Modem匹配,更新当前的链路信息
- boolean changed;
- changed = conn.update(dc);
- hasNonHangupStateChanged = hasNonHangupStateChanged || changed;
- }
- }
- //异常
- if (mPendingMO != null) {
- mDroppedDuringPoll.add(mPendingMO);
- mPendingMO = null;
- mHangupPendingMO = false;
- }
- if (newRinging != null) {
- //新的来电,需要通知registerForNewRingingConnection的监听者
- mPhone.notifyNewRingingConnection(newRinging);
- }
- //对于挂断的链接,需要标明挂断的原因
- for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {
- GsmConnection conn = mDroppedDuringPoll.get(i);
- if (conn.isIncoming() && conn.getConnectTime() == 0) {
- // Missed or rejected call
- Connection.DisconnectCause cause;
- if (conn.mCause == Connection.DisconnectCause.LOCAL) {
- //被拒掉
- cause = Connection.DisconnectCause.INCOMING_REJECTED;
- } else {
- //未接来电
- cause = Connection.DisconnectCause.INCOMING_MISSED;
- }
- mDroppedDuringPoll.remove(i);
- hasAnyCallDisconnected |= conn.onDisconnect(cause);
- } else if (conn.mCause == Connection.DisconnectCause.LOCAL
- || conn.mCause == Connection.DisconnectCause.INVALID_NUMBER) {
- mDroppedDuringPoll.remove(i);
- hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);
- }
- }
- if (mDroppedDuringPoll.size() > 0) {
- mCi.getLastCallFailCause( obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));
- }
- if (needsPollDelay) {
- pollCallsAfterDelay();
- }
- if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {
- internalClearDisconnected();
- }
- //更新通话状态
- updatePhoneState();
- if (unknownConnectionAppeared) {
- mPhone.notifyUnknownConnection();
- }
- if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {
- mPhone.notifyPreciseCallStateChanged();
- }
- }
1、Modem中存在,而mConnections中不存在,则说明是新来电,或者新的去电,需要在mConnections中创建新的GsmConnection对象;
2、Modem中不存在,而mConnections中存在,说明该线路已经被挂断,需要从mConnections中删除该线路的GsmConnection对象;
3、Modem中和mConnections都存在,但是信息不匹配,则说明该线路的状态有改变,需要在mConnections中更新信息;
更新线路之后,对于最新挂断的线路,还需要更新挂断的原因,比如是被对方拒接还是未接的来电,然后在更新的最后,通知所有监听者,Radio状态已经改变。我们简单看一下通知的过程:
- private void updatePhoneState() {
- PhoneConstants.State oldState = mState;
- if (mRingingCall.isRinging()) {
- //响铃状态
- mState = PhoneConstants.State.RINGING;
- } else if (mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {
- //通话状态
- mState = PhoneConstants.State.OFFHOOK;
- } else {
- //待机状态
- mState = PhoneConstants.State.IDLE;
- }
- if (mState == PhoneConstants.State.IDLE && oldState != mState) {
- mVoiceCallEndedRegistrants.notifyRegistrants( new AsyncResult(null, null, null));
- } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
- mVoiceCallStartedRegistrants.notifyRegistrants ( new AsyncResult(null, null, null));
- }
- if (mState != oldState) {
- //状态有更新,通过Phone对象发送给监听者
- mPhone.notifyPhoneStateChanged();
- }
- }
以上就是GsmCallTracker的更新机制。