求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
(三)Android 4.4 Kitkat Phone工作流程浅析
 
作者 yihongyuelan的博客,火龙果软件    发布于 2014-05-04
 

(四)__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来电流程分析,就不单独发出来了,以附件的形式以供大家下载,点这里下载。

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

 
相关文章

手机软件测试用例设计实践
手机客户端UI测试分析
iPhone消息推送机制实现与探讨
Android手机开发(一)
 
相关文档

Android_UI官方设计教程
手机开发平台介绍
android拍照及上传功能
Android讲义智能手机开发
相关课程

Android高级移动应用程序
Android系统开发
Android应用开发
手机软件测试
 
分享到
 
 


android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...