Android 4.4 Kitkat Phone工作流程浅析(五)__MT(来电)流程分析

本文来自http://blog.csdn.net/yihongyuelan 转载请务必注明出处

本文代码以MTK平台Android 4.4为分析对象,与Google原生AOSP有些许差异,请读者知悉。

前置文章:

Android 4.4 Kitkat Phone工作流程浅析(一)__概要和学习计划

Android 4.4 Kitkat Phone工作流程浅析(二)__UI结构分析

Android 4.4 Kitkat Phone工作流程浅析(三)__MO(去电)流程分析

Android 4.4 Kitkat Phone工作流程浅析(四)__RILJ工作流程简析

概述

        上一篇文章简单分析了RILJ的工作流程,实际上MTK对于RILJ的改动还是较大的,添加了很多request和respone类型。对于MT(来电)来讲,首先还是会由Modem接收到信息,然后发给RILC,RILC再发送给RILJ,并在RILJ的RILReceiver中接收到并进行处理。MTK对于Modem侧的AT指令进行一些定制,这和AOSP原生使用CLCC方式后去来电信息不同,MTK这里使用了自己添加的AT指令ECPI来反馈通话状态改变的信息。后面会针对MTK的log进行一个简单的分析,根据log来分析 MO/MT 流程,其中会涉及AT log和main log。

        通过上一篇文章《Android 4.4 Kitkat Phone工作流程浅析(四)__RILJ工作流程简析》的分析,我们知道RILJ会从RILC收到Solicited和Unsolicited信息,并对之进行处理之后向上反馈。本次分析的MT(来电)流程,就属于Unsolicited信息,而信息从RILJ中的RILReceiver的run方法中开始。首先我们先看到RILJ与RILC的简单交互图,当RILJ收到RILC的Unsolicited信息时,RILReceiver开始处理。


        先来看看MT(来电)的整个流程图:


通过上图可以知道,整个MT(来电)过程分为三个部分:Telephony framework、TeleService、InCallUI。来电信息通过处理并逐层传递,最终显示到界面上。界面主要包括:CallCardFragment、CallButtonFragment、AnswerFragment内容的更新。

Telephony framework处理

通过RILReceiver接收到MT(来电)信息,RILC将相关来电信息通过socket发送给RILJ,RILReceiver接收到之后进行读取并打包进行上报。整个流程如下图:


整个过程看起来比较清晰,但实际跟踪起来还是比较麻烦的,这里运用到了观察者模式也就是RegistrantList和Registrant,使用handler来传递信息。

RegistrantList消息处理

这里实际上使用的是观察者模式,即:

RegistrantList:通知者

通过add()/addUnique()、remove()方法负责添加、删除通知者,使用notifyRegistrants()方法发出通知;

Registrant:观察者

通过internalNotifyRegistrant()响应通知者发出的通知;

RegistrantList的工作流程大致如下:

(1). RegistrantList通过registerForXXX方法注册观察者Registrant的方法;

在BaseCommands.java中可以看到registerForCallProgressInfo()方法,如下:

  1. protected RegistrantList mCallProgressInfoRegistrants = new RegistrantList();  
  2. //注册观察者  
  3. public void registerForCallProgressInfo(Handler h, int what, Object obj) {  
  4.     Registrant r = new Registrant (h, what, obj);  
  5.     mCallProgressInfoRegistrants.add(r);  
  6. }  
  7. //取消注册观察者  
  8. public void unregisterForCallProgressInfo(Handler h) {  
  9.     mCallProgressInfoRegistrants.remove(h);  
  10. }  
这是对Registrant对象进行了赋值:
  1. WeakReference   refH;  
  2. int             what;  
  3. Object          userObj;  
  4.   
  5. public Registrant(Handler h, int what, Object obj) {  
  6.     refH = new WeakReference(h);  
  7.     this.what = what;  
  8.     userObj = obj;  
  9. }  
那么这个registerForCallProgressInfo在哪里调用的呢?在GsmCallTracker的构造方法中可以看到:
  1. mCi.registerForCallProgressInfo(this, EVENT_CALL_PROGRESS_INFO, null);  

