首页
社区
课程
招聘
[原创]深入分析RIL通信初始化原理
发表于: 2018-5-3 14:16 5195

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

2018-5-3 14:16
5195

吾爱破解论坛同步:
https://www.52pojie.cn/thread-734257-1-1.html


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月23日上海龙之梦大酒店举办!

最后于 2018-5-3 16:01 被wushaominkk编辑 ,原因:
收藏
免费 0
支持
分享
最新回复 (2)
雪    币: 63
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
2018-5-4 10:30
0
游客
登录 | 注册 方可回帖
返回
//