(四)__RILJ工作流程简析
RIL概述
RIL 即 Ridio Interface Layer缩写,无线通信接口层,Android
中的实现分为RILJ和RILC两部分。RILJ属于Framework层中的Java部分( 后文使用RILJ指代RIL.java
),RILC属于HAL层中的C/C++部分(也就是rild)。RIL负责将AP层用户的通话控制信息传递给BP层Modem端,同时Modem端也会将相关处理结果返回给AP层。另外Modem状态有改变时也会主动上报给RIL层,再逐步向上传递并最终通过界面显示出来。本文仅简单介绍RILJ部分,详细分析请参看其他大神的分析。
RILJ与RILC以及Modem之间关系如图1:
图1
MO/MT在RILJ中执行流程:
1. MO在RILJ中流程:
当用户执行MO(去电)时,通过层层判断之后,系统会将相关信息传递到RILJ中,封装好后使用AT指令的方式发送到Modem端,最终由Modem端发起通话请求。在通话接通后Modem端会返回相关AT指令信息给RIL层,RIL层再向上反馈。
整个MO(去电)流程可以简单的归结为两个步骤:
(1). ( Request , Response) ,即向RILC发起Request,再由RILC向Modem发送相关AT指令,等待Modem处理并反馈结果,如DIAL操作;
(2). (Response),即在对方接通之后,Modem会将状态信息反馈到RILJ中;
2. MT在RILJ中流程:
当Modem端收到来电信息时,会将相关来电信息通过AT指令发送给RILC,再通过RILC使用socket发送给RILJ,逐层向上传递,最终显示来电响铃界面。
MT(来电)与MO(去电)的第二步相同,通过( Response ),即Modem端主动上报状态改变信息;
我们先来看一下RILJ 的基本组成以及主要功能,图2为RILJ核心类图:
图2
通过上图我们可以看到,RILJ继承自BaseCommands实现了CommandsInterface接口,RILJ包含了三个内部类:RILRequest、RILReceiver、RILSender,分别对应mRequestsList、mSender、mReceiver三个对象。
RILJ中涉及通话控制和消息处理的关键方法:
通话控制类:
dial(),负责创建拨号RILRequest对象;
acceptCall(),负责创建接听RILRequest对象;
rejectCall(),负责创建拒接RILRequest对象;
以上方法最终会通过RILSender并使用socket方式向RILC发起相关请求。
消息处理类:
processUnsolicited(),负责处理UnSolicited反馈信息;
processSolicited(),负责处理Solicited反馈信息;
这些消息来自RILC的socket反馈,在上一篇MO(去电)流程分析中,dial最终会调用到RIL.java中的RILSender对象的相关方法,向RILC继续传递拨号信息,而MT(来电)时Modem会将相关信息发送到RILC,RILC再将信息通过socket反馈给RILJ,在RILJ中通过RILReceiver接收并开始处理。RILSender负责通过socket向RILC发送RILRequest信息,RILReceiver负责以socket方式从RILC接收Response信息。当RILJ从RILC接收到相关信息时,会逐步向上反馈,这些信息分为两类:
1. Solicited Response
请求返回消息,这类消息是上层主动请求Modem再由其返回的消息,这些消息由RILC上报给RILJ。比如当我们执行MO(去电)时,会通过RILSender发送dial类型的Request给RILC,RILC再传递给Modem端,再由Modem端发起通话请求。待对方接通之后Modem将相关信息发送到RILC,RILC将结果消息反馈给RILJ,此时由RILReceiver来接收并处理消息,这种消息就是Solicited消息。
可以简单的理解为( Request , Response )这样成对出现,此时RILC向RILJ返回的Respone就是Solicited消息。
2. UnSolicited Response
主动返回消息,这类消息由Modem端主动上报。比如当有MT(来电)时,Modem会接收到信息并向RILC发送来电消息,RILC将来电信息再发送给RILJ,这类没有request但由Modem主动上报信息就是UnSolicited信息。
可以简单的理解为Modem端主动给RILC,再由RIC上报给RILJ的(
Response )消息,这类消息就是UnSolicited消息。
无论是 Solicited 还是UnSolicited Respone
消息,在RILJ中都是有RILReceiver来负责接收并处理,整个RILJ的工作可以简单的用图3来描述:
图3
RILJ包含了RILSender和RILReceiver,其中RILSender负责发送Request给RILC,RILReceiver负责接收Solicited和UnSolicited的Response信息。
RILJ工作流程
RILJ的工作内容主要是负责向RILC发起Request,以及从RILC接收Response。主要涉及三个内部类:RILRequest、RILSender、RILReceiver,主要工作包含以下三大内容:
1. RILRequest的构造;
RILRequest是RILJ的内部类,我们可以把RILRequest看成一个打包类,将相关信息进行加工处理后打包成一个规范的RILRequest并使用RILSender发送给RILC。在RILRequest类中最重要的方法莫过于obtain了,该方法完成了打包工作。我们在MO(去电)流程分析中有看到调用RILJ中的dial方法,并在dial方法中看到了RILRequest的打包代码,如下:
RILRequest rr = RILRequest.obtain(RIL_REQUEST_DIAL, result, mySimId); |
这里再去看一下obtain方法的实现:
static RILRequest obtain(int request, Message result, int simId) { RILRequest rr = null; synchronized(sPoolSync) { if (sPool != null) { rr = sPool; sPool = rr.mNext; rr.mNext = null; sPoolSize--; } } if (rr == null) { rr = new RILRequest(); } //这里跟Android原生不同,这里多了一个参数simId用于MTK双卡手机标识哪一张SIM卡 //static int[] sNextSerial = {0, 0, 0, 0};这里后面会从log中看到如"[233]"的编号 //serial是Request的编号并且是唯一的 synchronized(sSerialMonitor) { rr.mSerial = sNextSerial[simId]++; } //请求类型 rr.mRequest = request; rr.mResult = result;//请求结果 Message对象 rr.mParcel = Parcel.obtain(); if (result != null && result.getTarget() == null) { throw new NullPointerException("Message target must not be null"); } // first elements in any RIL Parcel rr.mParcel.writeInt(request); rr.mParcel.writeInt(rr.mSerial); return rr; } |
2. RILSender发送Request;
在Request构造完成之后我们在dial方法中看到最终调用了send(rr)方法,这里就用到了RILSender,RILSender也是RILJ中的内部类,通过查看send方法可以知道:
private void send(RILRequest rr) { Message msg; if (mSocket == null) { rr.onError(RADIO_NOT_AVAILABLE, null); rr.release(); return; } //RILSender集成Handler因此会跳转到相应的handleMessage方法中 msg = mSender.obtainMessage(EVENT_SEND, rr); acquireWakeLock(); msg.sendToTarget(); } |
通过前面的核心类图我们可以知道,RILSender集成自Handler,因此这里会跳转到RILSender的handleMessage方法中,并执行case
EVENT_SEND,代码如下:
case EVENT_SEND: boolean alreadySubtracted = false; try { LocalSocket s; //获取LocalSocket,并对socket进行判断是否正常 s = mSocket; //... ...省略部分代码 //将Request添加到链表中 synchronized (mRequestList) { mRequestList.add(rr); } //... ...省略部分代码 //定义socket发送需要的byte数组 byte[] data; //获取数据 data = rr.mParcel.marshall(); //... ...省略部分代码 //向socket写入数据 s.getOutputStream().write(dataLength); s.getOutputStream().write(data); } //... ...省略部分代码 break; |
这里使用了socket的方式将拨号请求发送给了RILC,最后又RILC向Modem端传递。在完成以上步骤之后dial流程就走到了RILC中,再由RILC向Modem端发出dial的AT指令。
3. RILReceiver接收Response;
当MO(去电)接听以及MT(来电)时,Modem端会将相关信息通过RILC发送给RILJ,负责接收的正式RILReceiver,RILReceiver也是RILJ的内部类,该类实现了Runnable接口,因此实际启动是以Thread的方式启动的。RILReceiver在PhoneProxy的构造方法中通过调用startRilReceiver启动,这里是MTK改动过,原生是直接在RIL.java的构造方法中启动线程。
RILReceiver主要完成两项工作:
(1). 维护local socket的连接;
(2). 阻塞local socket并处理RILC的Response
( Solicited 和 UnSolicited );
RILReceiver启动之后会建立local socket并阻塞,代码如下:
@Override public void run() { //... ...省略部分代码 try { for (;;) { //... ...省略部分代码 //建立并维护 Local Socket try { s = new LocalSocket(); l = new LocalSocketAddress(socketRil, LocalSocketAddress.Namespace.RESERVED); s.connect(l); } catch (IOException ex){ //... ...省略部分代码 //当连接失败后休眠4s并继续尝试连接 } //... ...省略部分代码 try { InputStream is = mSocket.getInputStream(); for (;;) {//阻塞 Parcel p; length = readRilMessage(is, buffer); if (length < 0) { // End-of-stream reached break; } p = Parcel.obtain(); p.unmarshall(buffer, 0, length); p.setDataPosition(0); //处理Respone消息(Solicited和UnSolicited) processResponse(p); p.recycle(); } } //... ...省略部分代码 } |
那么通过以上分析我们可以简单的将RILJ的工作流程总结为:打包,发送,接收三个步骤。在RILReceiver中接收到RILC的socket信息之后,使用processResponse进行处理,进而分为processUnsolicited()和processSolicited()方法。
processSolicited()方法主要完成以下三件事:
(1). 根据Response中的request serial编号找到RequestList中的对象并移除;
因为Solicited的Request是一一对应的,当处理完本次Response之后不再需要改Request对象,因此这里会移除,使用方法findAndRemoveRequestFromList()。
(2). 根据request的类型使用responseXXX方法处获取从RILC中返回的信息;
这里会有很多request类型,对应的responseXXX方法也有很多,比如responseInts()、responseVoid()、responseString()、responseOperator()等等,通过这些方法取出Parcel对象中的内容,该对象中包含了Request需要查询的信息。
(3). 将第2步获取到的信息存储到Message中并触发回调;
执行代码如下:
if (rr.mResult != null) { AsyncResult.forMessage(rr.mResult, ret, null); rr.mResult.sendToTarget(); } |
我们知道RILRequest类中有定义mResult为Message类型,在obtain方法执行的时候已经对mResult进行了赋值,这里的Message就是从GSMCallTracker中传递过来的,有兴趣的童鞋自己追踪一下吧,注意看清楚dial中的参与即可。也就是说通过分析我们知道processSolicited()处理完成会携带相关信息跳转到GSMCallTracker中继续执行。
processUnsolicited()方法主要以下两件事:
(1). 根据response消息的类型使用responseXXX方法处获取从RILC中返回的信息;
这一步和processSolicited()方法的第2步类似,并将从Parcel对象中获取到的数据保存到局部变量Object类型的对象ret中。
(2). 使用notifyRegistrants()方法将相关信息通知到订阅者(
Subscribe );
因为这里使用的是观察者模式( Publisher-Subscribe ),相关订阅者会根据获取到不同的信息作出与之对应的反应。
对于MO(去电)而言,整个过程既包含了processSolicited()也包含了processUnsolicited()。为什么这么说呢?当我们执行dial操作后,会将相关信息发送到Modem端,此时执行的步骤是DIAL,Modem端发起DIAL成功后返回给RILJ,这个步骤就是(
Request , Respone )。之后如果对方接通了那么Modem会将相关信息反馈给RILJ,这个步骤即(
Respone ),通过Radio Log可以看到:
(a). 拨号Request和Respone
D/RILJ ( 966): RIL(1) :[0253]> DIAL
D/RILJ ( 966): RIL(1) :[0253]< DIAL
(b). 返回MO接通的Respone
V/RILJ ( 966): RIL(1) :[UNSL RIL]<
UNSOL_CALL_PROGRESS_INFO {1, 6, 0, 1, 0, 0, 13800138000,
145, }
至于MO接通后返回信息里面的参数这里不详细解释,主要是AT指令中携带的信息,不同硬件平台会有一些自己的定制。对于Request和Respone并不一定是连续出现在Log中的,我们只需要查看其唯一的serial编号即可找到Request对应的Respone。另外,在RILJ的Log中我们可以根据大于(>)符号和小于(<)符号来判断是Request还是Response,即:
(I). 大于符号(>)对应Request,表示RILJ向RILC发送请求;
(II). 小于符号(<)对应Response,表示RILC向RILJ反馈信息;
前面我们说过Solicited信息是成对出现的,通过log也能证明这一点,平时查看Log可以根据request的serial编号来找到对应的response。在完成以上拨号操作之后Solicited信息就执行完毕了,接下来是等待对方接通,若对方接通则Modem会收到相应的指令并向RILC传递接通信息。该信息由Modem主动上报,因此属于UnSolicited信息,由processUnsolicited()方法来处理。
总结
RILJ的主要作用是将通话控制信息使用socket传递给RILC,RILC再使用AT指令传递给Modem端;RILC通过socket返回的Modem处理结果给RILJ并通知上层应用;可以说RILJ在Android
Telephony结构中有着承上启下的作用。
RILJ的主要工作内容可以概括为以下三点:
1. 构造打包RILRequest;
2. RILSender通过socket向RILC发起request;
3. RILReceiver通过socket结构RILC反馈response;
RILReceiver接收到的消息分为两类:
1. Solicited Response,与之对应的处理方法是processSolicited();
2. UnSolicited Response,与之对应的处理方法是processUnsolicited();
(五)__MT(来电)流程分析
概述
上一篇文章简单分析了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()方法,如下:
protected RegistrantList mCallProgressInfoRegistrants = new RegistrantList(); //注册观察者 public void registerForCallProgressInfo(Handler h, int what, Object obj) { Registrant r = new Registrant (h, what, obj); mCallProgressInfoRegistrants.add(r); } //取消注册观察者 public void unregisterForCallProgressInfo(Handler h) { mCallProgressInfoRegistrants.remove(h); } |
这是对Registrant对象进行了赋值:
WeakReference refH; int what; Object userObj; public Registrant(Handler h, int what, Object obj) { refH = new WeakReference(h); this.what = what; userObj = obj; } |
那么这个registerForCallProgressInfo在哪里调用的呢?在GsmCallTracker的构造方法中可以看到:
mCi.registerForCallProgressInfo(this, EVENT_CALL_PROGRESS_INFO, null); |
也就是说在GsmCallTracker初始化的时候完成了注册过程。
(2). 调用notifyRegistrants触发Handler的handleMessage回调;
注册完毕之后又是如何触发的呢?我们在RIL.java的ProcessUnsolicited方法中,来电时会上报以下RIL_UNSOL_CALL_PROGRESS_INFO信息,该信息由底层上报,并执行以下方法:
mCallProgressInfoRegistrants.notifyRegistrants(new AsyncResult (null, ret, null)); |
这里的notifyRegistrants实际为RegistrantList的方法:
public void notifyRegistrants(AsyncResult ar){ internalNotifyRegistrants(ar.result, ar.exception); } private synchronized void internalNotifyRegistrants (Object result, Throwable exception) { for (int i = 0, s = registrants.size(); i < s ; i++) { Registrant r = (Registrant) registrants.get(i); r.internalNotifyRegistrant(result, exception); } } |
通过registrants.get(i)方法获取其中的Registrant对象,并调用Registrant的internalNotifyRegistrant方法,其中registrant为ArrayList对象,通过其中的add方法添加:
public synchronized void add(Registrant r) { removeCleared(); registrants.add(r); }
|
在前面有提到registerForCallProgressInfo方法调用了add方法。继续查看r.internalNotifyRegistrant方法,在Registrant.java中:
internalNotifyRegistrant (Object result, Throwable exception) { Handler h = getHandler(); if (h == null) { clear(); Log.d("Registrant", "internalNotifyRegistrant(): Warning! Handler is null, it could be already GCed.
( what=" + what + ", userObj=" + userObj + ", result=" + result + ", exception=" + exception + " )"); } else { Message msg = Message.obtain(); msg.what = what; msg.obj = new AsyncResult(userObj, result, exception); h.sendMessage(msg); } } |
看到这里后,我们知道了最后消息的传递是使用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来标识一个通话,在代码顶部的注释中可以发现:
/** * Creates a Call model from Call state and data received from the telephony * layer. The telephony layer maintains 3 conceptual objects: Phone, Call, * Connection. * * Phone represents the radio and there is an implementation per technology * type such as GSMPhone, SipPhone, CDMAPhone, etc. Generally, we will only ever * deal with one instance of this object for the lifetime of this class. * * There are 3 Call instances that exist for the lifetime of this class which * are created by CallTracker. The three are RingingCall, ForegroundCall, and * BackgroundCall. * * A Connection most closely resembles what the layperson would consider a call. * A Connection is created when a user dials and it is "owned" by one of the * three Call instances. Which of the three Calls owns the Connection changes * as the Connection goes between ACTIVE, HOLD, RINGING, and other states. * * This class models a new Call class from Connection objects received from * the telephony layer. We use Connection references as identifiers for a call; * new reference = new call. * * TODO: Create a new Call class to replace the simple call Id ints * being used currently. * * The new Call models are parcellable for transfer via the CallHandlerService * API. */ //大致信息如下: //根据从Telephony framework层返回的Call状态和数据创建一个Call model。
Telephony framework层包含3个概念性的对象:Phone、Call、Connection; //Phone包含的类型有:GSMPhone、SipPhone、CDMAPhone等,
一般来讲,在一个生命周期里仅存在三种类型中的其中一种。 //在一个生命周期中Call有三个引用:RingingCall、ForegroundCall、BackgroundCall。 //一般来说Connection被用来表示通话。当用户拨号时便会创建Connection,
其隶属于三种类型的Call。Connection的状态包含:ACTIVE、HOLD、RINGING等。 //Modeler中的Connection对象<span style="font-family: Arial, Helvetica, sans-serif;">
来自于</span><span style="font-family: Arial, Helvetica, sans-serif;">从telephony framework层,
并且,使用Connection的引用来标识一个通话。</span> //Modeler将相关数据通过CallHandlerService传递给InCallUI。 |
完成以上处理之后就进入了CallHandlerServiceProxy中,通过其名称我们大致可以推断是一个代理类。它主要完成信息的传输,代码如下:
@Override public void onIncoming(Call call) { // 清空重链接计数. resetConnectRetryCount(); synchronized (mServiceAndQueueLock) { if (mCallHandlerServiceGuarded == null) { if (DBG) { Log.d(TAG, "CallHandlerService not connected. Enqueue incoming."); } //设置操作类型为QueueParams.METHOD_INCOMING enqueueIncoming(call); //与InCallUI中的CallHandlerService建立连接,使用bindService setupServiceConnection(); return; } } //第二次再处理来电信息,第一次如果没有建立连接则会先执行上面的代码 processIncoming(call); } |
整个过程分解为以下三步:
(1). 设置界面处理类型;
因为这里是来电,因此类型为QueueParams.METHOD_INCOMING。
enqueueIncoming(call); private void enqueueIncoming(Call call) { getQueue().add(new QueueParams(QueueParams.METHOD_INCOMING, new Call(call))); } |
(2). 与CallHandlerService建立连接;
使用的是bindService来建立Service的连接。
if (!mContext.bindService(serviceIntent, mConnection, Context.BIND_AUTO_CREATE)) { // This happens when the in-call package is in the middle of being installed Log.w(TAG, "Could not bind to default call handler service: " + serviceIntent.getComponent()); failedConnection = true; } |
(3). 处理来电操作processIncoming;
执行上面两步之后会return,但来电信息并不是只有一次上报,因此在建立CallHandlerService连接之后再执行processIncoming。
private void processIncoming(Call call) { //... ...省略 synchronized (mServiceAndQueueLock) { if (mCallHandlerServiceGuarded != null) { mCallHandlerServiceGuarded.onIncoming(call, RejectWithTextMessageManager.loadCannedResponses()); } //... ....省略 } private void onCallHandlerServiceConnected(ICallHandlerService callHandlerService) { //... ...省略 mCallHandlerServiceGuarded = callHandlerService; //... ...省略 } onCallHandlerServiceConnected(ICallHandlerService.Stub.asInterface(service)); |
通过以上步骤之后所以的数据已经获取完毕,接下来需要更新界面UI了。
InCallUI界面更新
经过前面Telephoney framework和TeleService处理之后,已经获取到了UI界面所需要的数据,在InCallUI中要做的仅仅是将相关数据更新的界面上。整个流程如图:
在CallHandlerService中,首先会执行onIncoming()方法,并进而跳转到CallList中。
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来电流程分析,就不单独发出来了,以附件的形式以供大家下载,点这里下载。
文中相关图片资源下载可点击这里。 |