也就是说在GsmCallTracker初始化的时候完成了注册过程。

(2). 调用notifyRegistrants触发Handler的handleMessage回调;

注册完毕之后又是如何触发的呢?我们在RIL.java的ProcessUnsolicited方法中,来电时会上报以下RIL_UNSOL_CALL_PROGRESS_INFO信息,该信息由底层上报,并执行以下方法:
  1. mCallProgressInfoRegistrants.notifyRegistrants(new AsyncResult (null, ret, null));  
这里的notifyRegistrants实际为RegistrantList的方法:
  1. public void notifyRegistrants(AsyncResult ar){  
  2.     internalNotifyRegistrants(ar.result, ar.exception);  
  3. }  
  4. private synchronized void internalNotifyRegistrants (Object result, Throwable exception) {  
  5.     for (int i = 0, s = registrants.size(); i < s ; i++) {  
  6.         Registrant  r = (Registrant) registrants.get(i);  
  7.         r.internalNotifyRegistrant(result, exception);  
  8.     }  
  9. }  
通过registrants.get(i)方法获取其中的Registrant对象,并调用Registrant的internalNotifyRegistrant方法,其中registrant为ArrayList对象,通过其中的add方法添加:
  1. public synchronized void add(Registrant r) {  
  2.     removeCleared();  
  3.     registrants.add(r);  
  4. }  
在前面有提到registerForCallProgressInfo方法调用了add方法。继续查看r.internalNotifyRegistrant方法,在Registrant.java中:
  1. internalNotifyRegistrant (Object result, Throwable exception) {  
  2. Handler h = getHandler();  
  3.     if (h == null) {  
  4.         clear();  
  5.         Log.d("Registrant""internalNotifyRegistrant(): Warning! Handler is null, it could be already GCed. ( what=" + what + ", userObj=" + userObj + ", result=" + result + ", exception=" + exception + " )");  
  6.     } else {  
  7.         Message msg = Message.obtain();  
  8.         msg.what = what;  
  9.         msg.obj = new AsyncResult(userObj, result, exception);  
  10.         h.sendMessage(msg);  
  11.     }  
  12. }  
看到这里后,我们知道了最后消息的传递是使用handler的sendMessage()方法。经过了上面的种种之后,整个Registrant的工作流程如下:
        通过观察者模式,消息逐步传递到了GsmCallTracker中,在这里一方面继续将信息向上传递,另一方面这里调用了updatePhoneState()方法,该方法最后会使用broadcast的方式,发出TelephonyManager.ACTION_PHONE_STATE_CHANGE的广播,该广播用于通知Phone状态改变,API中提供监听PhoneState改变的广播就是这里负责通知的。

