Android Ril源代码分析
Android RIL深入解析

Android RIL深入解析
高蕾
【期刊名称】《通讯世界》
【年(卷),期】2017(000)015
【摘要】手机软件开发技术历经Symbian、WinCE、BREW、MTK、IOS、Android等平台,发展和进步可谓日新月异,Android经受住了市场考验,成为主流手机操作系统平台.市场对Android手机的需求量非常大,由于Android平台代码的开源,各大手机厂商纷纷加入了深度定制Android系统平台的队伍,通话是手机的必备功能,Android RIL作为通话功能的核心模块,其涉及的内容和范围顺理成章的成为定制过程中的重点和难点,本文将从RIL构架结构,RILJ和RILC三个方面对Android RIL进行深入解析.
【总页数】2页(P289-290)
【作者】高蕾
【作者单位】南京六九零二科技有限公司,210009
【正文语种】中文
【中图分类】TP316
【相关文献】
1.Android中SharedPreferences知识的深入研究 [J], 许姗姗
2.将Android平台的RIL层移植到基于LINUX的通用平台的研究与实现 [J], 赵国强;彭大芹
3.Android RIL兼容问题研究 [J], 杨倩;杨明赵
4.《深入理解Android》背后的故事 [J], 邓凡平
5.谷歌究竟做了什么?AndroidL中ART模式深入解读 [J], 周清
因版权原因,仅展示原文概要,查看原文内容请购买。
RIL层代码分析

