深入分析RIL通信初始化原理

Android的通话RIL通信由浅到深跨越了三个层次:

第一层 Applications应用层 (Dialer拨号盘和Phone应用)
第二层 Frameworks框架层(Telephony Frameworks)

第三层 UserLibraries系统运行库层(HAL)



图片转至《深入理解Android Telephony原理剖析与最佳实践》


Android手机要实现与网络端的通信,需要跨越三个层:
RIL Java(RILJ):负责将上层APP的通信请求发送给HAL层;(第一层和第二层)
RIL C++(RILD): 系统守护进程,负责将RILJ的请求命令发送给CP(Communication Processor)(第三层)
RILJ属于系统Phone进程的一部分,随Phone进程启动而加载;而RILD守护进程是通过Android的Init进程进行加载的。

RILJ分为了两个模块,RIL模块与Phone模块。其中RIL模块负责进行请求以及相应的处理,它将直接与RIL的原声代码进行通信。而Phone模块则向应用程序开发者提供了一系列的电话功能接口。
1.RIL模块结构
在RIL.java中实现了几个类来进行与下层rild的通信。
它实现了如下几个类来完成操作:
RILRequest:代表一个命令请求
     RIL.RILSender:负责AT指令的发送
     RIL.RILReceiver:用于处理主动和普通上报信息
     RIL.RILSender与RIL.RILReceiver是两个线程。
        RILRequest提供了obtain()方法,用于得到具体的request操作,这些操作被定义在RILConstants.java中 (RILConstants.java中定义的request命令与RIL原生代码中ril.h中定义的request命令是相同的),然后通过 send()函数发送EVENT_SEND,在RIL_Sender线程中处理这个EVENT_SEND将命令写入到stream(socket)中去。 Socket是来自常量SOCKET_NAME_RIL,它与RIL 原生代码部分的s_fdListen所指的socket是同一个。
  当有上报信息来到时,系统将通过RILReciver来得到信息,并进行处理。在RILReciver的生命周期里,它一直监视着 SOCKET_NAME_RIL这个socket,当有数据到来时,它将通过readRilMessage()方法读取到一个完整的响应,然后通过 processResponse来进行处理。

2.Phone模块结构
  Android通过暴露Phone模块来供上层应用程序用户使用电话功能相关的接口。它为用户提供了诸如电话呼叫,短信息,SIM卡管理之类的接口调用。它的核心部分是类GSMPhone,这个是Gsm的电话实现,需要通过PhoneFactory获取这个GSMPhone。
  GSMPhone并不是直接提供接口给上层用户使用,而是通过另外一个管理类TelephonyManager来供应用程序用户使用。
  类TelephonyManager实现了android的电话相关操作。它主要使用两个服务来访问telephony功能:
1.ITelephony,提供给上层应用程序用户与telephony进行操作,交互的接口,在packages/apps/Phone中由PhoneInterfaceManager.java实现。
2.ItelephonyRegistry提供了一个通知机制,将底层来的上报通知给框架中需要得到通知的部分,由TelephonyRegistry.java实现。
  GSMPhone通过PhoneNotifier的实现者DefaultPhoneNotifier将具体的事件转化为函数调用,通知到 TelephonyRegistry。TelephonyRegistry再通过两种方式通知给用户,其一是广播事件,另外一种是通过服务用户在 TelephonyRegistry中注册的IphoneStateListener接口,实现回调(回调方式参见android的aidl机制)。
为方便上层实时监听网络状态、通话状态以及CP的状态变化,RIL提供了一个专门的监听接口IPhoneStateListener.aidl,上层需要监听上述状态变化时,只需要实现上述接口!
详情查看我另外一篇帖子:[系统漏洞]模拟耳机广播实现来电自动接听和拒接 https://www.52pojie.cn/thread-710525-1-1.html



Android的RIL驱动模块:
在hardware/ril目录下,一共分rild,libril.so以及librefrence_ril.so三个部分,另有一 radiooptions可供自动或手动调试使用。都依赖于include目录中ril.h头文件。
目前cupcake分支上带的是gsm的支持,另有一 cdma分支,这里分析的是gsm驱动。
  GSM模块,AP一直是通过基于串口的AT命令与BB交互。包括到了目前的一些edge或3g模块,或像omap这类ap,bp集成的芯片,已经使用了USB或其他等高速总线通信,但大多仍然使用模拟串口机制来使用AT命令。这里的RIL(Radio Interface Layer)层,主要也就是基于AT命令的操作,如发命令,response解析等。

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。在初始化时, rild通过符号RIL_Init获取一组函数指针并以此与之建立联系。
4. radiooptions:radiooptiongs通过获取启动参数, 利用socket与rild通信,可供调试时配置Modem参数。
RILD的初始化
1)   Init.rc执行rild,并创建两个socket:/dev/socket/rild和/dev/socket/rild-debug
service ril-daemon /system/bin/rild
socket rild stream 660 root radio
socket rild stream 660 radio system

另外注意一下这两行,在init中会解析这个socket,并初始化这个socket,所以我们在rild中是找不到socket建立的代码,这里就已经完成了。

     socket rild stream 660 root radio
     socket rild-debug stream 660 radio system

进程中rild.c主要代码:

int main(int argc, char **argv)

{

const char * rilLibPath = NULL;

const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);

RIL_setRilSocketName("rild");

//通过属性系统获取lib路径:rild.libpath

property_get(LIB_PATH_PROPERTY, rilLibPath, NULL);

//动态加载链接库返回句柄 dlclose卸载掉动态链接库

dlHandle = dlopen(rilLibPath, RTLD_NOW);

//创建客户端事件监听线程

RIL_startEventLoop();

//通过dlsym定位到需要执行的函数指针

   rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");

