分享

【转】android Call(MO)流程介绍

 techres 2012-05-07

android Call(MO)流程介绍

分类: android linux 126人阅读 评论(0) 收藏 举报
网上有很多的资料,我这里整理一份属于自己的。当然其中的很多内容还是来自网络,无所不能的网络啊~~~


很多人的文章写Call的流程的时候,都是从OutgoingCallBroadcaster.java开始到RIL.java结束,java部分的确是这样。但为了更完美我将补充前面的TwelveKeyDialer.java,
及后续的RIL层的AT指令,这样便一条路走到尽头(其实GSM/CDMA模块中还有,这部分暂时不提)。
全部的代码在四个部分:package/../Contacts ,package/../Phone,frameworks/base/telephony/java/com/android/internal/telephony 及hardware/ril/reference-ril

文章中粘贴的代码,由于从2.0到2.2的移植,所以有些代码并不是和2.2源码中一致。
1、界面部分的TwelveKeyDialer.java,extends Activity implements View.OnClickListener...
这一部分在Contacts这个APP中,在你一个个按下按键时,可以从onClick()函数中看到会响按键音、接着afterTextChanged()会匹配联系人中包含你按下数字的联系人
当你按下dial键的时候则会进入Call的流程中。
  1.    void dialButtonPressed() {  
  2.        final String number = mDigits.getText().toString();  
  3.        boolean sendEmptyFlash = false;  
  4.        Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);  
  5.     ...  
  6.     intent.setData(Uri.fromParts("tel", number, null));  
  7.        ...  
  8.        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  9.        startActivity(intent);  
  10.     ...  
  11. }  

2、进入OutgoingCallBroadcaster.java,extends Activity
上面start intent时就进入OutgoingCallBroadcaster的onCreate(),首先判断电话号码是否为emergencyNumber
  1. if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {  
  2.     action = emergencyNumber  
  3.              Intent.ACTION_CALL_EMERGENCY  
  4.             : Intent.ACTION_CALL;  
  5.     intent.setAction(action);  
  6. }  

如果是紧急号码,将callNow变量赋值为true,立即启动InCallScreen。如不是则通过发送广播进入OutgoingCallReceiver.java
该类是一个内部类,作用是接收OutgoingCallBroadcaster发送的广播,判断是否已经启动InCallScreen。没有启动的话就进行一些初始化,如:对OTA进行初始化。
接收到广播之后,从Intent里面取出电话号码及其URi。然后设置Intent为ACTION_CALL,并带上号码和uri。启动InCallScreen。关闭该OutgoingCallBroadcaster。
  1.          Intent newIntent = new Intent(Intent.ACTION_CALL, uri);  
  2.          newIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);  
  3. ...  
  4.          newIntent.setClass(context, InCallScreen.class);  
  5.          newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);//注意这个flag保证通话界面这个activity同时只能显示一个  
  6. context.startActivity(newIntent);  

3 启动InCallScreen.java , extends Activity implements View.OnClickListener, View.OnTouchListener 
它主要类主要是负责通话的界面,包括通话时间的显示、联系人头像、电话号码、通话状态、DTMFTwelveKeyDialer及菜单等的现实
在Call流程中所起到的作用简单概述如下:得到action为ACTION_CALL、或ACTION_CALL_EMERGENCY,直接placeCall(intent),然后将状态返回.
  1.      } else if (action.equals(Intent.ACTION_CALL)  
  2.              || action.equals(Intent.ACTION_CALL_EMERGENCY)) {  
  3. ...  
  4.          InCallInitStatus status = placeCall(intent);  
  5.          if (status == InCallInitStatus.SUCCESS) {  
  6.              // Notify the phone app that a call is beginning so it can  
  7.              // enable the proximity sensor  
  8.              app.setBeginningCall(true);  
  9.          }  
  10.          return status;  

而在 placeCall(intent)中直接是调用PhoneUtils的接口
           callStatus = PhoneUtils.placeCall(mPhone, number, contactUri);
在接下来就只 Connection cn = phone.dial(number),这个就是调用Phone interface。
you cannot assume the audio path is connected until PhoneStateChanged notification has occurred.

4、接下来就进入到了framework中的code,我们已一个cdma 电话为例,总结一下相关的几个类
Phone 、CDMAPhone、PhoneBase、CdmaCallTracker、CdmaConnection、CallTracker、CommandsInterface、RIL、BaseCommands共8个类,我们先整理一下这些类的关系
本人较懒,就不画UML图了,直接文字表达一下,呵呵~~
public interface Phone
PhoneBase extends Handler implements Phone
CDMAPhone extends PhoneBase

所以可以看到CDMAPhone中的dial()函数如下    
  1. public Connection dial (String dialString) throws CallStateException {  
  2.         // Need to make sure dialString gets parsed properly  
  3.         String newDialString = PhoneNumberUtils.stripSeparators(dialString);  
  4.         return mCT.dial(newDialString);  
  5.     }  

mCT的定义也在CDMAPhone中 CdmaCallTracker mCT;
在mCT的dial()函数中有
  1. ...  
  2. pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall);  
  3. ...  
  4.           // In Ecm mode, if another emergency call is dialed, Ecm mode will not exit.  
  5.           if(!isPhoneInEcmMode || (isPhoneInEcmMode && isEmergencyCall)) {  
  6.               cm.dial(pendingMO.address, clirMode, obtainCompleteMessage());  
  7.           } else {  
  8. ...  
  9.           }  
  10.       }  
  11.       updatePhoneState();  
  12.       phone.notifyPreciseCallStateChanged();  

