开始看代码。
TwelveKeyDialer.java,既然要打电话,总要先输入号码才拨出,这个类就是拨号盘的界面,只是这个phone用到的类却是放在com.android.contacts包下,应该是出于代码结构的考虑吧。毕竟联系人、拨号盘、通话记录和收藏都是在一个Tab标签里的。这个界面没什么好说的,0-9数字键,P和W(也可能是*和#),P表示直接拨打带有分机号的号码(如2345-0000P1234)时会直接拨分机号,无需要再输入分机号码,而W拨号(如23450000W1234)则会有对话框提示你确认是否拨分机号,就这点区别。
按完电话号码点拨号键,接下来就开始去电流程了,离开TwelveKeyDialer.java前的代码,
[java]
view plaincopy
- void dialButtonPressed(String ipPrefix) {
- Log.d(TAG, "dialButtonPressed");
-
-
- Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);
-
- intent.setData(Uri.fromParts("tel", number, null));
- StickyTabs.saveTab(this, getIntent());
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- mDigits.getText().clear();
- }
发出来的Intent谁来接收呢,是OutgoingCallBroadcaster.java类(其实拨打电话的方式很多,不一定是拨号盘拨号:可以从联系人拨号,可以是历史记录拨号等,无论是何种拨号,最后一定走向OutgoingCallBroadcaster.java),这是一个中间类,实际我们是看不到它的,这个类先判断号码是否为紧急号码,如果是紧急号码,启动InCallScree.java,并发送广播;如果不是,发送广播“Intent.ACTION_NEW_OUTGOING_CALL”由它的内部类OutgoingCallReceiver接收,从onReceiver()再到doReceiver(),会把action里的字符串统一替换成Intent.ACTION_CALL,启动InCallScreen.java,在这个类也可以做一些其它的业务逻辑判断(比如固定拨号,视频电话等),IncallScreen.java是电话应用的主界面,这个界面负责的东西比较多,所需要做的判断也不少,值得一提的是它重写finish()方法,当调用这个方法时它又把自己放回栈中,这样可提高下次启动的响应速度。
[java]
view plaincopy
- 如果是第一次进入IncallScreen,会执行onCreate()
- protected void onCreate(Bundle icicle) {
- Profiler.callScreenOnCreate();
- …..省略代码...
- setPhone(app.phone);
- mCM = PhoneApp.getInstance().mCM;
- mBluetoothHandsfree = app.getBluetoothHandsfree();
- if (mBluetoothHandsfree != null) {
- mBluetoothHeadset = new BluetoothHeadset(this, null);
- }
- initInCallScreen();
- …..省略代码...
- registerForPhoneStates();
- if (icicle == null) {
- mInCallInitialStatus = internalResolveIntent(getIntent());
- } else {
- mInCallInitialStatus = InCallInitStatus.SUCCESS;
- }
- mUseTouchLockOverlay = !app.proximitySensorModeEnabled();
- Profiler.callScreenCreated();
- }
- 如果是第二次进入会执行onNewIntent()
- protected void onNewIntent(Intent intent) {
- mInCallInitialStatus = internalResolveIntent(intent);
- ……去掉log代码
- }
两个方法都会走到internalResolveIntent(intent),这里我们关心电话拨出的动作是怎么跑下去的,所以InCallScreen里的onResume()方法就不细看了,那里面有关于锁屏和蓝牙连接等逻辑判断。回来继续看internalResolveIntent()的代码。
[java]
view plaincopy
- InCallInitStatus internalResolveIntent(Intent intent) {
- ……省略很多代码… 传过来的action是Intent.ACTION_CALL,直接看重点
- } else if (action.equals(Intent.ACTION_CALL)
- || action.equals(Intent.ACTION_CALL_EMERGENCY)) {
- app.setRestoreMuteOnInCallResume(false);
- if (PhoneUtils.hasPhoneProviderExtras(intent)) {
- InCallInitStatus status = placeCall(intent);
- if (status == InCallInitStatus.SUCCESS) {
- app.setBeginningCall(true);
- }
- return status;
- } }
(当我看到这篇文章的时候,也按照作者的思路看代码,发现代码不一样,本文的代码是gingerbread的代码,我看的是四核代码,版本应该是4.2的。四核的代码里
internalResolveIntent()并没有执行
InCallInitStatus status = placeCall(intent); 这句,看了注释,
placeCall()方法在callcontroller.java里面,后续流程大致相同)
进入placeCall()方法后,会做一些关于是否紧急号码和OTA(Over-the-Air Technology空中下载技术,是通过移动通信(GSM或CDMA)的空中接口对SIM卡数据及应用进行远程管理的技术)的判断,不过我们更关心下面的代码。
[java]
view plaincopy
- if (null != mProviderGatewayUri && !(isEmergencyNumber || isEmergencyIntent) &&
- PhoneUtils.isRoutableViaGateway(number)) {
- callStatus = PhoneUtils.placeCallVia(this, phone, number,
- contactUri, mProviderGatewayUri);
- } else {
- callStatus = PhoneUtils.placeCall(phone, number, contactUri);
- }
后面switch分支会根据callStatus的值完成相应的功能或提示。到这里到代码就走到PhoneUtils.java里了,placeCall()是一个静态方法调用。里面最重要的代码是
[java]
view plaincopy
- Connection cn = PhoneApp.getInstance().mCM.dial(phone, number);
-
- Connection cn = phone.dial(number)
顺便提下在这个方法里还会有setAudioMode(),关于音频通道和模式的设置就在这里,这里面的故事也不少,不过要先放放了。下面的代码已经在Framework层了,不管之前的代码在哪里dial,最后都会来到RIL.java。
[java]
view plaincopy
- public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {
- RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);
- rr.mp.writeString(address);
- rr.mp.writeInt(clirMode);
- rr.mp.writeInt(0);
-
- if (uusInfo == null) {
- rr.mp.writeInt(0);
- } else {
- rr.mp.writeInt(1);
- rr.mp.writeInt(uusInfo.getType());
- rr.mp.writeInt(uusInfo.getDcs());
- rr.mp.writeByteArray(uusInfo.getUserData());
- }
- send(rr);
- }
跟着RIL_REQUEST_DIAL这个TAG标志向下走来到Reference-ril.c找到相应的case分支,
[java]
view plaincopy
- case RIL_REQUEST_DIAL:
- requestDial(data, datalen, t);
-
- static void requestDial(void *data, size_t datalen, RIL_Token t){
- p_dial = (RIL_Dial *)data;
-
- switch (p_dial->clir) {
- case 1: clir = "I"; break;
- case 2: clir = "i"; break;
- default:
- case 0: clir = ""; break;
- }
- asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);
- ret = at_send_command(cmd, NULL);
- free(cmd);
- RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
- }
到这里我们的电话基本上算是拨出去了,至于是否成功还要看返回结果,通常形式上的流程是这样的(下面只是很多可能中的一种,也是比较常见的一种),
[java]
view plaincopy
- ===>>[SendAT] ATD15812345678
- <<====[RecvAT] OK
- <<====[RecvAT] +CLCC: 1, 0, 2, 0, 0, "15812345678", 129,
- ===>>[SendAT] AT+CMUT=0, time=
- <<====[RecvAT] OK, time=
- ===>>[SendAT] AT+CLCC
- <<====[RecvAT] +CLCC: 1, 0, 2, 0, 0, "15812345678", 129,
这段代码描述这样一个事实,不管模块报上来CLCC数据如何,我们都会重新查询后再上报给应用层,上层收到消息后更新界面和维护Connection里每一路电话的状态。到这一步,去电的流程也算是基本走完了,后面还有些界面状态刷新的代码省略了,对于AT命令那一部分,具体实现要看芯片厂商,表现上不完全一致。
转自 http://blog.csdn.net/baimy1985/article/details/7853268