RILD
来源:互联网 发布:数据网关介绍 编辑:程序博客网 时间:2024/05/21 11:25
五、一个完整的过程
一个完整的过程包括以下四个步骤:
1、Eventloop接收RILJ的请求,并负责把请求发送给reference库:RILJ –> Eventloop –> reference
2、reference负责把命令转化为AT命令,然后发送给Modem:reference –> Modem
3、reference通过readerLoop得到Modem回应后把数据返回给Eventloop: Modem—>ReaderLoop
4、Eventloop再把数据返回给RILJ:ReaderLoop—>LibRIL–>RILJ
此处以RILJ获取SimStatus的request为例说明。
先来一张总的流程图:
5.1 RILJ –> Eventloop –> reference
总览如下:
Step1:为EventLoop的fd_listen监听到RILJ发送去request信息
Step2:使用fd_listen创建fdCommand句柄以及Event监听Socket流
Step3:中处理fdCommand流,获取到request和token。并找到对应CommandInfo
Step4:调用CommandInfo中对应dispatchFunction封装构建RequestInfo
Step5:调用ReferenceRIL中onRequest函数向ReferenceRIL发起请求
5.1.2 Step1
RILJ向RILC发送获取CardStatus的消息
@Overridepublic voidgetIccCardStatus(Message result) { //Note: This RIL request has not been renamed to ICC, // but this request is also valid for SIM and RUIM RILRequest rr = RILRequest.obtain(RIL_REQUEST_GET_SIM_STATUS, result); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)); send(rr);}
5.1.3 Step2
上面说到创建了包含Socket句柄的Event来监听RILJ发送来的信息,的那个fd_Listen监听到新信息时,会在EventLoop中触发Event的回调函数,此处Event的回调函数为listenCallback(),下面来看下如何接收和处理RILJ的信息。
static void listenCallback (int fd, short flags, void *param) { /* 从s_fdListen侦听套接字得到s_fdCommand流套接字 accept()接受一个客户端的连接请求,并返回一个新的套接字。 所谓“新的”就是说这个套接字与socket()返回的用于监听和接受客户端的连接请求的套接字不是同一个套接字。 与本次接受的客户端的通信是通过在这个新的套接字上发送和接收数据来完成的。 可以将s_fdListen理解为服务器的侦听端口,fdCommand为端口侦听到信息后创建的信息流 */ fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen); //获取当前命令的参数 err = getsockopt(fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); //设置fdCommand为非阻塞 ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK); //将fdCommand放入SocketListenParam p_info->fdCommand = fdCommand; //使用fdCommand构建一个RecordStream,此流用来读数据 p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES); //将其放入SocketListenParam中 p_info->p_rs = p_rs; //使用fdCommand构建commands_event,回调函数为SocketListenParam内的processCommandsCallback //注意到这里persist参数为true,所以需要手动删除Event。 ril_event_set (p_info->commands_event, p_info->fdCommand, 1, p_info->processCommandsCallback, p_info); //添加Event并激活EventLoop rilEventAddWakeup (p_info->commands_event); //发送URC消息,通知RILJ状态发生改变 onNewCommandConnect(p_info->socket_id);}
从以上可知, listenCallback中主要干了两件事:
1. 首先使用fd_Listen创建了一个流套接字(fdCommand)用来接收信息,并使用fdCommand构建了一个RecordStream和一个Event。不难想到,这个Event的回调函数要来读取这个fdCommand所传递的信息,这写信息会从RecordStream中读取出来。这个Event的回调函数为processCommandsCallback(),在4.3.2分析。
2. 调用 onNewCommandConnect回调函数,给予RILJ通知反馈,其主要逻辑如下。
static void onNewCommandConnect(RIL_SOCKET_ID socket_id) { //给RILJ发送URC消息,RIL连接成功 RIL_UNSOL_RESPONSE(RIL_UNSOL_RIL_CONNECTED, &rilVer, sizeof(rilVer), socket_id); //给RILJ发送URC消息,告诉RILJ,Radio状态改变了 RIL_UNSOL_RESPONSE(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED, NULL, 0, socket_id);}
5.1.2 Step3
在此函数中,开始读取和处理RILJ发送的消息:
static void processCommandsCallback(int fd, short flags, void *param) { RecordStream *p_rs; void *p_record; size_t recordlen; int ret; SocketListenParam *p_info = (SocketListenParam *)param; //校验 assert(fd == p_info->fdCommand); //这里获取到了上一步的RecordStream p_rs = p_info->p_rs; for (;;) { /* loop until EAGAIN/EINTR, end of stream, or other error */ //从RecordStream中读数据放入p_record,成功返回0 ret = record_stream_get_next(p_rs, &p_record, &recordlen); if (ret == 0 && p_record == NULL) {//读取完毕 /* end-of-stream */ break; } else if (ret < 0) {//读取失败 break; } else if (ret == 0) { /* && p_record != NULL *///读取成功,未完,需要继续读取 //把RILJ层数据通过AT发送到Modem processCommandBuffer(p_record, recordlen, p_info->socket_id); } } //接收结束后做收尾处理 if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) { /* fatal error or end-of-stream */ if (ret != 0) { RLOGE("error on reading command socket errno:%d\n", errno); } else { RLOGW("EOS. Closing command socket."); } //命令已经发送完成,关闭当前命令的流套接字 close(fd); p_info->fdCommand = -1; s_fdCommand[p_info->socket_id] = -1; //删掉当前Event(注:此Event的persist属性为true,需要手动删除) ril_event_del(p_info->commands_event); //释放RecordStream record_stream_free(p_rs); //重新添加RILJ与RILC之间的Socket Event //重新添加Listen Event,继续监听下一个连接 //注:s_listen_event的persist属性为false,所以每次连接完一次之后会被销毁,需要继续添加 /* start listening for new connections again */ rilEventAddWakeup(&s_listen_event[p_info->socket_id]); //fdCommand关闭时回调 onCommandsSocketClosed(p_info->socket_id); }}
从以上代码得知,主要干了两件事
1. 从RecordStream获取数据,传给Modem
2. 重新添加Socket监听Event
processCommandBuffer()函数:
static intprocessCommandBuffer(void *buffer, size_t buflen, RIL_SOCKET_ID socket_id) { Parcel p; status_t status; int32_t request; int32_t token; RequestInfo *pRI; int ret; //将数据设置给Parcel p.setData((uint8_t *) buffer, buflen); // status checked at end //读请求码 status = p.readInt32(&request); //读令牌 status = p.readInt32 (&token); //初始化RequestInfo pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo)); //设置令牌 pRI->token = token; //设置CommandInfo pRI->pCI = &(s_commands[request]); //设置SocketID pRI->socket_id = socket_id; //设置下一个节点,RequestInfo为链表结构 pRI->p_next = *pendingRequestsHook; //调用CommandInfo的分发函数 pRI->pCI->dispatchFunction(p, pRI);}
以上代码流程较简单,就是从参数数据中获取请求码(request)和令牌(token),然后用他们构建RequestInfo,最后调用RequestInfo中CommandInfo的dispatchFunction()函数,dispatchFunction函数是一个函数指针,对应不同的request有不同的实现。
那么就有几个疑问:
1.什么是CommandInfo?
形如:{RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus}
CommandInfo包含了辨别RILJ请求类型,请求处理方式,请求结果返回方式的信息。
详见4.3.2。
什么是请求码(request)?
从s_commands[request]得知,请求码(request)对应的是s_commands数组元素索引值。在RILJ和RILC中,这两个值要相互对应。什么是令牌(token)?
牌可以看作当前请求的ID,当我们从Modem得到数据后需要根据不同的令牌找到当初的请求命令,然后做出相应的答复。什么是RequestInfo?
包含请求信息的结构体,见4.3.3
5.1.2 Step4
上述函数最后调用CommandInfo的分发函数,将当前Request分发。
static voiddispatchVoid (Parcel& p, RequestInfo *pRI) { clearPrintBuf;printRequest(pRI->token, pRI->pCI->requestNumber);//打印log//宏定义:#define CALL_ONREQUEST(a, b, c, d, e) s_callbacks.onRequest((a), (b), (c), (d)) CALL_ONREQUEST(pRI->pCI->requestNumber, NULL, 0, pRI, pRI->socket_id);}
在分发函数中,最终调用了ReferenceRIL中的onRequest函数,注意这里的s_callbacks是在ReferenceRIL初始化时传过来的回调函数。
对于其他类型的分发函数,如dispatchString(), dispatchStrings() , dispatchInts()等,最终都是调用的 onRequest函数,只是不同的分发函数在这之前对数据做了不同的处理,最终onRequest的参数不同。
5.1.2 Step5
//@ril_callbacks.cstatic void onRequest(int request, void *data, size_t datalen, RIL_Token t, RIL_SOCKET_ID socket_id){ //获取radioState RIL_RadioState radioState = sState; //若modem off,一些请求被过滤 if (s_md_off && ...){ ... return; } //若Radio不可用,一些请求被过滤 if (radioState == RADIO_STATE_UNAVAILABLE && ...){ ... return; } //若Radio关闭,一些请求被过滤 if (radioState == RADIO_STATE_OFF &&...){ ... return; } //其他过滤操作 ... //这里是主要处理逻辑、 //不同类型的请求交由不同的函数来处理,他们定义在不同文件中,达到解耦的目的 //如rilSimMain()函数定义在mtk-ril/ril_sim.c文件中 //这里使用了短路或,如果前面有函数处理了请求,会直接跳出。 if (!(rilSimMain(request, data, datalen, t) || rilNwMain(request, data, datalen, t) || rilCcMain(request, data, datalen, t) || rilSsMain(request, data, datalen, t) || rilSmsMain(request, data, datalen, t) || rilStkMain(request, data, datalen, t) || rilOemMain(request, data, datalen, t) || rilDataMain(request, data, datalen, t)|| // MTK-START eMBMS feature. rilEmbmsMain(request, data, datalen, t)|| rilPhbMain(request, data, datalen, t))) { if (IMS_isRilRequestFromIms(t)) { IMS_RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); } else { RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); } }}
以上代码表明,对于不同模块的请求,会交给不同 的函数来处理,这些对应不同模块的处理函数分别定义在不同的模块C文件。具体可以参考mtk-ril目录。
由于我们关注的是RIL_REQUEST_GET_SIM_STATUS,其会进入rilSimMain ()中来处理
5.2 reference –> Modem
总览如下:
Step6:ReferenceRIL中调用不同模块对应请求的处理函数
Step7-9:根据Radio的状态等信息做预处理,构建发送到Modem的AT命令
Step10-12:发送AT命令到Modem,并等待Modem返回信息
5.2.1 Step6
//@mtk-ril/ril_sim.cextern int rilSimMain(int request, void *data, size_t datalen, RIL_Token t){ switch (request) { case RIL_REQUEST_GET_SIM_STATUS: requestGetSimStatus(data,datalen,t); break; ... default: //如没有处理的返回0 return 0; /* no match */ } return 1; /* request find */}
此方法对于不同的request会执行不同的函数,此处执行requestGetSimStatus()
5.2.1 Step7-9
static void requestGetSimStatus(void *data, size_t datalen, RIL_Token t){ RIL_CardStatus_v6 *p_card_status = NULL; char *p_buffer; int buffer_size; //与Modem交互,获取SIM Status并填充到p_card_status int result = getCardStatus(&p_card_status,rid); if (result == RIL_E_SUCCESS) { //对结果RIL_CardStatus_v6进行处理 p_buffer = (char *)p_card_status; buffer_size = sizeof(*p_card_status); if (RIL_CARDSTATE_PRESENT == p_card_status->card_state && !s_md_off) { //获取剩余的PIN PUK尝试次数 getPINretryCount(&retryCounts, t, rid); ... //获取AppStatus iResult = getIccApplicationStatus(&p_card_status, rid, sessionId); //将CardStatus转换为Char p_buffer = (char *)p_card_status; buffer_size = sizeof(*p_card_status); } } //把CardStatus回应传回给Eventloop RIL_onRequestComplete(t, result, p_buffer, buffer_size);}
上面函数先通过getCardStatus函数获取CardStatus,然后获取卡锁的状态,如果无锁,会获取AppStatus等信息。最后通过回调函数将信息传递给LibRIL。
以上需要关注的两点一个是RIL_CardStatus_v6包含卡的状态信息。二是getCardStatus的函数实现,此实现下面再分析。
RIL_CardStatus_v6数据结构:
typedef struct{ //卡状态 RIL_CardState card_state; //锁状态 RIL_PinState universal_pin_state; /* applicable to USIM and CSIM: RIL_PINSTATE_xxx */ int gsm_umts_subscription_app_index; /* value < RIL_CARD_MAX_APPS, -1 if none */ int cdma_subscription_app_index; /* value < RIL_CARD_MAX_APPS, -1 if none */ int ims_subscription_app_index; /* value < RIL_CARD_MAX_APPS, -1 if none */ int num_applications; /* value <= RIL_CARD_MAX_APPS */ //卡应用状态 RIL_AppStatus applications[RIL_CARD_MAX_APPS];} RIL_CardStatus_v6;
熟悉RILJ的朋友会觉得很眼熟,这些RIL_CardState ,RIL_PinState,RIL_AppStatus与RILJ的UiccCard状态具有对应关系。RILC的卡状态enum定义在ril.h文件中。
然后我们接着分析getCardStatus的函数实现:
int getCardStatus(RIL_CardStatus_v6 **pp_card_status, RIL_SOCKET_ID rid) { //此数组定义了卡的所有可能状态 static RIL_AppStatus app_status_array[] = { // SIM_ABSENT = 0//卡不存在 { RIL_APPTYPE_UNKNOWN, RIL_APPSTATE_UNKNOWN, RIL_PERSOSUBSTATE_READY, NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN }, // SIM_NOT_READY = 1//卡未准备好 { RIL_APPTYPE_SIM, RIL_APPSTATE_DETECTED, RIL_PERSOSUBSTATE_READY, NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN }, // SIM_READY = 2//卡准备好 { RIL_APPTYPE_SIM, RIL_APPSTATE_READY, RIL_PERSOSUBSTATE_READY, NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN }, // SIM_PIN = 3//PIN锁状态 { RIL_APPTYPE_SIM, RIL_APPSTATE_PIN, RIL_PERSOSUBSTATE_READY, NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN }, ... } //获取sim状态 sim_status = getSIMStatus(rid); //将状态放到变量中 p_card_status->applications[0] = app_status_array[sim_status]; //赋给参数指针指向的变量 *pp_card_status = p_card_status; return RIL_E_SUCCESS;}
上面调用了getSIMStatus来从Modem获取卡状态标识,然后把标识对应的状态变量存入CardStatus中。
getSIMStatus实现如下:
SIM_Status getSIMStatus(RIL_SOCKET_ID rid){ //封装AT命令返回结果的变量 ATResponse *p_response = NULL; char *cpinLine; char *cpinResult; //使用AT通道发送命令,并把返回结果封装在p_response中 err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &p_response, getChannelCtxbyProxy(rid)); //从AT返回结果中获取cpinLine cpinLine = p_response->p_intermediates->line; //从cpinLine中获取cpinResult err = at_tok_nextstr(&cpinLine, &cpinResult); //从cpinResult中获取卡状态,具体卡状态可查看ril_sim.h中SIM_Status枚举类 return ret;}
上面getSIMStatus中直接发送AT命令查询SIM卡信息,返回结果封装在ATResponse中。从ATResponse中获取结果,得到SIMStatus。
5.2.1 Step10-12
上面的at_send_command_singleline()函数最终会调用at_send_command_full_nolock()来将请求信息写入AT通道。
static int at_send_command_full_nolock (const char *command, ATCommandType type, const char *responsePrefix, const char *smspdu, long long timeoutMsec, ATResponse **pp_outResponse){ //向AT通道写信息 err = writeline (command); ... //一直等待Modem返回信息,如果Modem返回了信息,会放入finalResponse中 while (sp_response->finalResponse == NULL && s_readerClosed == 0) { if (timeoutMsec != 0) { err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts); } else { err = pthread_cond_wait(&s_commandcond, &s_commandmutex); } if (err == ETIMEDOUT) { err = AT_ERROR_TIMEOUT; goto error; } } ...}
5.3 Modem –> ReaderLoop
总览如下:
Step13:ReadLooper从AT通道获取Modem上发的数据
5.2.1 Step13
static void *readerLoop(void *arg){ for (;;) { const char * line; //从AT通道文件句fd中中读一行,如果超时,返回NULL line = readline(); //如果没读到东西,break if (line == NULL) { break; } if(isSMSUnsolicited(line)) { //对于SMS UNSOL消息的处理,注意这个s_unsolHandler是在at_open中传入的函数参数。 s_unsolHandler (line1, line2); free(line1); } else { //大部分消息在这里处理 processLine(line); } } //读AT通道结束时的回调 onReaderClosed(); return NULL;}
5.4 ReaderLoop –> LibRIL –> RILJ
总览如下:
Step14:处理上发数据,区别UNSOL和SOL
Step15:处理SOL类型的上发数据,将上发数据存入ATResponse,此步骤会唤醒Step11的等待
Step16:Step7函数链调用返回
Step17:调用LibRIL的回调函数RIL_onRequestComplete()
Step18-20:调用Step3中CommandInfo指定的responseFunction,构建要返回到RILJ的信息
Step21-23:将返回信息发送回RILJ
5.4.1 Step14
static void processLine(const char *line){ //sp_response为NULL,表示为USOL消息 //sp_response下面再讨论 if (sp_response == NULL) { /* no command pending */ handleUnsolicited(line); } else if (isFinalResponseSuccess(line)) { //对于SOL消息的处理 sp_response->success = 1; handleFinalResponse(line); } ... pthread_mutex_unlock(&s_commandmutex);}
5.4.1 Step15
static void handleFinalResponse(const char *line, RILChannelCtx *p_channel){ ATResponse *p_response = p_channel->p_response;p_response->finalResponse = strdup(line);//将数据写入p_response pthread_cond_signal(&p_channel->commandcond);}
5.4.1 Step16
使用AT命令从Modem获取到了想要的信息。结果一层层往上传递,最终回到当初发起请求的onRequest函数中。
5.4.1 Step17
这里从onRequest函数的结果返回函数RIL_onRequestComplete()开始说起:
RIL_onRequestComplete是不是很熟悉,它就是前面说到LibRIL中RIL_Env内部的回调函数。
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) { //这个就是前面封装请求信息的变量 RequestInfo *pRI; pRI = (RequestInfo *)t; if (pRI->cancelled == 0) { Parcel p; //写入令牌 p.writeInt32 (pRI->token); //调用返回函数将数据打包 ret = pRI->pCI->responseFunction(p, response, responselen); } //将打包好的数据发送到RILJ sendResponse(p, socket_id);}
上面先将返回的数据写入Parcel中,不同的数据使用不同的函数来写入,即responseFunction。
最后在调用sendResponse将Parcel数据发送给RILJ。
如下来分析:
从前面的请求处理Command:{RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus}
可以看到最终返回数据使用的是responseSimStatus函数
5.4.1 Step18-20
static int responseSimStatus(Parcel &p, void *response, size_t responselen) { responseSimStatusV6(p, response);}static void responseSimStatusV6(Parcel &p, void *response) { RIL_CardStatus_v6 *p_cur = ((RIL_CardStatus_v6 *) response); //将数据写入Parcel p.writeInt32(p_cur->card_state); p.writeInt32(p_cur->universal_pin_state); p.writeInt32(p_cur->gsm_umts_subscription_app_index); p.writeInt32(p_cur->cdma_subscription_app_index); p.writeInt32(p_cur->ims_subscription_app_index); sendSimStatusAppInfo(p, p_cur->num_applications, p_cur->applications);}static void sendSimStatusAppInfo(Parcel &p, int num_apps, RIL_AppStatus appStatus[]) { p.writeInt32(num_apps); startResponse; for (int i = 0; i < num_apps; i++) { p.writeInt32(appStatus[i].app_type); p.writeInt32(appStatus[i].app_state); p.writeInt32(appStatus[i].perso_substate); writeStringToParcel(p, (const char*)(appStatus[i].aid_ptr)); writeStringToParcel(p, (const char*) (appStatus[i].app_label_ptr)); p.writeInt32(appStatus[i].pin1_replaced); p.writeInt32(appStatus[i].pin1); p.writeInt32(appStatus[i].pin2); appendPrintBuf("%s[app_type=%d,app_state=%d,perso_substate=%d,\ aid_ptr=%s,app_label_ptr=%s,pin1_replaced=%d,pin1=%d,pin2=%d],", printBuf,//static char printBuf[PRINTBUF_SIZE];LibRIL缓存的信息 appStatus[i].app_type, appStatus[i].app_state, appStatus[i].perso_substate, appStatus[i].aid_ptr, appStatus[i].app_label_ptr, appStatus[i].pin1_replaced, appStatus[i].pin1, appStatus[i].pin2); } closeResponse;}
这几个调用做的都是将返回数据写入Parcel中。
5.4.1 Step21-23
static intsendResponse (Parcel &p, RIL_SOCKET_ID socket_id) { printResponse; return sendResponseRaw(p.data(), p.dataSize(), socket_id);}static intsendResponseRaw (const void *data, size_t dataSize, RIL_SOCKET_ID socket_id) { //这里的fdCommand就是之前说过的有fd_Listen生成的流套接字 int fd = s_ril_param_socket[socket_id].fdCommand; //写入数据到Socket,上次RILJ会接收到 ret = blockingWrite(fd, data, dataSize);}
调用流套接字将数据返回到RILJ。
自此,RILC的一条请求流程完成。
- rild
- rild
- RILD
- RILD
- RILD
- RILD
- RILD
- RILD
- RILD
- RILD
- rild 术语
- rild进程
- Introduction to Android RILD
- Rild层代码分析
- rild 源码分析
- Android RILD学习
- Android Rild 概述
- Android电话系统rild
- CodeChef:Chef and Problems(分块 & 二分)
- smtp邮件协议
- java.nio.Buffer源码解读
- python使用scrapy爬取qq音乐(二)
- Spark基础-Scala模式匹配
- RILD
- python 基础教程系列12.GUI
- 操作系统:虚拟页式存储管理(缺页中断、页面置换算法)
- 先埋下一些坑,以后再填
- 【位运算符与逻辑运算符知识点】【二进制枚举子集】【just for 状压】
- LeetCode之Merge Two Sorted Lists
- MySql的学习
- (C语言)换热器报价生成器设计报告
- Codeforces Round #426 (Div. 2)The Meaningless Game(思维+二分)