wushaominkk 发表于 2018-5-3 09:55

深入分析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







qq20036 发表于 2018-5-3 10:39

一脸懵逼的进来,一脸懵逼的出去.{:1_926:},每个字我都认识连起来就不知道怎么回事了.

liyu6056 发表于 2018-5-3 11:02

本帖最后由 liyu6056 于 2018-5-3 11:04 编辑

{:1_894:}膜拜学习,感谢分享,已然收藏,后续继续深入理解。好巧,居然没有免费评分了。{:1_918:}

nohack 发表于 2018-5-3 14:37

学习了学习了,虽然有很多知识不是很了解

Narrator 发表于 2018-5-3 23:38

长知识了,感谢楼主

ruby 发表于 2018-5-4 08:45

不明觉厉:lol

wula770202 发表于 2018-5-5 09:06

赞,谢谢楼主谢谢吾爱破解

wabanana 发表于 2018-5-5 14:33

很难懂的呢
页: [1]
查看完整版本: 深入分析RIL通信初始化原理