现在的位置: 首页 > 综合 > 正文

android — phone (二) 去电流程

2018年04月21日 ⁄ 综合 ⁄ 共 5834字 ⁄ 字号 评论关闭

http://blog.csdn.net/baimy1985/article/details/7853268

  这篇是关于外拨电话的具体流程,也就是去电流程,虽然网上的资料很多(重复的也很多),但作为电话的主要操作之一,为了保证phone系列的完整性,还是要把它写一下的。开始看代码。

TwelveKeyDialer.java,既然要打电话,总要先输入号码才拨出,这个类就是拨号盘的界面,只是这个phone用到的类却是放在com.android.contacts包下,应该是出于代码结构的考虑吧。毕竟联系人、拨号盘、通话记录和收藏都是在一个Tab标签里的。这个界面没什么好说的,0-9数字键,P和W(也可能是*和#),P表示直接拨打带有分机号的号码(如2345-0000P1234)时会直接拨分机号,无需要再输入分机号码,而W拨号(如23450000W1234)则会有对话框提示你确认是否拨分机号,就这点区别。

       按完电话号码点拨号键,接下来就开始去电流程了,离开TwelveKeyDialer.java前的代码,

[java] view
plain
copy

  1. void dialButtonPressed(String ipPrefix) {  
  2.     Log.d(TAG, "dialButtonPressed");     
  3.     //注意这个action,只有从拨号盘拨出的才是Intent.ACTION_CALL_PRIVILEGED  
  4.     //外部调用的是Intent.ACTION_CALL  
  5.     Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED);    
  6.     //省略次要代码……  
  7.     intent.setData(Uri.fromParts("tel", number, null));//拼一个电话的Uri,关键代码  
  8.     StickyTabs.saveTab(this, getIntent());//与拨号无关  
  9.     intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
  10.     startActivity(intent);  
  11.     mDigits.getText().clear(); //清除输入  
  12.  }  

         发出来的Intent谁来接收呢,是OutgoingCallBroadcaster.java类,这是一个中间类,实际我们是看不到它的,这个类先判断号码是否为紧急号码,如果是紧急号码,启动InCallScree.java,并发送广播;如果不是,发送广播“Intent.ACTION_NEW_OUTGOING_CALL”由它的内部类OutgoingCallReceiver接收,从onReceiver()再到doReceiver(),会把action里的字符串统一替换成Intent.ACTION_CALL,启动InCallScreen.java,在这个类也可以做一些其它的业务逻辑判断(比如固定拨号,视频电话等),IncallScreen.java是电话应用的主界面,这个界面负责的东西比较多,所需要做的判断也不少,值得一提的是它重写finish()方法,当调用这个方法时它又把自己放回栈中,这样可提高下次启动的响应速度。

[java] view
plain
copy

  1. 如果是第一次进入IncallScreen,会执行onCreate()  
  2. protected void onCreate(Bundle icicle) {  
  3.     Profiler.callScreenOnCreate();//获得通话界面被创建的时间。  
  4.     …..省略代码...         
  5.     setPhone(app.phone);  // Sets mPhone  
  6.     mCM =  PhoneApp.getInstance().mCM;  
  7.     mBluetoothHandsfree = app.getBluetoothHandsfree();//设置蓝牙  
  8.     if (mBluetoothHandsfree != null) {             
  9.         mBluetoothHeadset = new BluetoothHeadset(thisnull);   
  10.     }  
  11.     initInCallScreen();   //加载界面元素  
  12.  …..省略代码...     
  13.     registerForPhoneStates();//注册各种状态  
  14.     if (icicle == null) {              
  15. mInCallInitialStatus = internalResolveIntent(getIntent());  //这个是关键代码           
  16.     } else {  
  17.         mInCallInitialStatus = InCallInitStatus.SUCCESS;  
  18.     }  
  19.     mUseTouchLockOverlay = !app.proximitySensorModeEnabled();  
  20.     Profiler.callScreenCreated();//记录通话界面创建完成后的时间  
  21. }  
  22. 如果是第二次进入会执行onNewIntent()  
  23. protected void onNewIntent(Intent intent) {        
  24.     mInCallInitialStatus = internalResolveIntent(intent);//这个是关键代码  
  25.     ……去掉log代码  
  26. }  

        两个方法都会走到internalResolveIntent(intent),这里我们关心电话拨出的动作是怎么跑下去的,所以InCallScreen里的onResume()方法就不细看了,那里面有关于锁屏和蓝牙连接等逻辑判断。回来继续看internalResolveIntent()的代码。