//通过属性系统获取参数:rild.libargs

property_get(LIB_ARGS_PROPERTY, args, "");

argc = make_argv(args, s_argv);

//reference-ril.so初始化 处理客户端请求的模块reference-ril.c

//s_rilEnv建立应答回调机制

//返回处理请求的相关接口

funcs_inst[0] = rilInit(&s_rilEnv, argc, s_argv);

//多卡模式

if (isMultiSimEnabled() && !isMultiRild()) {

}

RIL_setMaxNumClients(numClients);

//注册客户端事件处理接口,并创建socket监听事件

for (i = 0; i < numClients; i++) {

RIL_register(funcs_inst[i], i);

}

  done:

while(1) {

// sleep(UINT32_MAX) seems to return immediately on bionic

sleep(0x00ffffff);

}

}


2)      进入rild.cpp的main函数,读取rild.lib的path和rild.libargs系统属性,确定厂商的RIL库和初始化参数。
3)      执行RIL_startEventLoop开启事件队列,进行事件监听。这个函数会建立s_tid_dispatch线程。
4)      加载厂商的RIL库,调用RIL_Init初始化RIL,建立s_tid_mainloop线程。在该线程主循环中会调用at_open建立另一个线程s_tid_reader。
5)      调用RIL_register建立vender ril和ril库之间的联系。获取init.rc中建立的两个socket(rild,rild-debug),进行侦听,并加入消息事件循环中(s_tid_dispatch负责轮询分发)。
RIL_startEventLoop在ril.cpp中实现, 它的主要目的是通过pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL)建立一个dispatch线程,入口点在eventLoop. 而eventLoop中,
会调ril_event.cpp中的ril_event_loop()函数,建立起消息(event)队列机制。

我们来仔细看看这一消息队列的机制,这些代码都在ril_event.cpp中

ril_event.cpp关键代码解析:

enum WakeType {DONT_WAKE, WAKE_PARTIAL, WAKE_FULL}; 

/*一次请求的dispatch和response函数*/ 

typedef struct { 

int requestNumber; 

void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI); 

//只有成功了才回调,主要功能是处理返回值,把返回的数据写到Parcel里面 

int(*responseFunction) (Parcel &p, void *response, size_t responselen); 

} CommandInfo; 

typedef struct { 

int requestNumber; 

int (*responseFunction) (Parcel &p, void *response, size_t responselen); 

WakeType wakeType; 

} UnsolResponseInfo; 

/*

一次请求的信息,保包含了token

*/ 

typedef struct RequestInfo { 

int32_t token;      //this is not RIL_Token ,是一个整形值 

CommandInfo *pCI; //包含了request号,处理函数和处理返回值的函数 

struct RequestInfo *p_next;//链表的下一个元素 

char cancelled; //是否已经cancel了 

/*

responses to local commands do not go back to command process

如果是本地发起的一个request,就不要再将回复发到command进程

*/ 

char local;          

} RequestInfo; 

/*timeout的event使用的,和RequestInfo对应,RequestInfo里面存的是一个

来自客户端(或者debug)的请求信息,UserCallbackInfo存的是一个来自内部

的请求(不是local,local对应于debug事件)

*/ 

typedef struct UserCallbackInfo{ 

RIL_TimedCallback p_callback; 

void *userParam; 

struct ril_event event; 

struct UserCallbackInfo *p_next; 

} UserCallbackInfo;

RIL_RadioFunctions s_callbacks = {0, NULL, NULL, NULL, NULL, NULL}; 

static int s_registerCalled = 0; //RIL_Register已经调用,s_callbacks已经赋值 

static pthread_t s_tid_dispatch;  

static pthread_t s_tid_reader; //本文件的这个线程值没有使用 

static int s_started = 0; //用于标识event_loop已经开始了 

static int s_fdListen = -1; //监听客户端连接的server句柄,连接以后会得到s_fdCommand句柄 

static int s_fdCommand = -1; //接收来自客户端命令的句柄,所有的request都来自这个句柄 

static int s_fdDebug = -1;//监听Debug命令的句柄,连接以后会生成另外一个fd,不过是临时变量 

//下面两个是唤醒多路复用(select)的pipe的两端句柄 

static int s_fdWakeupRead;  

static int s_fdWakeupWrite; 

static struct ril_event s_commands_event; 

static struct ril_event s_wakeupfd_event; 

static struct ril_event s_listen_event; 

static struct ril_event s_wake_timeout_event; //这个没有使用 

static struct ril_event s_debug_event; 

static const struct timeval TIMEVAL_WAKE_TIMEOUT = {1,0}; 

static pthread_mutex_t s_pendingRequestsMutex = PTHREAD_MUTEX_INITIALIZER; 

static pthread_mutex_t s_writeMutex = PTHREAD_MUTEX_INITIALIZER; 

static pthread_mutex_t s_startupMutex = PTHREAD_MUTEX_INITIALIZER; 

static pthread_cond_t s_startupCond = PTHREAD_COND_INITIALIZER; 

static pthread_mutex_t s_dispatchMutex = PTHREAD_MUTEX_INITIALIZER; 

static pthread_cond_t s_dispatchCond = PTHREAD_COND_INITIALIZER; 

static RequestInfo *s_pendingRequests = NULL; //挂起的请求队列,也就是待处理的请求 

static RequestInfo *s_toDispatchHead = NULL; 

static RequestInfo *s_toDispatchTail = NULL; 

static UserCallbackInfo *s_last_wake_timeout_info = NULL; 

static void *s_lastNITZTimeData = NULL; 

static size_t s_lastNITZTimeDataSize; 

#if RILC_LOG 

static char printBuf[PRINTBUF_SIZE]; 

#endif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值