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.java的sendBurstDtmf 方法发起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 方法释放锁
- 再接着就是挂断电话的流程了,所以本文也就分析到这里。
最后,贴出自动拨打分机流程的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