TeleService消息处理

        经过Telephony framework中的各种处理后,来电信息就传递到了TeleService中。在TeleService中会查询来电号码的信息,比如归属地,是否是已知联系人等,将这些查询到的消息保存到Connection对象中,再将其传递给InCallUI进行最后的显示。整个处理流程如下:

        这一块原来是放在Phone中进行处理的,在4.4中Phone一分为二,TeleService在后台负责相关数据的查询和获取,InCallUI负责取出TeleService中的数据并进行界面显示更新。这里的CallStateMonitor正如其名,用于注册监听Phone的状态改变并统一发起通知。CallNotifier和CallModeler均添加了CallStateMonitor的Listener,一旦Phone状态改变即可通过Handler的handleMessage回调到相关注册类中,并进行一些处理。

        因为我们这里仅分析MT(来电)流程,其中附带的响铃以及点亮屏幕等流程这里就不详述,在来电的时候并不是直说只有EVENT_NEW_RINGING_CONNECTION,还有EVENT_PRECISE_CALL_STATE_CHANGED和EVENT_INCOMING_RING等,每一个状态对应一种处理。这些状态在CallManager中处理之后便通过CallStateMonitor进行回调处理。

        startIncomingCallQuery()方法负责查询来电号码的相关信息,CallModeler将查询结果放入Connection对象中,并触发Listener的回调方法onIncoming()。CallModeler中包含了很多重要的方法,CallModeler继承自Handler,CallHandlerServiceProxy注册了其Listener,CallModeler处理完Connection之后会回调到CallHandlerServiceProxy中。

        CallModeler主要对Connection进行处理,并且用Connection来标识一个通话,在代码顶部的注释中可以发现:

  1. /** 
  2.  * Creates a Call model from Call state and data received from the telephony 
  3.  * layer. The telephony layer maintains 3 conceptual objects: Phone, Call, 
  4.  * Connection. 
  5.  * 
  6.  * Phone represents the radio and there is an implementation per technology 
  7.  * type such as GSMPhone, SipPhone, CDMAPhone, etc. Generally, we will only ever 
  8.  * deal with one instance of this object for the lifetime of this class. 
  9.  * 
  10.  * There are 3 Call instances that exist for the lifetime of this class which 
  11.  * are created by CallTracker. The three are RingingCall, ForegroundCall, and 
  12.  * BackgroundCall. 
  13.  * 
  14.  * A Connection most closely resembles what the layperson would consider a call. 
  15.  * A Connection is created when a user dials and it is "owned" by one of the 
  16.  * three Call instances.  Which of the three Calls owns the Connection changes 
  17.  * as the Connection goes between ACTIVE, HOLD, RINGING, and other states. 
  18.  * 
  19.  * This class models a new Call class from Connection objects received from 
  20.  * the telephony layer. We use Connection references as identifiers for a call; 
  21.  * new reference = new call. 
  22.  * 
  23.  * TODO: Create a new Call class to replace the simple call Id ints 
  24.  * being used currently. 
  25.  * 
  26.  * The new Call models are parcellable for transfer via the CallHandlerService 
  27.  * API. 
  28.  */  
  29. //大致信息如下:  
  30. //根据从Telephony framework层返回的Call状态和数据创建一个Call model。Telephony framework层包含3个概念性的对象:Phone、Call、Connection;  
  31.   
  32. //Phone包含的类型有:GSMPhone、SipPhone、CDMAPhone等,一般来讲,在一个生命周期里仅存在三种类型中的其中一种。  
  33.   
  34. //在一个生命周期中Call有三个引用:RingingCall、ForegroundCall、BackgroundCall。  
  35.   
  36. //一般来说Connection被用来表示通话。当用户拨号时便会创建Connection,其隶属于三种类型的Call。Connection的状态包含:ACTIVE、HOLD、RINGING等。  
  37.   
  38. //Modeler中的Connection对象<span style="font-family: Arial, Helvetica, sans-serif;">来自于</span><span style="font-family: Arial, Helvetica, sans-serif;">从telephony framework层,并且,使用Connection的引用来标识一个通话。</span>  
  39.   
  40. //Modeler将相关数据通过CallHandlerService传递给InCallUI。  
完成以上处理之后就进入了CallHandlerServiceProxy中,通过其名称我们大致可以推断是一个代理类。它主要完成信息的传输,代码如下:
  1. @Override  
  2. public void onIncoming(Call call) {  
  3.     // 清空重链接计数.  
  4.     resetConnectRetryCount();  
  5.     synchronized (mServiceAndQueueLock) {  
  6.         if (mCallHandlerServiceGuarded == null) {  
  7.             if (DBG) {  
  8.                 Log.d(TAG, "CallHandlerService not connected.  Enqueue incoming.");  
  9.             }  
  10.             //设置操作类型为QueueParams.METHOD_INCOMING  
  11.             enqueueIncoming(call);  
  12.             //与InCallUI中的CallHandlerService建立连接,使用bindService  
  13.             setupServiceConnection();  
  14.             return;  
  15.         }  
  16.     }  
  17.     //第二次再处理来电信息,第一次如果没有建立连接则会先执行上面的代码  
  18.     processIncoming(call);  
  19. }  
整个过程分解为以下三步:

(1). 设置界面处理类型;

因为这里是来电,因此类型为QueueParams.METHOD_INCOMING。

  1. enqueueIncoming(call);  
  2. private void enqueueIncoming(Call call) {  
  3.     getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call)));  
  4. }  
