吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4433|回复: 6
收起左侧

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

[复制链接]
wushaominkk 发表于 2018-5-3 09:55
本帖最后由 wushaominkk 于 2018-5-3 16:02 编辑

看雪同步:
https://bbs.pediy.com/thread-226491.htm
Android的通话RIL通信由浅到深跨越了三个层次:
第一层 Applications应用层 (Dialer拨号盘和Phone应用)
第二层 Frameworks框架层(Telephony Frameworks)
第三层 UserLibraries系统运行库层(HAL)

QQ图片20180502170015.png
图片转至《深入理解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主要代码:
[Java] 纯文本查看 复制代码
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关键代码解析:
[C++] 纯文本查看 复制代码
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;  


[C++] 纯文本查看 复制代码
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  








免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
pj5008 + 1 + 1 用心讨论,共获提升!
zy1234 + 1 + 1 用心讨论,共获提升!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

qq20036 发表于 2018-5-3 10:39
一脸懵逼的进来,一脸懵逼的出去.,每个字我都认识连起来就不知道怎么回事了.
liyu6056 发表于 2018-5-3 11:02
本帖最后由 liyu6056 于 2018-5-3 11:04 编辑

膜拜学习,感谢分享,已然收藏,后续继续深入理解。好巧,居然没有免费评分了。
nohack 发表于 2018-5-3 14:37
Narrator 发表于 2018-5-3 23:38
长知识了,感谢楼主
ruby 发表于 2018-5-4 08:45
不明觉厉
wula770202 发表于 2018-5-5 09:06
赞,谢谢楼主谢谢吾爱破解
wabanana 发表于 2018-5-5 14:33
很难懂的呢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-24 22:10

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表