Android 4.4 自动拨打分机流程分析

Android 分析,现在我们只关注framework层,以CDMA为例,GSM同理。
至于什么是自动拨打分机,如下图,输入一个电话号码,再选择“等待时间延长2秒”,就会显示一个分号,接着就可以输入分机号码了
输入界面


http://blog.csdn.net/linyongan


好了直接进入正题吧,在上面输入完号码之后,点击“拨号键”就开始拨号了
1. 至于 ,本文就不分析了,前面有文章讲得比较详细,在这里值贴出log

08-17 17:34:37.718 D/RILJ    ( 2941): [5765]> DIAL
08-17 17:34:37.831 D/RILJ    ( 2941): [5765]< DIAL 
08-17 17:34:37.867 D/RILJ    ( 2941): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
08-17 17:34:38.058 D/RILJ    ( 2941): [5766]> GET_CURRENT_CALLS
08-17 17:34:38.061 D/RILJ    ( 2941): [5766]< GET_CURRENT_CALLS 
[id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 
08-17 17:34:39.248 D/RILJ    ( 2941): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
08-17 17:34:40.376 D/RILJ    ( 2941): [5768]> GET_CURRENT_CALLS
08-17 17:34:40.383 D/RILJ    ( 2941): [5768]< GET_CURRENT_CALLS
[id=1,ACTIVE,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 

时序图:

2.1 我们重点要关注的是RIL收到底层发来的UNSOL_CDMA_INFO_REC消息,RIL.java的processUnsolicited方法处理完之后,通知CdmaCallTracker,在 CdmaCallTracker handleMessage 方法里进行响应

public void handleMessage (Message msg) {
        ...
        switch (msg.what) {
        ...
         case EVENT_CDMA_INFO_REC:
         if (mCallAnsweredRegistrants != null){
                mCallAnsweredRegistrants.notifyRegistrants();
                }
         onControlInfoRec();
         break;
     ...
}

2.2 来到CdmaCallTracker.java的 onControlInfoRec 方法里

private void onControlInfoRec() {
        if (mState == PhoneConstants.State.OFFHOOK) {
            Rlog.d(LOG_TAG, "on accepted, reset connection time");
            CdmaConnection c = (CdmaConnection) mForegroundCall.getLatestConnection();
            if (c.getDurationMillis() > 0 && !c.isConnectionTimerReset() 
            && !c.isIncoming()) {
                c.resetConnectionTimer();
                mPhone.notifyPreciseCallStateChanged();
                c.processNextPostDialChar();
            }
        }
}

第2小节的log是

08-17 17:34:45.661 D/RILJ    ( 2941): [UNSL]< UNSOL_CDMA_INFO_REC 
com.android.internal.telephony.cdma.CdmaInformationRecords@41b83748
08-17 17:34:45.661 D/RILJ    ( 2941): [UNSL]< UNSOL_CDMA_INFO_REC 
CdmaLineControlInfoRec: { lineCtrlPolarityIncluded: 1 lineCtrlToggle: 0 
lineCtrlReverse: 0 lineCtrlPowerDenial: 0 }
08-17 17:34:45.661 D/CdmaCallTracker( 2941): on accepted, reset connection time

3.1 接着进入CdmaConnection的resetConnectionTimer方法,这个不是重点,重点看 processNextPostDialChar 方法,processNextPostDialChar方法将会多次被调用,目前我们是 进入这个方法里

void processNextPostDialChar() {
        char c = 0;
        Registrant postDialHandler;

        if (mPostDialState == PostDialState.CANCELLED) {
            releaseWakeLock();
            //Rlog.v("CDMA", "##### processNextPostDialChar: postDialState == CANCELLED, bail");
            return;
        }

        if (mPostDialString == null ||
                mPostDialString.length() <= mNextPostDialChar) {
            setPostDialState(PostDialState.COMPLETE);

            // We were holding a wake lock until pause-dial was complete, so give it up now
            releaseWakeLock();

            // notifyMessage.arg1 is 0 on complete
            c = 0;
        } else {
            boolean isValid;

            setPostDialState(PostDialState.STARTED);

            c = mPostDialString.charAt(mNextPostDialChar++);

            isValid = processPostDialChar(c);
            try {
                android.util.Log.w("bai", "mNextPostDialChar:" + mNextPostDialChar);
                Thread.sleep(WAIT_DELAY_MILLIS);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (!isValid) {
                // Will call processNextPostDialChar
                mHandler.obtainMessage(EVENT_NEXT_POST_DIAL).sendToTarget();
                // Don't notify application
                Rlog.e("CDMA", "processNextPostDialChar: c=" + c + " isn't valid!");
                return;
            }
        }

        postDialHandler = mOwner.mPhone.mPostDialHandler;

        Message notifyMessage;

        if (postDialHandler != null &&
                (notifyMessage = postDialHandler.messageForRegistrant()) != null) {
            // The AsyncResult.result is the Connection object
            PostDialState state = mPostDialState;
            AsyncResult ar = AsyncResult.forMessage(notifyMessage);
            ar.result = this;
            ar.userObj = state;

            // arg1 is the character that was/is being processed
            notifyMessage.arg1 = c;

            notifyMessage.sendToTarget();
        }
}    

打印出的log是

08-17 17:34:45.662 W/bai     ( 2941): mNextPostDialChar:1

这个方法比较重要就全部贴出来,前面两个if语句都不会进去,直接来到else里,

 setPostDialState(PostDialState.STARTED);
 c = mPostDialString.charAt(mNextPostDialChar++);
 isValid = processPostDialChar(c);

3.2 mPostDialString存储的是逗号及分机的内容,即“,820716”;.charAt方法是把一个字符串的第几位取出来,mNextPostDialChar一开始的值是0。好吧,我们接着看 processPostDialChar 方法

   /**
     * Performs the appropriate action for a post-dial char, but does not
     * notify application. returns false if the character is invalid and
     * should be ignored
     */
    private boolean
    processPostDialChar(char c) {
        if (PhoneNumberUtils.is12Key(c)) {
            //mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
            String dtmfStr = Character.toString(c);
            mOwner.mCi.sendBurstDtmf(dtmfStr, 0, 0, 
            mHandler.obtainMessage(EVENT_DTMF_DONE));

        } else if (c == PhoneNumberUtils.PAUSE) {
            setPostDialState(PostDialState.PAUSE);
            // Upon occurrences of the separator, the UE shall
            // pause again for 2 seconds before sending any
            // further DTMF digits.
            mHandler.sendMessageDelayed(
            mHandler.obtainMessage(EVENT_PAUSE_DONE),
            PAUSE_DELAY_MILLIS);
        } else if (c == PhoneNumberUtils.WAIT) {
            setPostDialState(PostDialState.WAIT);
        } else if (c == PhoneNumberUtils.WILD) {
            setPostDialState(PostDialState.WILD);
        } else {
            return false;
        }
        return true;
}

3.3 因为我们从mPostDialString取出来的第一个字符是逗号“,”,而逗号对应的是PhoneNumberUtils.PAUSE,所以会进入这个if语句,在这里,2秒之后会发送一个消息类型为EVENT_PAUSE_DONE的Message,所以会在内部类MyHandler 的 handleMessage 里处理这个消息

 class MyHandler extends Handler {
        MyHandler(Looper l) {super(l);}
        @Override
        public void
        handleMessage(Message msg) {
            switch (msg.what) {
                case EVENT_NEXT_POST_DIAL:
                case EVENT_DTMF_DONE:
                case EVENT_PAUSE_DONE:
                    processNextPostDialChar();
                    break;
                case EVENT_WAKE_LOCK_TIMEOUT:
                    releaseWakeLock();
                    break;
            }
        }
    }

3.4 看到了吧,又回到了 processNextPostDialChar 方法,看3.1的代码,这是我们 进入这个方法;又是跳过两个if语句,直接来到else里,此时,从mPostDialString取出来的第二个字符是8。接着把8传入到 processPostDialChar 方法里,看3.2的代码,看第一个if语句

if (PhoneNumberUtils.is12Key(c)) {
//mOwner.mCi.sendDtmf(c, mHandler.obtainMessage(EVENT_DTMF_DONE));
String dtmfStr = Character.toString(c);
mOwner.mCi.sendBurstDtmf(dtmfStr, 0, 0, mHandler.obtainMessage(EVENT_DTMF_DONE));
} 

/** True if c is ISO-LATIN characters 0-9, *, # */
    public final static boolean is12Key(char c) {
        return (c >= '0' && c <= '9') || c == '*' || c == '#';
}

看is12Key方法,因为8是在0~9这个范围里的,所以这个方法返回true,所以会进入第一个if语句里,看到mCi,我们就知道下面会调用 RIL.javasendBurstDtmf 方法发起Tone音请求

public void
    sendBurstDtmf(String dtmfString, int on, int off, Message result) {
        RILRequest rr = RILRequest.obtain(RIL_REQUEST_CDMA_BURST_DTMF, result);
        rr.mParcel.writeInt(3);
        rr.mParcel.writeString(dtmfString);
        rr.mParcel.writeString(Integer.toString(on));
        rr.mParcel.writeString(Integer.toString(off));
        if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                + " : " + dtmfString);
        send(rr);
}

一直重复循环5次(加上上面那次共6次)processNextPostDialChar方法
对应的log

08-17 17:34:47.662 D/RILJ    ( 2941): [5774]> RIL_REQUEST_CDMA_BURST_DTMF : 8
08-17 17:34:47.667 W/bai     ( 2941): mNextPostDialChar:2
08-17 17:34:47.679 D/RILJ    ( 2941): [5774]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:48.277 D/RILJ    ( 2941): [5775]> RIL_REQUEST_CDMA_BURST_DTMF : 2
08-17 17:34:48.280 W/bai     ( 2941): mNextPostDialChar:3
08-17 17:34:48.285 D/RILJ    ( 2941): [5775]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:49.077 D/RILJ    ( 2941): [5776]> RIL_REQUEST_CDMA_BURST_DTMF : 0
08-17 17:34:49.082 W/bai     ( 2941): mNextPostDialChar:4
08-17 17:34:49.086 D/RILJ    ( 2941): [5776]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:49.711 D/RILJ    ( 2941): [5777]> RIL_REQUEST_CDMA_BURST_DTMF : 7
08-17 17:34:49.713 W/bai     ( 2941): mNextPostDialChar:5
08-17 17:34:49.718 D/RILJ    ( 2941): [5777]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:50.316 D/RILJ    ( 2941): [5778]> RIL_REQUEST_CDMA_BURST_DTMF : 1
08-17 17:34:50.318 W/bai     ( 2941): mNextPostDialChar:6
08-17 17:34:50.323 D/RILJ    ( 2941): [5778]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:50.919 D/RILJ    ( 2941): [5779]> RIL_REQUEST_CDMA_BURST_DTMF : 6
08-17 17:34:50.926 W/bai     ( 2941): mNextPostDialChar:7
08-17 17:34:50.931 D/RILJ    ( 2941): [5779]< RIL_REQUEST_CDMA_BURST_DTMF 

3.5 等到把mPostDialString最后一个字符取出来,发起Tone音请求之后,还会 进入processNextPostDialChar方法里,看3.1的代码的第二个if语句

if (mPostDialString == null ||
    mPostDialString.length() <= mNextPostDialChar) {
     setPostDialState(PostDialState.COMPLETE);
     // We were holding a wake lock until pause-dial was complete,
      so give it up now
     releaseWakeLock();
     // notifyMessage.arg1 is 0 on complete
     c = 0;
}

因为mPostDialString.length()是7,mNextPostDialChar的值也是7,所以这个判断条件为真,会进入这个if语句,在这里,因为要发的Tone音都发完了,所以调用 releaseWakeLock 方法释放锁


  1. 再接着就是挂断电话的流程了,所以本文也就分析到这里。
    最后,贴出自动拨打分机流程的log片段(拨打的电话是02568033931,分机是820716)
08-17 17:34:37.718 D/RILJ    ( 2941): [5765]> DIAL
08-17 17:34:37.831 D/RILJ    ( 2941): [5765]< DIAL 
08-17 17:34:37.867 D/RILJ    ( 2941): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
08-17 17:34:38.058 D/RILJ    ( 2941): [5766]> GET_CURRENT_CALLS
08-17 17:34:38.061 D/RILJ    ( 2941): [5766]< GET_CURRENT_CALLS  
[id=1,DIALING,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 
08-17 17:34:39.248 D/RILJ    ( 2941): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
08-17 17:34:40.376 D/RILJ    ( 2941): [5768]> GET_CURRENT_CALLS
08-17 17:34:40.383 D/RILJ    ( 2941): [5768]< GET_CURRENT_CALLS  
[id=1,ACTIVE,toa=129,norm,mo,0,voc,noevp,,cli=1,,0] 

08-17 17:34:45.661 D/RILJ    ( 2941): [UNSL]< UNSOL_CDMA_INFO_REC
 com.android.internal.telephony.cdma.CdmaInformationRecords@41b83748
08-17 17:34:45.661 D/RILJ    ( 2941): [UNSL]< UNSOL_CDMA_INFO_REC
 CdmaLineControlInfoRec: { lineCtrlPolarityIncluded: 1 lineCtrlToggle: 0 
 lineCtrlReverse: 0 lineCtrlPowerDenial: 0 }
08-17 17:34:45.661 D/CdmaCallTracker( 2941): on accepted, reset connection time
08-17 17:34:45.661 D/CdmaConnection( 2941): [CDMAConn] CdmaConnection time reseted
08-17 17:34:45.662 W/bai     ( 2941): mNextPostDialChar:1
08-17 17:34:47.662 D/RILJ    ( 2941): [5774]> RIL_REQUEST_CDMA_BURST_DTMF : 8
08-17 17:34:47.667 W/bai     ( 2941): mNextPostDialChar:2
08-17 17:34:47.679 D/RILJ    ( 2941): [5774]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:48.277 D/RILJ    ( 2941): [5775]> RIL_REQUEST_CDMA_BURST_DTMF : 2
08-17 17:34:48.280 W/bai     ( 2941): mNextPostDialChar:3
08-17 17:34:48.285 D/RILJ    ( 2941): [5775]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:49.077 D/RILJ    ( 2941): [5776]> RIL_REQUEST_CDMA_BURST_DTMF : 0
08-17 17:34:49.082 W/bai     ( 2941): mNextPostDialChar:4
08-17 17:34:49.086 D/RILJ    ( 2941): [5776]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:49.711 D/RILJ    ( 2941): [5777]> RIL_REQUEST_CDMA_BURST_DTMF : 7
08-17 17:34:49.713 W/bai     ( 2941): mNextPostDialChar:5
08-17 17:34:49.718 D/RILJ    ( 2941): [5777]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:50.316 D/RILJ    ( 2941): [5778]> RIL_REQUEST_CDMA_BURST_DTMF : 1
08-17 17:34:50.318 W/bai     ( 2941): mNextPostDialChar:6
08-17 17:34:50.323 D/RILJ    ( 2941): [5778]< RIL_REQUEST_CDMA_BURST_DTMF 
08-17 17:34:50.919 D/RILJ    ( 2941): [5779]> RIL_REQUEST_CDMA_BURST_DTMF : 6
08-17 17:34:50.926 W/bai     ( 2941): mNextPostDialChar:7
08-17 17:34:50.931 D/RILJ    ( 2941): [5779]< RIL_REQUEST_CDMA_BURST_DTMF 

08-17 17:34:53.115 D/CdmaCallTracker( 2941): [CdmaCallTracker]
 hangupForegroundResumeBackground
08-17 17:34:53.115 D/RILJ    ( 2941): [5780]> HANGUP_FOREGROUND_RESUME_BACKGROUND
08-17 17:34:53.136 D/RILJ    ( 2941): [5780]< HANGUP_FOREGROUND_RESUME_BACKGROUND 
08-17 17:34:53.151 D/RILJ    ( 2941): [UNSL]< UNSOL_RESPONSE_CALL_STATE_CHANGED
08-17 17:34:53.151 D/RILJ    ( 2941): [5781]> GET_CURRENT_CALLS
08-17 17:34:53.155 D/RILJ    ( 2941): [5781]< GET_CURRENT_CALLS  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值