Telephony之GsmCallTracker

在前一篇《 Telephony之进程与实体》中我们分析了,Application如果要发起通话相关的动作,可以通过Telephony的实体对象,也就是Phone对象来发起请求,而Phone对象就会通话相关的请求通过GsmCallTracker转发给RILJ,然后传递给Modem。

        所以,GsmCallTracker是Phone对象和RILJ之间通话相关事务的接力者


一、GsmCallTracker的作用及创建过程


        我们从他提供的方法来看其提供的功能:
  1. synchronized Connection dial (){}  
  2. void acceptCall () throws CallStateException {}  
  3. void rejectCall () throws CallStateException {}  
  4. void switchWaitingOrHoldingAndActive() throws CallStateException {}  
  5. void clearDisconnected() {}  
  6. boolean canDial() {}  
  7. private void updatePhoneState() {}  
  8. void hangup (GsmConnection conn) throws CallStateException {}  
  9. void hangup (GsmCall call) throws CallStateException {}  
        从这些方法可以看出,GsmCallTracker的作用主要包括两方面:
        1、对通话线路进行操作,包括接听、挂断、切换、设置静音等;
        2、对当前的通话状态进行通知(IDLE, RINGING, OFFHOOK);

        然后来看他的初始化过程。
        他的创建过程是在GSMPhone的初始化中完成的:
  1. public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) {  
  2.     mCT = new GsmCallTracker(this);  
  3. }  
        然后看GsmCallTracker的构造方法:
  1. GsmCallTracker (GSMPhone phone) {  
  2.     this.mPhone = phone;  
  3.     //拿到RILJ  
  4.     mCi = phone.mCi;  
  5.     //监听通话、Radio状态  
  6.     mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);  
  7.     mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);  
  8.     mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);  
  9. }  

        我们看到,在构造函数中GsmCallTracker拿到了RILJ对象,当需要对当前通话连接操作时,就会直接调用RILJ去实现。同时在构造方法中又注册了通话状态和Radio的状态监听器,用于向其他对象通知当前Radio状态的改变。


二、GsmCallTracker对通话动作的处理


        通话动作包含接听、挂断、切换、静音等,这些事件在APP层被请求后,最终都会发送给当前的Phone对象,也就是PhoneProxy,然后再转交给当前的mActivePhone,也就是某个GSMPhone,此时GSMPhone对象就会把请求转交给GsmCallTracker来处理:
        拨号动作:
  1. synchronized Connection dial (String dialString, int clirMode, UUSInfo uusInfo) throws CallStateException {  
  2.     //清除链接  
  3.     clearDisconnected();  
  4.     //条件判断  
  5.     if (!canDial()) {  
  6.         throw new CallStateException("cannot dial in current state");  
  7.     }  
  8.   
  9.     //是否需要切换通话  
  10.     if (mForegroundCall.getState() == GsmCall.State.ACTIVE) {  
  11.         switchWaitingOrHoldingAndActive();  
  12.         fakeHoldForegroundBeforeDial();  
  13.     }  
  14.   
  15.     //准备新的通话连接  
  16.     mPendingMO = new GsmConnection(mPhone.getContext(), checkForTestEmergencyNumber(dialString), this, mForegroundCall);  
  17.     mHangupPendingMO = false;  
  18.     if (mPendingMO.mAddress == null || mPendingMO.mAddress.length() == 0  
  19.             || mPendingMO.mAddress.indexOf(PhoneNumberUtils.WILD) >= 0  
  20.        ) {  
  21.         mPendingMO.mCause = Connection.DisconnectCause.INVALID_NUMBER;  
  22.         pollCallsWhenSafe();  
  23.     } else {  
  24.         //设置非静音模式  
  25.         setMute(false);  
  26.         //向RIL层发送拨号请求  
  27.         mCi.dial(mPendingMO.mAddress, clirMode, uusInfo, obtainCompleteMessage());  
  28.     }  
  29.   
  30.     //更新通话状态  
  31.     updatePhoneState();  
  32.     mPhone.notifyPreciseCallStateChanged();  
  33.     return mPendingMO;  
  34. }  
        接听动作:
  1. void acceptCall () throws CallStateException {  
  2.     if (mRingingCall.getState() == GsmCall.State.INCOMING) {  
  3.         //向RIL层发送接听的请求  
  4.         setMute(false);  
  5.         mCi.acceptCall(obtainCompleteMessage());  
  6.     } else if (mRingingCall.getState() == GsmCall.State.WAITING) {  
  7.         //切换通话  
  8.         setMute(false);  
  9.         switchWaitingOrHoldingAndActive();  
  10.     } else {  
  11.         throw new CallStateException("phone not ringing");  
  12.     }  
  13. }  
        拒接动作:
  1. void rejectCall () throws CallStateException {  
  2.     if (mRingingCall.getState().isRinging()) {  
  3.         //拒接  
  4.         mCi.rejectCall(obtainCompleteMessage());  
  5.     } else {  
  6.         throw new CallStateException("phone not ringing");  
  7.     }  
  8. }  

        以上动作最后都要调用mCi对象来处理,这个对象就是RILJ,他会把请求发送给RIL层来处理。