(2). 与CallHandlerService建立连接;

使用的是bindService来建立Service的连接。

  1. if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) {  
  2.     // This happens when the in-call package is in the middle of being installed  
  3.     Log.w(TAG, "Could not bind to default call handler service: " + serviceIntent.getComponent());  
  4.     failedConnection = true;  
  5. }  
(3). 处理来电操作processIncoming;

执行上面两步之后会return,但来电信息并不是只有一次上报,因此在建立CallHandlerService连接之后再执行processIncoming。

  1. private void processIncoming(Call call) {  
  2.     //... ...省略  
  3.         synchronized (mServiceAndQueueLock) {  
  4.             if (mCallHandlerServiceGuarded != null) {  
  5.                 mCallHandlerServiceGuarded.onIncoming(call,  
  6.                         RejectWithTextMessageManager.loadCannedResponses());  
  7.             }  
  8.     //... ....省略  
  9. }  
  10.   
  11. private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) {  
  12.     //... ...省略  
  13.     mCallHandlerServiceGuarded = callHandlerService;  
  14.     //... ...省略  
  15. }  
  16.   
  17. onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service));  
通过以上步骤之后所以的数据已经获取完毕,接下来需要更新界面UI了。

InCallUI界面更新

经过前面Telephoney framework和TeleService处理之后,已经获取到了UI界面所需要的数据,在InCallUI中要做的仅仅是将相关数据更新的界面上。整个流程如图:

在CallHandlerService中,首先会执行onIncoming()方法,并进而跳转到CallList中。

  1. mCallList.onIncoming(entry.getKey(), entry.getValue());  
这里的CallList包含了各种call的处理方法,并将处理结果通知感兴趣的类( 也就是注册了其Listener的类 ),主要是InCallPresenter。这里还会去更新call的id信息,这个id信息存储在HashMap中,比如当需要使用或者查找call可以根据id来查找,CallList主要是对Call进行列表化。

        在完成call列表化之后便会通知相应的presenter,这里是MT(来电)流程。所以主要涉及到两个presenter即AnswerPresenter和InCallPresenter。前者负责来电接听/拒接控件的更新显示,后者负责CallCard和CallButton以及VTCall ( MTK加入的VideoCall )的界面更新显示。

        通过CallCardPresenter中的onStateChange()方法,回调到CallCardFragment,并使用ui.setXXX()方法设置界面元素的内容,这里CallCard的更新可以查看《Android 4.4 Kitkat Phone工作流程浅析(二)__UI结构分析》,里面有涉及CallCard的更新流程。

小结

MT(来电)从Modem端发起,最终显示到界面上,整个过程的传递大致可以分为以下三个步骤:

1. Telephony framework处理;

主要对来电信息进行初步加工与分类。

2. TeleService处理;

完成对Telephony framework加工后的消息进行记录登记,并获取显示界面所需要的各种数据。

3. InCallUI界面显示更新

接收TeleService传递过来的更新请求,并将相关联系人数据更新到界面上。

        虽然简单的将MT(来电)流程分成了三个部分,但通过代码的查看可以知道,在此过程中有很多分支。比如:来电响铃,Notification通知等等,这些都属于MT(来电)流程的一部分。对于Android 4.4来讲,Phone模块一分为二即InCallUI和TeleService,google实际上将界面显示逻辑与后台处理逻辑进行了更好的分离,对比Android 4.2的Phone可以知道,在Android 4.4 的Phone中很多东西都比以前明确和清晰。特别是界面更新这一块,引入了Presenter和Fragment的方式,使得逻辑更加清楚。

        虽然简单的理清了MT(来电)的整个流程,但很多细节地方还值得推敲。MTK在原生Android 4.4 的基础上又添加和修改了许多东西,对于这一块MTK很大程度上是为了复用自己以前的代码,所以很多地方看起来杂乱无章,与原生Android 4.4 的代码相比显得格格不入。

        之前有做过Android 4.2的MT来电流程分析,就不单独发出来了,以附件的形式以供大家下载,点这里下载

        文中相关图片资源下载可点击这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值