ril/rild下的文件rild.c->mian()为函数入口int main(int argc, char **argv){//....//OpenLib:#endifswitchUser();//打开dlopen()加载vendor RIL 获取由RIL_register(funcs);注册进来的参数,并解析dlHandle = dlopen(rilLibPath, RTLD_NOW);if (dlHandle == NULL) {fprintf(stderr, "dlopen failed: %s\n", dlerror());exit(-1);}//1:消息队列的入口1.到select阻塞//每当看到打印信息,不按顺序打下来说明阻塞RIL_startEventLoop();//通过dlsym函数得到rilInit函数指针的引用rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");if (rilInit == NULL) {fprintf(stderr, "RIL_Init not defined or exported in %s\n", rilLibPath);exit(-1);}if (hasLibArgs) {rilArgv = argv + i - 1;argc = argc -i + 1;} else {static char * newArgv[MAX_LIB_ARGS];static char args[PROPERTY_VALUE_MAX];rilArgv = newArgv;property_get(LIB_ARGS_PROPERTY, args, "");argc = make_argv(args, rilArgv);}// Make sure there's a reasonable argv[0]rilArgv[0] = argv[0];//2:利用得到的rilInit函数指针,调用真正的RIL_Initfuncs = rilInit(&s_rilEnv, argc, rilArgv);RIL_register(funcs);done:while(1) {// sleep(UINT32_MAX) seems to return immediately on bionicsleep(0x00ffffff);}-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------RIL_startEventLoop()函数分析—ril/libril->ril.cpp-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------RIL_startEventLoop(void) {int ret;pthread_attr_t attr;LOGD("-2--ril.cpp--RIL_startEventLoop---");/* spin up eventLoop thread and wait for it to get started */s_started = 0;pthread_mutex_lock(&s_startupMutex);pthread_attr_init (&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);LOGD("-1-ril.cpp--kai-kou--pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);------");ret = pthread_create(&s_tid_dispatch, &attr,eventLoop, NULL);//创建线程,为入口函数LOGD("-2.....-ril.cpp--tiao-chu--rpthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);----");while (s_started == 0) {pthread_cond_wait(&s_startupCond, &s_startupMutex);}pthread_mutex_unlock(&s_startupMutex);if (ret < 0) {LOGE("Failed to create dispatch thread errno:%d", errno);return;}}------eventLoop(void *param) {1.---ril_event_init();//初始化void ril_event_init(){MUTEX_INIT();LOGD("-2.2-Ril_evnet.cpp-ril_event_init-shi-xian-han-shu-");FD_ZERO(&readFds);init_list(&timer_list);init_list(&pending_list);memset(watch_table, 0, sizeof(watch_table));}2.--- ret = pipe(filedes);3.---ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL); 在ril/libril/ril_event.cpp实现void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param){LOGD("----RIL_evnent.cpp-shi-xian--void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)-");dlog("~~~~ ril_event_set %x ~~~~", (unsigned int)ev);memset(ev, 0, sizeof(struct ril_event));ev->fd = fd;ev->index = -1;ev->persist = persist;ev->func = func;ev->param = param;fcntl(fd, F_SETFL, O_NONBLOCK);}3.1processWakeupCallbackstatic void processWakeupCallback(int fd, short flags, void *param) {char buff[16];int ret;LOGD("--2.5-ril_event_set--processWakeupCallback- read(s_fdWakeupRead, &buff, sizeof(buff));---");LOGV("processWakeupCallback");/* empty our wakeup socket out */do {ret = read(s_fdWakeupRead, &buff, sizeof(buff));} while (ret > 0 || (ret < 0 && errno == EINTR));}4.---rilEventAddWakeup (&s_wakeupfd_event);static void rilEventAddWakeup(struct ril_event *ev) {LOGD("-2.7-rilEventAddWakeup--write--tiao--ru-- ril_event_add(ev);---");ril_event_add(ev);LOGD("--2.8--ril_event_add(ev);---tiao--chu---");triggerEvLoop();}4.1---void ril_event_add(struct ril_event * ev) 在ril/libril/ril_event.cpp实现{LOGD("---RIL_evnent.cpp--shi--xian--void ril_event_add(struct ril_event * ev)----");dlog("~~~~ +ril_event_add ~~~~");MUTEX_ACQUIRE();for (int i = 0; i < MAX_FD_EVLL) {//1:watch_table[i] = ev;//把上面ril_event_add函数添加的事件_wakeupfd_event结构体添加到这个数组来ev->index = i;dlog("~~~~ added at %d ~~~~", i);dump_event(ev);//2:FD_SET(ev->fd, &readFds);if (ev->fd >= nfds) nfds = ev->fd+1;dlog("~~~~ nfds = %d ~~~~", nfds);break;ENTS; i++) {if (watch_table[i] == NU}}MUTEX_RELEASE();dlog("~~~~ -ril_event_add ~~~~");}4.2---static void triggerEvLoop() {int ret;LOGD("--- ret = write (s_fdWakeupWrite, " ", 1);---");if (!pthread_equal(pthread_self(), s_tid_dispatch)) {/* trigger event loop to wakeup. No reason to do this,* if we're in the event loop thread */do {ret = write (s_fdWakeupWrite, " ", 1);} while (ret < 0 && errno == EINTR);}}5.ril_event_loop();}ril/rild/rild.c->main()为函数入口--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------1.消息队列select为非阻塞的去轮询事件2.read的阻塞的去读取上层发下来的命令,并响应--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------int main(int argc, char **argv){const char * rilLibPath = NULL;char **rilArgv;void *dlHandle;const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);const RIL_RadioFunctions *funcs;char libPath[PROPERTY_VALUE_MAX];unsigned char hasLibArgs = 0;........OpenLib:#endifswitchUser();/*打开dlopen()函数,就会动态去加载动态库vendor RIL 获取由RIL_register(funcs);注册进来的参数,并解析*/ dlHandle = dlopen(rilLibPath, RTLD_NOW);if (dlHandle == NULL) {fprintf(stderr, "dlopen failed: %s\n", dlerror());exit(-1);}/*消息队列的入口,添加到select,用阻塞方式去读取那些ril_event_set()的数据##每当看到打印信息,不按顺序打下来说明阻塞##*/RIL_startEventLoop();/*通过dlsym函数得到rilInit函数指针的引用*/rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");if (rilInit == NULL) {fprintf(stderr, "RIL_Init not defined or exported in %s\n", rilLibPath);exit(-1);}if (hasLibArgs) {rilArgv = argv + i - 1;argc = argc -i + 1;} else {static char * newArgv[MAX_LIB_ARGS];static char args[PROPERTY_VALUE_MAX];rilArgv = newArgv;property_get(LIB_ARGS_PROPERTY, args, "");argc = make_argv(args, rilArgv);}// Make sure there's a reasonable argv[0]rilArgv[0] = argv[0];/*利用得到的rilInit函数指针,调用真正的RIL_Init ,实际是动态加载动态库去链接reference-ril.c ,由dlopen()函数加载*/ funcs = rilInit(&s_rilEnv, argc, rilArgv);/*RIL_register作用一:把vendor RIL(即RIL_init) 注册到reference-ril库去等待,dopen()函数加载链接附:RIL_init通过是onRequest()方法,将上层来的请求进行映射后转换成对应的AT命令发给硬件,rild通过RIL_register注册这一指针。
AndroidRIL原生代码(cc++)

AndroidRIL原生代码(cc++)当我们开始编写Android的电话应用程序的时候,如果需要进行电话拨号,可以进行如下调用:ITelephony tpCallModule = (ITelephony)ITelephony.Stub.asInterface(ServiceManager.getServ ice("phon"));tpCallModule.dial("138****8000");而对于短信的应用,我们需要调用的则是SmsManager:SmsManager SMS = SmsManager.getDefault();SMS.sendTextMessage("134****6323",null,"this is a test sms",null,null);到底Android是如何跟底层GSM模块通讯的呢?我一开始就猜测是不是跟WM那样采用RIL的架构来实现,查看了google的官方文档,证实了我的猜测是正确的,Android里面的确采用了RIL架构跟底层GSM模块通讯,先看看RIL在Android里的位置吧。
Android的RIL在这里被做成一个叫做rild的库,通过一个系统环境变量ro.radio.noril是否为空来决定要不要把RIL的代码编译进内核,这点跟CE的编译方式是一样的,如果设置了该变量,将会有个"SimulateCommand"的玩意编译进内核,它可以在没有实际GSM硬件的情况下模拟部分实际硬件的指令,然后让RIL驱动提供给上层应用,如果没有设置该系统环境变量就会采用RIL。
通过研究RIL的代码可以看到,Android的rild库是介于HAL接口与baseband modem之间,它同样提供了语音、数据、短信、SIM 卡管理以及STK应用的功能,实现思路跟微软的RIL有异曲同工之妙,也是把标准的GSM27.007中常用的如dial这些做主动请求的操作称之为request,一共75个;另外一类GSM模块主动上报的例如信号强度、基站信息等,称之为unsolicited response,一共17个;开发模式也是跟微软RIL开发差不多,需要针对不同的GSM模块进行不同的GSM驱动开发,公用的部分google给你做好了,特定的部分需要你自己去定制,这样做可以大大地提高开发效率。
Android_RIL层剖析(一家网站上找的)

/android_pdk/telephony.html这个上的是文还有英文Android 无线接口层(Radio Layer Interface)目录:1、介绍2、RIL 初始化3、RIL 交互----3.1 主动请求----3.2 被动请求4、实现RIL-----RIL初始化5、RIL函数----5.1 RIL 主动命令请求----5.2 RIL 被动请求的命令1 介绍Android的无线接口层(RIL)提供了Android服务(android.telephony)与无线电硬件之间的抽象层。
RIL是通讯无关的,提供基于GSM的网络支持。
下图显示了RIL位于Android系统架构中的位置。
图1实线框表示Android部分,虚线框表示合作伙伴所专用的部分。
RIL包含两个基本部件:RIL守护进程(RIL Daemon):RIL守护进程初始化Vendor RIL,管理所有来自Android通讯服务的通讯,将其作为被请求的命令(solicited commands)调度给Vendor RIL。
Vendor RIL:ril.h文件中的无线电专用Vendor RIL掌管着所有和无线电硬件的通讯,并且通过未被请求的命令(unsolicited commands)分发给RIL 守护进程。
2 RIL 初始化Android在启动时初始化通讯栈和Vendor RIL,描述如下:1. RIL守护进程读取rild.lib路径和rild.libargs系统参数,决定应该使用的Vendor RIL库和向Vendor RIL提供的初始化参数2. RIL守护进程加载Vendor RIL库,执行RIL_Init初始化RIL并为RIL函数获取参数。
3. RIL守护进程调用Android通讯栈中RIL_register,为Vendor RIL函数提供参考。
RIL守护进程源码请参考//device/commands/rild/rild.c3 RIL 交互RIL句柄提供了两种交互方式:主动请求命令(Solicited commands):主动请求命令来自RIL lib,比如DIAL 和HANGUP。
Android GSM驱动模块详细分析

Android GSM驱动模块(rild)详细分析Android的RIL驱动模块,在hardware/ril目录下,一共分rild,libril.so以及librefrence_ril.so 三个部分,另有一radiooptions可供自动或手动调试使用。
都依赖于include目录中ril.h头文件。
目前cupcake分支上带的是gsm的支持,另有一cdma分支,这里分析的是gsm驱动。
GSM模块,由于Modem的历史原因,AP一直是通过基于串口的AT命令与BB交互。
包括到了目前的一些edge或3g模块,或像omap这类ap,bp集成的芯片,已经使用了USB或其他等高速总线通信,但大多仍然使用模拟串口机制来使用AT命令。
这里的RIL(Radio Interface Layer)层,主要也就是基于AT命令的操作,如发命令,response解析等。
(gprs等传输会用到的MUX协议等在这里并没有包含,也暂不作介绍。
)首先介绍一下rild与libril.so以及librefrence_ril.so的关系:1. rild:仅实现一main函数作为整个ril层的入口点,负责完成初始化。
2. libril.so:与rild结合相当紧密,是其共享库,编译时就已经建立了这一关系。
组成部分为ril.cpp,ril_event.cpp。
libril.so驻留在rild这一守护进程中,主要完成同上层通信的工作,接受ril请求并传递给librefrence_ril.so,同时把来自librefrence_ril.so的反馈回传给调用进程。
3. librefrence_ril.so:rild通过手动的dlopen方式加载,结合稍微松散,这也是因为librefrence.so主要负责跟Modem硬件通信的缘故。
这样做更方便替换或修改以适配更多的Modem种类。
它转换来自libril.so的请求为AT命令,同时监控Modem的反馈信息,并传递回libril.so。
Android RIL 分析

RIL的Request流程(4)
onRequest方法会通过传入的请求类型来调用指定的
request×××()方法,该方法则负责组装AT指令并下 发给at_send_command()方法集合中的一个,这个方 法集合提供了针对不同类型AT指令的实现,如单行AT 指令at_send_command_singleline(),短信息指令 at_send_command_sms()等。 最后,执行at_send_command_full(),再通过一个互斥 的at_send_command_full_nolock()调用,完成最终的 写出操作,在writeline()中,写出到初始化时打开的设备 中。
SMS在RIL中的处理流程1
应用中使用android.telephony.smsManager类的
sendTextMessage()方法发送sms smsManager类通过AIDL接口(注1)与Java Framework 中的 com.android.internal.telephony.IccSmsInterfaceMana ger通信 IccSmsInterfaceManager最终通过 com.android.internal.telephony.ril,将sms数据与 RIL_REQUEST_SEND_SMS消息,通过JNI接口(注2) 下发到RIL的C/C++实现部份中去。
ห้องสมุดไป่ตู้
普通上报的Response处理
IsFinalResponse()和isFinalResponseError()所处理的是一条AT指令的
RIL的Response流程
AT的response有两种,一种是unsolicited。另一种是普通
adroid通信模块ril解析

第一部分简介简单介绍性部分:Android电话系统围绕底层使用Modem硬件来搭建,提供呼叫、短信和网络连接功能,其中modem也称为基带。
Modem驱动程序包含在Linux内核层中,所以3G/4G模块的搭建其实就是电话系统的搭建主流3G/4G模块:主要分有内置(集成在处理器中如高通等,高通拥有相关基带的专利,内置模块主要通过共享内存与处理器通信),外置(较常见,大多通过usb转串口接口并利用AT命令与处理器通信,usb串口一般使用标准的驱动,AT命令由Hayes公司发明,一种是调制解调器命令语言每条命令以AT (也有一些笔记本专用的接口类型如PCIE和NGFF,手机上的LGA,工业用B2B等等)对与目前的通信模块来说,都已经非常成熟,有有着接口简单和统一的软件接口一般大多数模块装上SIM卡直接上电即可完成初始找网、网络注册等工作。
框架部分主要是Android:1 电话系统大体框架(图1):分三大层,硬件(连接modem设备),Android系统(主要包括了Linux内核层,运行库层和Framework层),平台API(即再往上为应用层)图(1)电话系统大体框架2.电话系统部分结构图(2):具体实现自下到上:Modem驱动,RIL库、RIL守护进程,电话JA V A框架和电话应用(其中电话系统部分无JNI)Kernel Space 属于驱动层在内核中实现,一般分为AT命令通道和数据通道两路接口。
RIL层:贯穿Android的内核层至应用层,由三部分组成:RIL守护进程(本质是rild 可执行程序,开机通过init启动,与framework层主要通过socket来进行)、libril库和ril实现库(电话层硬件抽象层)。
主要负责负责数据的可靠传输、AT命令的发送以及response(来自modem)的解析图(2)电话系统部分结构图3.RIL接口下层结构(图3)(更细化的下层)ril实现库的接口非常复杂,需要处理的命令,相关结构体比较多,其接口主要定义在hardware/ril/include/telephony目录的ri.h中。
Android中RIL层详细分析

Android的电话功能介绍——整个RIL文件夹的分析介绍本文档对Android RIL部分的内容进行了介绍,其重点放在了Android RIL的原生代码部分。
包括四个主题:1。
Android RIL框架介绍2。
Android RIL与WindowsMobile RIL3。
Android RIL porting4。
Android RIL的java框架在本文档中将Android代码中的重要模块列出进行分析,并给出了相关的程序执行流程介绍,以加深对模块间交互方式的理解。
对于java代码部分,这里仅进行简单的介绍。
如果需要深入了解,可以查看相关参考资料.本文档中还对Android RIL的Porting部分内容进行了描述和分析。
针对对unix操作系统环境并不熟悉的读者,本文档中所涉及到的相关知识包括:Unix file systemUnix socketUnix threadUnix 下I/O多路转接以上信息可以在任意一份描述Unix系统调用的文档中找到.1.Android RIL框架介绍术语:fd Linux文件描述符pipe Linux管道cond 一般是condition variable的缩写tty 通常使用tty来简称各种类型的终端设备unsolicited response 被动请求命令来自basebandevent loop android的消息队列机制,由Linux的系统调用select()实现init.rc init守护进程启动后被执行的启动脚本。
HAL 硬件抽象层(Hardware Abstraction Layer,HAL)1.1 Android RIL概况Android RIL提供了无线硬件设备与电话服务之间的抽象层。
下图展示了RIL在Android 体系中的位置.android的ril位于应用程序框架与内核之间,分成了两个部分,一个部分是rild,它负责socket与应用程序框架进行通信.另外一个部分是Vendor RIL,这个部分负责向下是通过两种方式与radio进行通信,它们是直接与radio通信的AT指令通道和用于传输包数据的通道,数据通道用于手机的上网功能.对于RIL的java框架部分,也被分成了两个部分,一个是RIL模块,这个模块主要用于与下层的rild进行通信,另外一个是Phone模块,这个模块直接暴露电话功能接口给应用开发用户,供他们调用以进行电话功能的实现。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
1Framework Layer(Java)
1.1数据连结的核心是DataConnectionTracker.java, 主要流程是:
Android 2.1:
DataConnectionTracker -> GsmDataConnectionTracker.OnTrySetupData ->
GsmDataConnectionTracker.trySetupData -> GsmDataConnectionTracker.setupData ->
PdpConnection.connect
Android 2.2
DataConnectionTracker -> GsmDataConnectionTracker.OnTrySetupData ->
GsmDataConnectionTracker.trySetupData -> GsmDataConnectionTracker.setupData ->
GsmDataConnection.connect
在上述的connect函数中,通过如下语句
phone.mCM.setupDataCall(…)
实际上调用了CommandsInterface. setupDataCall(),这里只是一个Interface的定义。
RIL.java实现了这个Interface:
public final class RIL extends BaseCommands implements CommandsInterface {
所以在上述的connect函数实际调用了RIL. setupDataCall()
在RIL. setupDataCall()中发送了一个叫RIL_REQUEST_SETUP_DATA_CALL的请求,这
个请求最后会被reference-ril捕获,在reference-ril.onRequest中处理,代码如下:case RIL_REQUEST_SETUP_DATA_CALL:
requestSetupDataCall(data, datalen, t);
break;
原生的Android代码中reference-ril.requestSetupDataCall()
是以模拟器为例,建立的。
所以在实际硬件上应该重新修改本函数。
1.2
2APN
2.1保存位置
2.1.1android/development/data/etc/apns-conf_sdk.xml ---> system/etc/apns-
conf.xml
2.1.2例子:
<apns version="6">
<apn carrier="中国联通3g 网络(China Unicom)"
apn="3gnet"
mcc="460"
mnc="01"
proxy=""
port=""
user=""
server=""
password=""
type="default,supl"
/>
</apns>
2.2解析代码位置:
android/packages/providers/TelephonyProvider/src/com/android/providers/telephon
y/TelephonyProvider.java
2.3方法:
根据system/etc/apns-conf.xml 创建
/data/data/com.android.providers.telephony/databases/telephony.db
2.4
3拨号流程
asprintf(&cmd, "AT+CGACT=0,%d", s_defultPDPCid);
err = at_send_command(cmd, NULL);
free(cmd);
asprintf(&cmd, "AT+CGDCONT=%d,/"IP/",/"%s/",,0,0", s_defultPDPCid, apn);
err = at_send_command(cmd, NULL);
free(cmd);
AT 命令:
AT
ATZ
AT+CGDCONT=1,"IP","3gnet",,0,0
ATDT*99#
4加载Rild时,定义参数
service ril-daemon /system/bin/rild -l /system/lib/libreference-ril.so -- -d /dev/ttyS0
5总体结构
6关于PPP的端口:通常情况下PPP和A T不用同一个设备节点。
7
8备注:
8.1/proc/cmdline
/boot/grub/menu.lis
...
kernel /boot/vmlinuz root=/dev/hdc6 vga=789
...
#cat /proc/cmdline
root=/dev/hdb6 rw
8.2。