这段代码主要注意3点
首先是pendingMO = new CdmaConnection(phone.getContext(), dialString, this, foregroundCall),这个是创建一个Call的connection实例,然后将其加入到
一个connections的list中,因为每个网络支持好几通电话。
其次是obtainCompleteMessage() { return obtainCompleteMessage(EVENT_OPERATION_COMPLETE);},这个是要处理返回信息的

最后还更新了Phone的状态,phone call的状态一共就3个

  1. enum State {  
  2.         IDLE, RINGING, OFFHOOK;  
  3.     };  

再往下看cm.dial()
public final class CdmaCallTracker extends CallTracker
CallTracker中定义public CommandsInterface cm;
CommandsInterface为接口类 public interface CommandsInterface
public abstract class BaseCommands implements CommandsInterface
public final class RIL extends BaseCommands implements CommandsInterface
由于以上的这些关系,最后走到RIL.java的dial部分,这段代码不多说,就是往socket中写东西。这里先记住RIL_REQUEST_DIAL
  1. public void  
  2. dial (String address, int clirMode, Message result) {  
  3.     RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);  
  4.   
  5.     rr.mp.writeString(address);  
  6.     rr.mp.writeInt(clirMode);  
  7.     rr.mp.writeInt(0); // UUS information is absent  
  8.   
  9.     if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));  
  10.   
  11.     send(rr);  
  12. }  

其实到了这,我们暂时不去分析RIL层。先假设如果这通电话成功播出的话,应该是一个ATD<dial string>下去,然后返回一个OK。这个在上层是怎么处理的呢?
在RIL.java中有RILSender extends Handler implements Runnable 和RILReceiver implements Runnable,其实发送就是通过RILSender往socket中写data。
而接收AT就是就是从socket中读取data,然后处理。这里返回的OK会在processResponse()->processSolicited()中处理。如果返回没有错误最终是
走rr.mResult.sendToTarget(),然后出发CdmaCallTracker.java中的msg(EVENT_OPERATION_COMPLETE),可以看到
CdmaCallTracker.java中有handlemessage()
  1. ...  
  2.        case EVENT_OPERATION_COMPLETE:  
  3.               operationComplete();  
  4.           break;  

在operationComplete()中其实是将pendingOperations计数减1,然后获取当前的calls(这个一般对应AT+CLCC).这样便知道此时该网络的通话情况。
  1. pendingOperations--;  
  2.     ...  
  3.       if (pendingOperations == 0 && needsPoll) {  
  4.           lastRelevantPoll = obtainMessage(EVENT_POLL_CALLS_RESULT);  
  5.           cm.getCurrentCalls(lastRelevantPoll);  
  6.       } else if (pendingOperations < 0) {  
  7.     ...  
  8.       }  

5、接下来,步入正轨。进入RIL层。
这里涉及的几个文件为
Ril_commands.h 中一个映射结构{RIL_REQUEST_DIAL, dispatchDial, responseVoid}
Ril.h 主要定义一个宏 #define RIL_REQUEST_DIAL 10 ,其实这个必须和java层的RILConstants.java中定义的int RIL_REQUEST_DIAL = 10;一致,并且在
Ril_commands.h 中的顺序也应从上数第10行,不然的话会出错。这一点,设计的确实有些呆板。
Ril.cpp case RIL_REQUEST_DIAL: return "DIAL",这个是要是一个请求转换为string的操作
Reference-ril.c 中的
  1. static void  
  2. onRequest (int request, void *data, size_t datalen, RIL_Token t)  
  3.         ...  
  4.         case RIL_REQUEST_DIAL:  
  5.             requestDial(data, datalen, t);  
  6.             break;  
  7.         ...  

在requestDial()中
  1. static void requestDial(void *data, size_t datalen, RIL_Token t)  
  2. {  
  3.     ...  
  4.     p_dial = (RIL_Dial *)data;  
  5.     ...  
  6.     asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);  
  7.   
  8.   
  9.     ret = at_send_command(cmd, NULL);  
  10.     free(cmd);  
  11.     ...  
  12.     RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);  
  13. }  

这样一个ATD通过at_send_command()写向串口(最终调用的是系统函数writeline()),剩下的就交给CDMA模块了。呵呵~~

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多