三、GsmCallTracker对通话状态的处理


        在GsmCallTracker中完成了通话相关动作之后,就要立刻更新当前的状态并发送给Radio状态的监听者。
        比如接听电话时,当发送了mCi.dial()的请求之后,就会立刻调用updatePhoneState()进行状态更新:
  1. private void updatePhoneState() {  
  2.     PhoneConstants.State oldState = mState;  
  3.   
  4.     //获取当前状态  
  5.     if (mRingingCall.isRinging()) {  
  6.         mState = PhoneConstants.State.RINGING;  
  7.     } else if (mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {  
  8.         mState = PhoneConstants.State.OFFHOOK;  
  9.     } else {  
  10.         mState = PhoneConstants.State.IDLE;  
  11.     }  
  12.   
  13.     if (mState == PhoneConstants.State.IDLE && oldState != mState) {  
  14.         mVoiceCallEndedRegistrants.notifyRegistrants( new AsyncResult(nullnullnull));  
  15.     } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {  
  16.         mVoiceCallStartedRegistrants.notifyRegistrants ( new AsyncResult(nullnullnull));  
  17.     }  
  18.   
  19.     if (mState != oldState) {  
  20.         //通知GSMPhone进行状态广播  
  21.         mPhone.notifyPhoneStateChanged();  
  22.     }  
  23. }  

        在这个过程中,最后要通过GSMPhone的notifyPhoneStateChanged()方法来通知其他对象,具体的通知过程将会在接下来的TelephonyRegistry中介绍。


四、GsmCallTracker的更新机制


        手机通话功能可以支持多路通话。比如最基本的情况是,在和A通话过程中(线路A),有新的来电时(线路B),如果选择接听B,那么A线路将处于“呼叫保持”状态,此时如果B线路被挂断,那么A线路重新被激活。

        而GsmCallTracker的更新机制核心任务就是维护这些不同线路,包括对各个线路的操作(比如接听、挂断、保持),以及各个线路状态的维护。为了达到这个目的,GsmCallTracker内部创建了两个非常重要的对象:GsmConnectionGsmCall


4.1、GsmConnection


        为了管理不同的线路,Android定义了GsmConnection类,简单来说, 每一条通话线路,都是一个GsmConnection类型的对象
        在GsmCallTracker的成员变量中,创建了GsmConnection类型的数组变量来维护所有的通话线路:
  1. GsmConnection mConnections[] = new GsmConnection[MAX_CONNECTIONS];  

        从这里可以看到,最多可以同时存在MAX_CONNECTIONS,也就是7条线路。


4.2、GsmCall


        这个对象和GsmConnection的作用类似, 每一个通话线路都<可以>是一个GsmCall对象,但实际上并不是<一个GsmConnection对应一个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运行机制的核心就是要及时更新GsmConnection和GsmCall的状态,因此弄明白这两个对象的更新机制就会明白GsmCallTracker的更新机制。
        现在回到GsmCallTracker的构造方法中,刚才我们看到,GsmCallTracker在构造方法的最后注册了对通话和Radio状态的监听器,下面我们从这些监听器入手分析GsmCallTracker的运行机制。
        我们先来分析第一个监听器:
  1. mCi.registerForCallStateChanged(this, EVENT_CALL_STATE_CHANGE, null);  
        这个监听器监听的是RIL层通话的状态,当有新的状态到来时(比如新的来电),就会通过EVENT_CALL_STATE_CHANGE消息通知到GsmCallTracker,然后就会在handleMessage中进行处理:
  1. public void handleMessage (Message msg) {  
  2.     AsyncResult ar;  
  3.     switch (msg.what) {  
  4.         case EVENT_REPOLL_AFTER_DELAY:  
  5.         case EVENT_CALL_STATE_CHANGE:  
  6.             //得到RIL层消息,通话状态有变  
  7.             pollCallsWhenSafe();  
  8.             break;  
  9.     }  
  10. }  
        然后就会调用pollCallsWhenSafe()方法去获取当前最新的通话状态,我们暂时不去分析pollCallsWhenSafe的流程,先来看看其他两个监听器的流程。
        第二个监听器监听的是Radio的可用状态:
  1. mCi.registerForOn(this, EVENT_RADIO_AVAILABLE, null);  
        当Radio的状态上来后,就会在handleMessage中处理:
  1. public void handleMessage (Message msg) {  
  2.     AsyncResult ar;  
  3.     switch (msg.what) {  
  4.         case EVENT_RADIO_AVAILABLE:  
  5.             handleRadioAvailable();  
  6.             break;  
  7.     }  
  8. }  
        然后进入handleRadioAvailable()中处理:
  1. protected void handleRadioAvailable() {  
  2.     pollCallsWhenSafe();  
  3. }  
        到这里我们发现,接下来的流程和第一个监听器的流程相同。
        然后我们看第三个监听器:
  1. mCi.registerForNotAvailable(this, EVENT_RADIO_NOT_AVAILABLE, null);  
        监听的是Radio的不可用状态,当监听的消息上来后,在handleMessage中处理:
  1. public void handleMessage (Message msg) {  
  2.     AsyncResult ar;  
  3.     switch (msg.what) {  
  4.         case EVENT_RADIO_NOT_AVAILABLE:  
  5.             handleRadioNotAvailable();  
  6.             break;  
  7.     }  
  8. }  
        然后会进入handleRadioNotAvailable()的流程:
  1. private void handleRadioNotAvailable() {  
  2.     pollCallsWhenSafe();  
  3. }  
        接下来又是pollCallsWhenSafe()的操作,到这里我们发现,在GsmCallTracker构造函数中注册的三个监听器,无论哪一个被触发都会进入pollCallsWhenSafe的流程,接下来的分析我们将会看到,GsmCallTracker将会主动请求最新的通话状态,然后根据当前状态去更新GsmConnection和GsmCall对象。
        我们现在来分析pollCallsWhenSafe()的流程,请注意,这个pollCallsWhenSafe()方法是在父类CallTracker中实现的:
  1. @CallTracker.java  
  2. protected void pollCallsWhenSafe() {  
  3.     mNeedsPoll = true;  
  4.     if (checkNoOperationsPending()) {  
  5.         //通过RILJ获取当前的最新通话状态  
  6.         mLastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);  
  7.         mCi.getCurrentCalls(mLastRelevantPoll);  
  8.     }  
  9. }  
        这里看到,在pollCallsWhenSafe中通过RILJ(也就是mCi)去向Modem查询当前的通话状态,并注册了回调的消息EVENT_POLL_CALLS_RESULT,当拿到Modem返回值后,就会再次通过handleMessage()来处理最新的通话状态:
  1. public void handleMessage (Message msg) {  
  2.     AsyncResult ar;  
  3.     switch (msg.what) {  
  4.         case EVENT_POLL_CALLS_RESULT:  
  5.             //拿到最新通话状态  
  6.             ar = (AsyncResult)msg.obj;  
  7.             if (msg == mLastRelevantPoll) {  
  8.                 mNeedsPoll = false;  
  9.                 mLastRelevantPoll = null;  
  10.                 handlePollCalls((AsyncResult)msg.obj);  
  11.             }  
  12.             break;  
  13.     }  
  14. }  
        然后将数据拿到后,交由handlePollCalls()来处理,在这个方法里,就需要将当前的Modem通话状态数据进行解析,更新GsmConnection和GsmCall对象:
  1. protected synchronized void handlePollCalls(AsyncResult ar) {  
  2.     List polledCalls;  
  3.     Connection newRinging = null//or waiting  
  4.     boolean hasNonHangupStateChanged = false;   // Any change besides  
  5.     boolean hasAnyCallDisconnected = false;  
  6.     boolean needsPollDelay = false;  
  7.     boolean unknownConnectionAppeared = false;  
  8.   
  9.     for (int i = 0, curDC = 0, dcSize = polledCalls.size() ; i < mConnections.length; i++) {  
  10.         //拿到当前GsmCallTracker中的通话线路  
  11.         GsmConnection conn = mConnections[i];  
  12.         DriverCall dc = null;  
  13.   
  14.         if (curDC < dcSize) {  
  15.             //拿到当前的Modem中的通话线路状态  
  16.             dc = (DriverCall) polledCalls.get(curDC);  
  17.             if (dc.index == i+1) {  
  18.                 curDC++;  
  19.             } else {  
  20.                 dc = null;  
  21.             }  
  22.         }  
  23.   
  24.   
  25.         if (conn == null && dc != null) {  
  26.             if (mPendingMO != null && mPendingMO.compareTo(dc)) {  
  27.                 //mConnections中没有当前线路,而且当前线路是匹配mPendingMO的,说明是最新发起的呼出线路  
  28.                 mConnections[i] = mPendingMO;  
  29.                 mPendingMO.mIndex = i;  
  30.                 mPendingMO.update(dc);  
  31.                 mPendingMO = null;  
  32.   
  33.                 if (mHangupPendingMO) {  
  34.                     //是否在呼出之后用户立刻挂断了线路  
  35.                     mHangupPendingMO = false;  
  36.                     try {  
  37.                         //挂断这通线路  
  38.                         hangup(mConnections[i]);  
  39.                     } catch (CallStateException ex) {  
  40.                     }  
  41.                     return;  
  42.                 }  
  43.             } else {  
  44.                 //Modem中有该线路,而GsmConnection中没有该线路,说明有新的通话来临,需要创建新的线路连接  
  45.                 mConnections[i] = new GsmConnection(mPhone.getContext(), dc, this, i);  
  46.                 if (mConnections[i].getCall() == mRingingCall) {  
  47.                     //新来电  
  48.                     newRinging = mConnections[i];  
  49.                 } else {  
  50.                     //异常通话线路  
  51.                     if (dc.state != DriverCall.State.ALERTING && dc.state != DriverCall.State.DIALING) {  
  52.                         mConnections[i].onConnectedInOrOut();  
  53.                         if (dc.state == DriverCall.State.HOLDING) {  
  54.                             mConnections[i].onStartedHolding();  
  55.                         }  
  56.                     }  
  57.                     unknownConnectionAppeared = true;  
  58.                 }  
  59.             }  
  60.             hasNonHangupStateChanged = true;  
  61.         } else if (conn != null && dc == null) {  
  62.             //Modem中已经没有当前的链接,说明该线路已经被挂断,需要从mConnections中删除(置为null)  
  63.             mDroppedDuringPoll.add(conn);  
  64.             mConnections[i] = null;  
  65.         } else if (conn != null && dc != null && !conn.compareTo(dc)) {  
  66.             //Modem中的链接信息与当前的不匹配,可能发生了掉话或者新的通话  
  67.             mDroppedDuringPoll.add(conn);  
  68.             //需要创建新的链接  
  69.             mConnections[i] = new GsmConnection (mPhone.getContext(), dc, this, i);  
  70.   
  71.             if (mConnections[i].getCall() == mRingingCall) {  
  72.                 newRinging = mConnections[i];  
  73.             }  
  74.             hasNonHangupStateChanged = true;  
  75.         } else if (conn != null && dc != null) {  
  76.             //当前线路与Modem匹配,更新当前的链路信息  
  77.             boolean changed;  
  78.             changed = conn.update(dc);  
  79.             hasNonHangupStateChanged = hasNonHangupStateChanged || changed;  
  80.         }  
  81.   
  82.     }  
  83.   
  84.     //异常  
  85.     if (mPendingMO != null) {  
  86.         mDroppedDuringPoll.add(mPendingMO);  
  87.         mPendingMO = null;  
  88.         mHangupPendingMO = false;  
  89.     }  
  90.   
  91.     if (newRinging != null) {  
  92.         //新的来电,需要通知registerForNewRingingConnection的监听者  
  93.         mPhone.notifyNewRingingConnection(newRinging);  
  94.     }  
  95.   
  96.     //对于挂断的链接,需要标明挂断的原因  
  97.     for (int i = mDroppedDuringPoll.size() - 1; i >= 0 ; i--) {  
  98.         GsmConnection conn = mDroppedDuringPoll.get(i);  
  99.   
  100.         if (conn.isIncoming() && conn.getConnectTime() == 0) {  
  101.             // Missed or rejected call  
  102.             Connection.DisconnectCause cause;  
  103.             if (conn.mCause == Connection.DisconnectCause.LOCAL) {  
  104.                 //被拒掉  
  105.                 cause = Connection.DisconnectCause.INCOMING_REJECTED;  
  106.             } else {  
  107.                 //未接来电  
  108.                 cause = Connection.DisconnectCause.INCOMING_MISSED;  
  109.             }  
  110.   
  111.             mDroppedDuringPoll.remove(i);  
  112.             hasAnyCallDisconnected |= conn.onDisconnect(cause);  
  113.         } else if (conn.mCause == Connection.DisconnectCause.LOCAL  
  114.                 || conn.mCause == Connection.DisconnectCause.INVALID_NUMBER) {  
  115.             mDroppedDuringPoll.remove(i);  
  116.             hasAnyCallDisconnected |= conn.onDisconnect(conn.mCause);  
  117.         }  
  118.     }  
  119.   
  120.     if (mDroppedDuringPoll.size() > 0) {  
  121.         mCi.getLastCallFailCause( obtainNoPollCompleteMessage(EVENT_GET_LAST_CALL_FAIL_CAUSE));  
  122.     }  
  123.     if (needsPollDelay) {  
  124.         pollCallsAfterDelay();  
  125.     }  
  126.     if (newRinging != null || hasNonHangupStateChanged || hasAnyCallDisconnected) {  
  127.         internalClearDisconnected();  
  128.     }  
  129.   
  130.     //更新通话状态  
  131.     updatePhoneState();  
  132.   
  133.     if (unknownConnectionAppeared) {  
  134.         mPhone.notifyUnknownConnection();  
  135.     }  
  136.   
  137.     if (hasNonHangupStateChanged || newRinging != null || hasAnyCallDisconnected) {  
  138.         mPhone.notifyPreciseCallStateChanged();  
  139.     }  
  140.   
  141. }  
        上面的更新过程中,用从Modem获取到的通话线路信息与mConnections中存储的信息做对比,从而更新mConnections中线路的状态,比如:
        1、Modem中存在,而mConnections中不存在,则说明是新来电,或者新的去电,需要在mConnections中创建新的GsmConnection对象;
        2、Modem中不存在,而mConnections中存在,说明该线路已经被挂断,需要从mConnections中删除该线路的GsmConnection对象;
        3、Modem中和mConnections都存在,但是信息不匹配,则说明该线路的状态有改变,需要在mConnections中更新信息;
        更新线路之后,对于最新挂断的线路,还需要更新挂断的原因,比如是被对方拒接还是未接的来电,然后在更新的最后,通知所有监听者,Radio状态已经改变。我们简单看一下通知的过程:
  1. private void updatePhoneState() {  
  2.     PhoneConstants.State oldState = mState;  
  3.   
  4.     if (mRingingCall.isRinging()) {  
  5.         //响铃状态  
  6.         mState = PhoneConstants.State.RINGING;  
  7.     } else if (mPendingMO != null || !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) {  
  8.         //通话状态  
  9.         mState = PhoneConstants.State.OFFHOOK;  
  10.     } else {  
  11.         //待机状态  
  12.         mState = PhoneConstants.State.IDLE;  
  13.     }  
  14.   
  15.     if (mState == PhoneConstants.State.IDLE && oldState != mState) {  
  16.         mVoiceCallEndedRegistrants.notifyRegistrants( new AsyncResult(nullnullnull));  
  17.     } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {  
  18.         mVoiceCallStartedRegistrants.notifyRegistrants ( new AsyncResult(nullnullnull));  
  19.     }  
  20.   
  21.     if (mState != oldState) {  
  22.         //状态有更新,通过Phone对象发送给监听者  
  23.         mPhone.notifyPhoneStateChanged();  
  24.     }  
  25. }  
        其实通知的过程就是通过mPhone的notifyPhoneStateChanged()方法来实现,这里的mPhone,也就是GSMPhone对象,会把该广播发送给监听者们。
        以上就是GsmCallTracker的更新机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值