[java] view
plain
copy

  1. InCallInitStatus internalResolveIntent(Intent intent) {  
  2.     ……省略很多代码…   传过来的action是Intent.ACTION_CALL,直接看重点  
  3.     } else if (action.equals(Intent.ACTION_CALL)  
  4.             || action.equals(Intent.ACTION_CALL_EMERGENCY)) {  
  5.         app.setRestoreMuteOnInCallResume(false);  
  6.         if (PhoneUtils.hasPhoneProviderExtras(intent)) {                 
  7.         InCallInitStatus status = placeCall(intent);//这是我们要找的  
  8.         if (status == InCallInitStatus.SUCCESS) {          
  9.             app.setBeginningCall(true);  
  10.         }  
  11.         return status;  
  12.     } }  

        进入placeCall()方法后,会做一些关于是否紧急号码和OTA(Over-the-Air Technology空中下载技术,是通过移动通信(GSM或CDMA)的空中接口对SIM卡数据及应用进行远程管理的技术)的判断,不过我们更关心下面的代码。

[java] view
plain
copy

  1. if (null != mProviderGatewayUri && !(isEmergencyNumber || isEmergencyIntent) &&  
  2.      PhoneUtils.isRoutableViaGateway(number)) {    
  3.      callStatus = PhoneUtils.placeCallVia(this, phone, number,  
  4.        contactUri, mProviderGatewayUri);//多了个网关参数  
  5. else {  
  6.      callStatus = PhoneUtils.placeCall(phone, number, contactUri);//多数调用这里  
  7. }  

       后面switch分支会根据callStatus的值完成相应的功能或提示。到这里到代码就走到PhoneUtils.java里了,placeCall()是一个静态方法调用。里面最重要的代码是     

[java] view
plain
copy

  1. Connection cn = PhoneApp.getInstance().mCM.dial(phone, number);  
  2. //也有可能看到下面的代码  
  3. Connection cn = phone.dial(number)//如果是这种,会先走到GsmCallTracker.java  

        顺便提下在这个方法里还会有setAudioMode(),关于音频通道和模式的设置就在这里,这里面的故事也不少,不过要先放放了。下面的代码已经在Framework层了,不管之前的代码在哪里dial,最后都会来到RIL.java。        

[java] view
plain
copy

  1.  public void dial(String address, int clirMode, UUSInfo uusInfo, Message result) {  
  2.     RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result);  
  3.     rr.mp.writeString(address);  
  4.     rr.mp.writeInt(clirMode);  
  5.     rr.mp.writeInt(0); // UUS information is absent  
  6.   
  7.     if (uusInfo == null) {  
  8.         rr.mp.writeInt(0); // UUS information is absent  
  9.     } else {  
  10.         rr.mp.writeInt(1); // UUS information is present  
  11.         rr.mp.writeInt(uusInfo.getType());  
  12.         rr.mp.writeInt(uusInfo.getDcs());  
  13.         rr.mp.writeByteArray(uusInfo.getUserData());  
  14.     }   
  15.     send(rr);  
  16. }  

       跟着RIL_REQUEST_DIAL这个TAG标志向下走来到Reference-ril.c找到相应的case分支,

[java] view
plain
copy

  1. case RIL_REQUEST_DIAL:  
  2.          requestDial(data, datalen, t);  
  3.   
  4. static void requestDial(void *data, size_t datalen, RIL_Token t){  
  5.  p_dial = (RIL_Dial *)data;  
  6.   
  7.  switch (p_dial->clir) {  
  8.      case 1: clir = "I"break;  /*invocation*/  
  9.      case 2: clir = "i"break;  /*suppression*/  
  10.      default:  
  11.      case 0: clir = ""break;   /*subscription default*/  
  12.  }  
  13.  asprintf(&cmd, "ATD%s%s;", p_dial->address, clir);   
  14.  ret = at_send_command(cmd, NULL);  
  15.  free(cmd);    
  16.  RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);  
  17. }  

      到这里我们的电话基本上算是拨出去了,至于是否成功还要看返回结果,通常形式上的流程是这样的(下面只是很多可能中的一种,也是比较常见的一种),

[java] view
plain
copy

  1. ===>>[SendAT] ATD15812345678 //拨号,看到atd是不是会想到贝尔实验室和AT&T呢?  
  2. <<====[RecvAT] OK  
  3. <<====[RecvAT] +CLCC: 10200"15812345678"129//主动上报clcc  
  4. ===>>[SendAT] AT+CMUT=0, time=   //设置话简静音关  
  5. <<====[RecvAT] OK, time=  
  6.  ===>>[SendAT] AT+CLCC           //主动查询,  
  7. <<====[RecvAT] +CLCC: 10200"15812345678"129//以这一次的clcc为准  

        这段代码描述这样一个事实,不管模块报上来CLCC数据如何,我们都会重新查询后再上报给应用层,上层收到消息后更新界面和维护Connection里每一路电话的状态。到这一步,去电的流程也算是基本走完了,后面还有些界面状态刷新的代码省略了,对于AT命令那一部分,具体实现要看芯片厂商,表现上不完全一致。关于去电的流程就写这些,尽量写详细点,不过也有点长了,如仍有遗漏的地方,欢迎补充。

抱歉!评论已关闭.