Android电话部分综述
主要部分:
呼叫
短信
数据连接
SIM卡
电话本
电话部分分为以下几层:
Modem驱动
RIL(radiointerfacelayer)
电话服务框架
应用层
Modem通信模块
Chip-on-board
在Modem硬件上一般使用两个渠道:
一个是用于AT命令:一般使用UART或USB方式,AT命令由Hayes公司发明
以AT开头,用于完成调制解调器之间的交互
另一个用于数据传输:通过usb方式传输数据
如果基带与应用处理器集成一般通过共享内存方式传输
本地的RIL代码
本地代码的路径:\hardware\ril
部分文件;
include:RIL头文件
Libril:RIL库,最终生成libril.so是一个辅助库
Rild:守护进程安装在system/bin目录下
Rdference-ril:参考库,生成动态库libreference-ril.so
Include目录中ril.h头文件是RIL框架结构和接口
Rild守护进程
Rild是一个可执行程序:获取参数------->r打开功能库------->建立事件循环----------->执行RIL-Init------>register
在ril.c中
staticstructRIL_Envs_rilEnv={
RIL_onRequestComplete,//RIL请求完成
RIL_onUnsolicitedResponse,//主动上报的响应
RIL_requestTimedCallback//用户定义响应的回调函数
};
主函数部分代码如下:
intmain(intargc,char**argv)
{
...
//获取参数并解析
dlHandle=dlopen(rilLibPath,RTLD_NOW);
//启动线程进入事件循环
RIL_startEventLoop();
rilInit=(constRIL_RadioFunctions*(*)(conststructRIL_Env*,int,char**))dlsym(dlHandle,"RIL_Init");
//处理参数
funcs=rilInit(&s_rilEnv,argc,rilArgv);
RIL_register(funcs);
...
}
通过select多路复用机制,读取来自上层的Socket接口的具体操作命令
在init.rc中启动这个守护进程,如果使用-l则可以指定所使用的功能库
Libril库
Libril.so主要提供了用于注册的RIL_register(),
几个回调函数
RIL_onRequestComplete(RIL_Tokent,RIL_Errnoe,void*response,size_tresponselen){}
voidRIL_onUnsolicitedResponse(intunsolResponse,void*data,size_tdatalen){}
staticUserCallbackInfo*internalRequestTimedCallback(RIL_TimedCallbackcallback,void*param,
conststructtimeval*relativeTime){}
字符串转换函数
extern"C"constchar*requestToString(intrequest);
extern"C"constchar*failCauseToString(RIL_Errno);
extern"C"constchar*callStateToString(RIL_CallState);
extern"C"constchar*radioStateToString(RIL_RadioState);
RIL的实现库ReferenceRIL
libreference-ril.so功能库的一个参考实现,完成从具体电话服务命令到实际的AT命令之间的转换
Request请求流程
首先由java层通过socket将命令发送到RIL层的RILD守护进程,负责监听的ril_event_loop消息循环中
的RILDSocket有了请求链接信号,会建立起一个record_system,打通与上层的数据通道开始接收请求数据
数据通道的回调函数processCommandsCallback()会保证收到一个完整的Request后(Request包的完整性由record_stream的机制保证),将其送达processCommandBuffer()函数,
staticint
processCommandBuffer(void*buffer,size_tbuflen){
Parcelp;
status_tstatus;
int32_trequest;
int32_ttoken;
RequestInfo*pRI;
intret;
p.setData((uint8_t*)buffer,buflen);
//statuscheckedatend
status=p.readInt32(&request);
status=p.readInt32(&token);
if(status!=NO_ERROR){
LOGE("invalidrequestblock");
return0;
}
if(request<1||request>=(int32_t)NUM_ELEMS(s_commands)){
LOGE("unsupportedrequestcode%dtoken%d",request,token);
//FIXMEthisshouldperhapsreturnaresponse
return0;
}
pRI=(RequestInfo*)calloc(1,sizeof(RequestInfo));
pRI->token=token;
pRI->pCI=&(s_commands[request]);
//互斥量上锁
ret=pthread_mutex_lock(&s_pendingRequestsMutex);
assert(ret==0);
pRI->p_next=s_pendingRequests;
s_pendingRequests=pRI;
//互斥量解锁
ret=pthread_mutex_unlock(&s_pendingRequestsMutex);
assert(ret==0);
/*sLastDispatchedToken=token;*/
//调用分派函数
pRI->pCI->dispatchFunction(p,pRI);
return0;
}
processCommandBuffer()函数正式进入命令的解析流程它从socket中序列化的数据流里还原信息,将其组织到RequestInfo中,每个命令将以下形式存在
typedefstructRequestInfo{
int32_ttoken;//thisisnotRIL_Token
CommandInfo*pCI;
structRequestInfo*p_next;
charcancelled;
charlocal;//responsestolocalcommandsdonotgobacktocommandprocess
}RequestInfo;
在还原出来的信息中,最重要的是request号它在RIL层和上层之间,在ril_commands.h中定义
/**Index==requestNumber*/
staticCommandInfos_commands[]={
#include"ril_commands.h"
};
RIL层中采用表驱动方式分派请求,分派的基础是ruquest号,头文件按以下方式被包含
commandInfo结枸表示命令信息,关联了request号和实际的请求函数,及响应函数之间的关系
typedefstruct{
intrequestNumber;
void(*dispatchFunction)(Parcel&p,structRequestInfo*pRI);
int(*responseFunction)(Parcel&p,void*response,size_tresponselen);
}CommandInfo;
接下来的分发流程,
由s_callbacks.onReauest()完成这一操作,s-callbacks是获取libreference-ril的RILRadioFunctions的结构指针,请求在这里转入底层libreference-ril处理,handler是referece-ril.c的Request
例如:RIL——REQUEST——DIAL请求流程如下
onRequest()---->requestDial()-这里将命令和参数转换成对应的AT命令----------->at_send_connand------->at_command_full()------>at_send_command_full_nolock()---->writeline()
Response流程
RIL的移植工作
当宏RIL——SHLIB被定义时,将使用库的行式,
没有被定义时,将使用守护进程的方式
移植需要考虑的问题:
RIL设备所使用的不同端口
在RIL_RadioFunctions的onRequest函数中需要处理的不同命令(差异部分在数据连接和呼叫状态查询等方面)
电话部分Java框加及应用
代码路径为:\frameworks\base\telephony\java
Java层与RIL本地代码的接口,是一个名为rild的socket接口
RIL.java中的几个类
RILRequest代表一个电话服务命令请求
RILSender负责命令的发送
RILReceiver处理信命令响应和主动上报信息的响应
RILConstants.java定义了电话报务的具体命令
classRILRequest{
staticRILRequestobtain(intrequest,Messageresult){}
voidrelease(){}
StaticvoidresetSerial(){}
StringserialString(){}
voidonError(interror,Objectret){}
}
RILRIL.RIL_SenderRIL.RILReceiver几个类的关系
publicfinalclassRILextendsBaseCommandsimplementsCommandsInterface{}
Connamds为命令响应或主动上报提供回调函数的注册机制
commandInterface提供具体的电话服务接口
在RIL。Java中一个命令的发送标准流程是:
RILRequest.obtain--->复制参数----->通过send()函数发送EVENT_SEND----->RILSender线程中处理EVENT_SEND------>将命令写到outstream(socket)
Socket的取得,是在RILReceiver中建立
staticfinalStringSOCKET_NAME_RIL="rild";
s=newLocalSocket();
l=newLocalSocketAddress(SOCKET_NAME_RIL,
LocalSocketAddress.Namespace.RESERVED);
s.connect(l);
响应和主动上报消息的流程:
RILReceiver线程监视mSocketinput------>readRilMessage(读取完整响应)---------->processResponse---------->分别处理RESPONSE_UNSOLICITED与RESPONSE_SOLICITED(前者为主动上报,后者为命令响应)
注:send和processResponse是异步的,电话服务的发送和响应是异步的
RIL直接同RIL本地代码打交道,对上层应用来说他并不是直接的接口,GSMPhone.java他继承了PhoneBase,而phoneBase实现了phone接口,GSMPhone对RIL进行了一层封装,通过phone接口来实现功能
在Phone应用程式序中:
通过PhoneFactory来获取GSMPhone
流程是:
PhoneFactory.makeDefaultPhones------>PhoenFactory.useNewRIL------>PhoneFactory.registerPhone
然后Phone就可以获取GSMPhone的实例(Phone接口)
拔打电话和获取网络状态,是由telephonyManager来完成
TelephoneManager通过两个IBander接口ITelephony和ITelephonyRegistry来完成
ITelephony是电话服务的用户主动进行RIL访问的路径(如拔打电话),它的服务实现类不在框架代码中,而在phone应用中phoneInterfaceManager.java
ITelephonyRegistry提供一个通知机制,将底层状态或变更通知给电话服务如网络状态/信号强弱
底层通知的来源是GSMPhone通过PhoneNotifier的实现者DefaultPhoneNotifier.java
telephonyRegistry通过两种方式通知用户
一种是Broadcast
另一种是在TelephoneRegistry.java中注册的IPhoneStateListener接口实现回调机制
interfaceITelephonyRegistry{
voidlisten(Stringpkg,IPhoneStateListenercallback,intevents,booleannotifyNow);
}
传递的是IPhoneStateListenercallback参数
呼叫
ITelephony接口实现在Phone应用中的phone服务,通过TelephonyManager提供访问接口
服务内部通过PhoneFactory获取GSMPhone来访问URI提供如拔号/接通/挂断
在呼叫部分中,从GSMPhone到RIL的路径中用到的几个数据结构如下:
GSMCall基本的呼叫控制结构,每个接通的GSMCall都拥有一个GSMConnection结构
GSMConnection该结构用于存放呼叫时长等信息
CallTracker是呼叫模块的核心它提供与呼叫相关的接口
GSMPhone拥有CallTracker的实例,通过调用CommandsInterface实现
维护当前的GSMCall,实现追踪的方法为:pollCallsWhenSafe
getCurrentCalls取得当前活动的呼叫列表
短信部分
SMSManager实现短信的发送与sim卡短信相关的操作通过ISms接口实现对应操作
ISms的服务端实现是:simsmsInterfaceManager
SMSDispatacher是短信的核心部分,负责发送接收短信,同样也集成在GSMPhone中
发送方面
提供了text和pdu两种方式.SmsTracker跟踪短信的发送过程
发送结果根据PendingIntent回传
接收方面
SMSDispatcher启动时会通过CommandInterface注册新短信和返回报告的回调接口
mCm.setOnNewSMS(this,EVENT_NEW_SMS,null);
mCm.setOnSmsStatus(this,EVENT_NEW_SMS_STATUS_REPORT,null);
mCm.setOnIccSmsFull(this,EVENT_ICC_FULL,null);
mCm.registerForOn(this,EVENT_RADIO_ON,null);
有新消息上来时,相应的消息会被发送到SMSDispatcher的消息处理函数
dispatchMessage会读取SMSHeader分析是否需要拼接,完在后由dispatchPdus通过
Intentintent=newIntent(Intents.SMS_REJECTED_ACTION);
intent.putExtra("result",result);
mWakeLock.acquire(WAKE_LOCK_TIMEOUT);
mContext.sendBroadcast(intent,"android.permission.RECEIVE_SMS");
广播出去
数据联接
Android数据联接是通过ppp方式实现的
数据联接分两个步骤:
首先是通过AT命令激活PDP联接
pppd通过数据端口完成拔号联接
GSMPhone拥有其实例,基入口点是:
DataConnectionTrackr.trySetupData------>setupData------->pdpConnection.connect------>commandsInterface.setupDefaultPDP
数据连接部分的结构
注:pppd是一个单独的进程,需要在init.rc里注册此服务