深入分析RIL通信初始化原理
本帖最后由 wushaominkk 于 2018-5-3 16:02 编辑看雪同步:
https://bbs.pediy.com/thread-226491.htm
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,上层需要监听上述状态变化时,只需要实现上述接口!
详情查看我另外一篇帖子:[系统漏洞]模拟耳机广播实现来电自动接听和拒接
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 = rilInit(&s_rilEnv, argc, s_argv);
//多卡模式
if (isMultiSimEnabled() && !isMultiRild()) {
}
RIL_setMaxNumClients(numClients);
//注册客户端事件处理接口,并创建socket监听事件
for (i = 0; i < numClients; i++) {
RIL_register(funcs_inst, 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;
#endif
一脸懵逼的进来,一脸懵逼的出去.{:1_926:},每个字我都认识连起来就不知道怎么回事了. 本帖最后由 liyu6056 于 2018-5-3 11:04 编辑
{:1_894:}膜拜学习,感谢分享,已然收藏,后续继续深入理解。好巧,居然没有免费评分了。{:1_918:} 学习了学习了,虽然有很多知识不是很了解 长知识了,感谢楼主 不明觉厉:lol 赞,谢谢楼主谢谢吾爱破解 很难懂的呢
页:
[1]