1.1 RIL概述
由于Android开发者使用的Modem是不一样的,不同方案使用的Modem也不一样,GSM和CDMA就差别更大了,所以各种指令格式,初始化序列都不一样,为了消除这些差别,Android将ril做了一个抽象,使用一个虚拟电话的概念。这个虚拟电话对象就是GSMPhone(CDMAPhone),Phone对象所提供的功能协议,以及对下层支撑环境的要求都有一个统一的描述,这个描述的实现就是靠RIL来完成适配,主要是通过Reference-ril.so(如果对Linux熟悉的话就知道这是共享库,类似有windows下的dll)来实现。总之,Android RIL提供了无线硬件设备与电话服务之间的抽象层。
以下为Android RILD(RILD是Radio Interface Layer Deamon的简称)的大体结构。
1.2 RIL启动
RIL作为一个daemon进程在init.rc中被配置,如下:
service ril-daemon /system/bin/rild class main socket rild stream 660 root radio //主处理器端的socket,可以接收命令和上报消息,当然真正接收命令和上报消息的是此socket accept出来的clientsocket socket rild-debug stream 660 radio system//主处理器和radio通信的socket user root group radio cache inet misc audio log |
如果研究过Android的init和Zygote孵化进程的过程的话,就立即知道这个进程是由init进程开启的,并且最后肯定会执行到有关RIL的main函数。这其中的过程就不详述了。
我们直接锁定rild.c的main():
int main(int argc, char **argv) { const char * rilLibPath = NULL; char **rilArgv; void *dlHandle; // 这里猜测所有的.so库文件都符合下面的函数结构,所以在Android中可以通过找地址来匹配refrence-rill.so的rilInit函数。 //注意函数指针的返回值为RIL_RadioFunctions ,存放的是以后需要调用的refrence-rill.so的函数指针 const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); const RIL_RadioFunctions *(*rilUimInit)(const struct RIL_Env *, int, char **); char *err_str = NULL;
const RIL_RadioFunctions *funcs; char libPath[PROPERTY_VALUE_MAX]; unsigned char hasLibArgs = 0;
int i; const char *clientId = NULL; RLOGD("**RIL Daemon Started**"); RLOGD("**RILd param count=%d**", argc);
umask(S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH); for (i = 1; i < argc ;) {//解析启动参数 if (0 == strcmp(argv[i], "-l") && (argc - i > 1)) { rilLibPath = argv[i + 1]; i += 2; } else if (0 == strcmp(argv[i], "--")) { i++; hasLibArgs = 1; break; } else if (0 == strcmp(argv[i], "-c") && (argc - i > 1)) { clientId = argv[i+1]; i += 2; } else { usage(argv[0]); } }
if (clientId == NULL) { clientId = "0"; } else if (atoi(clientId) >= MAX_RILDS) { RLOGE("Max Number of rild's supported is: %d", MAX_RILDS); exit(0); } if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) { RIL_setRilSocketName(strncat(rild, clientId, MAX_SOCKET_NAME_LENGTH)); }
if (rilLibPath == NULL) { if ( 0 == property_get(LIB_PATH_PROPERTY, libPath, NULL)) { // No lib sepcified on the command line, and nothing set in props. // Assume "no-ril" case. goto done; } else { rilLibPath = libPath; } }
/* special override when in the emulator */ #if 1 { static char* arg_overrides[5]; static char arg_device[32]; int done = 0;
#define REFERENCE_RIL_PATH "libreference-ril.so"
/* first, read /proc/cmdline into memory */ char buffer[1024], *p, *q; int len; int fd = open("/proc/cmdline",O_RDONLY);
if (fd < 0) { RLOGD("could not open /proc/cmdline:%s", strerror(errno)); goto OpenLib; }
do {//开始读 len = read(fd,buffer,sizeof(buffer)); } while (len == -1 && errno == EINTR);
if (len < 0) { RLOGD("could not read /proc/cmdline:%s", strerror(errno)); close(fd); goto OpenLib; } close(fd);
if (strstr(buffer, "android.qemud=") != NULL)//strstr判断是否是子串 { /* the qemud daemon is launched after rild, so * give it some time to create its GSM socket */ int tries = 5; #define QEMUD_SOCKET_NAME "qemud" //这里是模拟器走的,一般真机的rild socket是在init进程中就被创建了
while (1) { int fd;
sleep(1);
fd = qemu_pipe_open("qemud:gsm");//创建Socket if (fd < 0) {//若果没有获得有效的fd fd = socket_local_client( QEMUD_SOCKET_NAME, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM ); } if (fd >= 0) { close(fd);//注意这里只创建却并没有使用 snprintf( arg_device, sizeof(arg_device), "%s/%s", ANDROID_SOCKET_DIR, QEMUD_SOCKET_NAME );
arg_overrides[1] = "-s"; arg_overrides[2] = arg_device; done = 1;//设置创建成功标识符 break; } RLOGD("could not connect to %s socket: %s", QEMUD_SOCKET_NAME, strerror(errno)); if (--tries == 0) break; } if (!done) { RLOGE("could not connect to %s socket (giving up): %s", QEMUD_SOCKET_NAME, strerror(errno)); while(1) sleep(0x00ffffff); } }
/* otherwise, try to see if we passed a device name from the kernel */ if (!done) do {//这里应该是外部传参配置走的分支 #define KERNEL_OPTION "android.ril=" #define DEV_PREFIX "/dev/"
p = strstr( buffer, KERNEL_OPTION ); if (p == NULL) break;
p += sizeof(KERNEL_OPTION)-1; q = strpbrk( p, " \t\n\r" ); if (q != NULL) *q = 0;
snprintf( arg_device, sizeof(arg_device), DEV_PREFIX "%s", p ); arg_device[sizeof(arg_device)-1] = 0; arg_overrides[1] = "-d"; arg_overrides[2] = arg_device; done = 1;
} while (0);
if (done) { argv = arg_overrides; argc = 3; i = 1; hasLibArgs = 1; rilLibPath = REFERENCE_RIL_PATH;
RLOGD("overriding with %s %s", arg_overrides[1], arg_overrides[2]); } } OpenLib://打开lib,这个lib可以理解为不同modem的驱动 #endif switchUser(); //切换UID为AID_RADIO
dlHandle = dlopen(rilLibPath, RTLD_NOW);//打开dl(refrence-ril.so)
if (dlHandle == NULL) { RLOGE("dlopen failed: %s", dlerror()); exit(EXIT_FAILURE); }
RIL_startEventLoop();//开启RIL循环 //从链接库中(也就是reference-ril.c)寻找RIL_Init函数地址 ,注意返回的是函数指针 rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **)) dlsym(dlHandle, "RIL_Init");
if (rilInit == NULL) { RLOGE("RIL_Init not defined or exported in %s\n", rilLibPath); exit(EXIT_FAILURE); }
dlerror(); // Clear any previous dlerror rilUimInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **)) dlsym(dlHandle, "RIL_SAP_Init"); err_str = dlerror(); if (err_str) { RLOGW("RIL_SAP_Init not defined or exported in %s: %s\n", rilLibPath, err_str); } else if (!rilUimInit) { RLOGW("RIL_SAP_Init defined as null in %s. SAP Not usable\n", rilLibPath); } //若果带参数 if (hasLibArgs) { rilArgv = argv + i - 1; argc = argc -i + 1; } else { static char * newArgv[MAX_LIB_ARGS]; static char args[PROPERTY_VALUE_MAX]; rilArgv = newArgv; property_get(LIB_ARGS_PROPERTY, args, ""); argc = make_argv(args, rilArgv); }
rilArgv[argc++] = "-c"; rilArgv[argc++] = clientId; RLOGD("RIL_Init argc = %d clientId = %s", argc, rilArgv[argc-1]);
// Make sure there's a reasonable argv[0] rilArgv[0] = argv[0]; //调用reference-ril.c中的RIL_Init函数对驱动进行初始化,同时得到reference-ril的回调函数 funcs携带了所需调用reference-ril.so的函数指针 funcs = rilInit(&s_rilEnv, argc, rilArgv); RLOGD("RIL_Init rilInit completed"); //注册得到的reference的回调函数指针集合funcs到ril.cpp(rillib) RIL_register(funcs);
RLOGD("RIL_Init RIL_register completed");
if (rilUimInit) { RLOGD("RIL_register_socket started"); RIL_register_socket(rilUimInit, RIL_SAP_SOCKET, argc, rilArgv); }
RLOGD("RIL_register_socket completed");
done:
RLOGD("RIL_Init starting sleep loop"); while (true) { sleep(UINT32_MAX); } } |
从上面可以看出rild.main主要做了三件事:
1,开启了EventLoop循环,完成RILC与RILJ层数据交互(通过socket)
2,打开动态库reference并构建ReaderLoop循环,完成RILC与Modem的数据交互(通过AT)
3,注册reference的回调函数到ril.cpp
首先来看EventLoop循环,ril.cpp中
extern "C" void RIL_startEventLoop(void) { /* spin up eventLoop thread and wait for it to get started */ s_started = 0; pthread_mutex_lock(&s_startupMutex);
pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //线程创建 int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL); if (result != 0) { RLOGE("Failed to create dispatch thread: %s", strerror(result)); goto done; }
while (s_started == 0) {//等待,直到状态改变,此变量应该是线程共享变量 pthread_cond_wait(&s_startupCond, &s_startupMutex); }
done: pthread_mutex_unlock(&s_startupMutex); } |
这里明显创建了一个线程,入口函数为eventLoop
static void *eventLoop(void *param) { int ret; int filedes[2];
ril_event_init();//event初始化,ril_event实质上是一个链表①
pthread_mutex_lock(&s_startupMutex);
s_started = 1;//注意这里设置了标示符,此线程的父线程的等待状态被解除 pthread_cond_broadcast(&s_startupCond);//这里就是发信号,解除父线程的wait状态
pthread_mutex_unlock(&s_startupMutex);//解锁
ret = pipe(filedes);//此函数将两个描述符变成pipe,这个pipe仅仅是为了唤醒select的
if (ret < 0) { RLOGE("Error in pipe() errno:%d", errno); return NULL; } //这里是一个典型的pipe s_fdWakeupRead = filedes[0];//读端fd s_fdWakeupWrite = filedes[1];//写端fd //设置管道读端属性 fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK); //将pipe创建一个Event,使得在别的线程添加event后select被唤醒 ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true, processWakeupCallback, NULL); //将上面创建的Event加入到watch_table中,这里起到唤醒LoopEvent的作用 rilEventAddWakeup (&s_wakeupfd_event);
// 下面的循环除非出错,不会返回 ril_event_loop(); RLOGE ("error in event_loop_base errno:%d", errno); // 在返回错误的时候,杀掉自己,以图重启 kill(0, SIGKILL);
return NULL; } |
① 处event链表的初始化
void ril_event_init() { MUTEX_INIT(); //清零,如果对Linux的socket编程比较熟悉的话,看到这个函数就知道之后的操作肯定会有select FD_ZERO(&readFds); //三个list分别初始化 init_list(&timer_list); init_list(&pending_list); memset(watch_table, 0, sizeof(watch_table)); } |
② Loop循环
void ril_event_loop() { int n; fd_set rfds; struct timeval tv; struct timeval * ptv; //死循环检测event for (;;) {
// 复制read fd_set供select memcpy(&rfds, &readFds, sizeof(fd_set)); //计算下一次超时时间 if (-1 == calcNextTimeout(&tv)) { // no pending timers; block indefinitely dlog("~~~~ no timers; blocking indefinitely ~~~~"); ptv = NULL; } else { dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec); ptv = &tv; } printReadies(&rfds); //下面是典型的linux的select //用select去扫描readFds中的所有管道集合,检测RILJ是否有新数据下发过来。 n = select(nfds, &rfds, NULL, NULL, ptv);//可读性集合检测 printReadies(&rfds); dlog("~~~~ %d events fired ~~~~", n); if (n < 0) { if (errno == EINTR) continue;
RLOGE("ril_event: select error (%d)", errno); // bail? return; }
// time_list检查超时事件,如果超时,将Event放入pending_list中 processTimeouts(); //检查watch_table,将Event加入pending_list中 processReadReadies(&rfds, n); // 执行pending_list中的event,pending_list才是真正执行的event链表 firePending(); } } |
上面的for循环可以清晰的看到,当RILJ向RILD发送数据后,在EventLoop中会依次被timer_list、watch_table、pending_list处理;并且由于有计算超时时间并设置进了select,使得select会在定时任务到时间后返回,从而被执行。接下来主要分析firePending()
static void firePending() { dlog("~~~~ +firePending ~~~~"); struct ril_event * ev = pending_list.next; while (ev != &pending_list) {//遍历panding_list struct ril_event * next = ev->next; removeFromList(ev);//从pending_list中删除 ev->func(ev->fd, 0, ev->param);//开始处理 ev = next; } dlog("~~~~ -firePending ~~~~"); } |
上面的循环过程说明,eventLoop的是通过在内部循环中用Linux中的select方法检测readFds中所有的文件句柄(或者说管道),如果发现有新的数据进来,就去遍历watch_table和timer_list表,把需要处理的eventLoop加入到pending_list中,然后进入pending_list中去执行每个Event的func。注意这里的循环执行的是从RILJ下发来的命令,而非Modem上传的AT命令。
在这一步中,RIL需要加载一个AT相关的***ril.so的动态链接库。之所以使用库的形式,就是考虑到每个厂商使用的Modem不同,我们没法用统一的接口去向底层负责,因此使用库的形式。这样一来,不同的Modem厂商提供不同的链接库,只要符合RIL层的框架即可。而当前的链接库中最主要的就是就是reference-ril.c和atchannel.c文件。
而reference库需要完成两个任务:
1、将eventLoop中的命令通过AT发送给Modem;
2、构建一个readerLoop循环,接受Modem消息,并根据消息的不同(URC和非URC)将消息返回给eventLoop(非URC消息)或者直接发送给RILJ(URC消息)。
其实说白了就是读写!来看RIL_Init()
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv) { int ret; int fd = -1; int opt; pthread_attr_t attr;
s_rilenv = env;
while ( -1 != (opt = getopt(argc, argv, "p:d:s:c:"))) { switch (opt) { case 'p': s_port = atoi(optarg); if (s_port == 0) { usage(argv[0]); return NULL; } RLOGI("Opening loopback port %d\n", s_port); break;
case 'd': s_device_path = optarg; RLOGI("Opening tty device %s\n", s_device_path); break;
case 's': s_device_path = optarg; s_device_socket = 1; RLOGI("Opening socket %s\n", s_device_path); break;
case 'c': RLOGI("Client id received %s\n", optarg); break;
default: usage(argv[0]); return NULL; } }
if (s_port < 0 && s_device_path == NULL) { usage(argv[0]); return NULL; }
sMdmInfo = calloc(1, sizeof(ModemInfo));//分配内存 if (!sMdmInfo) { RLOGE("Unable to alloc memory for ModemInfo"); return NULL; } pthread_attr_init (&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);//开启循环,维护AT通道的开通
return &s_callbacks; } |
进入线程mainloop
static void * mainLoop(void *param __unused) { int fd; int ret;
AT_DUMP("== ", "entering mainLoop()", -1 ); at_set_on_reader_closed(onATReaderClosed); at_set_on_timeout(onATTimeout);
for (;;) { fd = -1; while (fd < 0) { if (s_port > 0) { fd = socket_loopback_client(s_port, SOCK_STREAM); } else if (s_device_socket) { if (!strcmp(s_device_path, "/dev/socket/qemud")) { /* Before trying to connect to /dev/socket/qemud (which is * now another "legacy" way of communicating with the * emulator), we will try to connecto to gsm service via * qemu pipe. */ fd = qemu_pipe_open("qemud:gsm");//Init主线程创建的socket,此处调用 if (fd < 0) { /* Qemu-specific control socket */ fd = socket_local_client( "qemud", ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_STREAM ); if (fd >= 0 ) { char answer[2];
if ( write(fd, "gsm", 3) != 3 || read(fd, answer, 2) != 2 || memcmp(answer, "OK", 2) != 0) { close(fd); fd = -1; } } } } else fd = socket_local_client( s_device_path, ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM ); } else if (s_device_path != NULL) { fd = open (s_device_path, O_RDWR); if ( fd >= 0 && !memcmp( s_device_path, "/dev/ttyS", 9 ) ) { /* disable echo on serial ports */ struct termios ios; tcgetattr( fd, &ios ); ios.c_lflag = 0; /* disable ECHO, ICANON, etc... */ tcsetattr( fd, TCSANOW, &ios ); } }
if (fd < 0) { perror ("opening AT interface. retrying..."); sleep(10); /* never returns */ } }
s_closed = 0; //打开AT并把处理URC消息的方法onUnsolicited传进去 ret = at_open(fd, onUnsolicited); if (ret < 0) { RLOGE ("AT error %d on at_open\n", ret); return 0; }
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
// Give initializeCallback a chance to dispatched, since // we don't presently have a cancellation mechanism sleep(1);
waitForClose(); RLOGI("Re-opening after close"); } } |
上面可以看到,不仅打开了AT通道,而且还设置了超时方法。当前线程在打开AT通道后,在waitForClose中阻塞等待,如果AT通道在检测超时后,将会主动的关闭当前的AT通道,此时将会激活waitForClose中的阻塞线程,然后waitForClose将会返回。而一旦waitForClose函数返回,将会再次进入for循环,重新打开AT通道。
/** * Starts AT handler on stream "fd' * returns 0 on success, -1 on error */ int at_open(int fd, ATUnsolHandler h) { int ret; pthread_t tid; pthread_attr_t attr;
s_fd = fd; s_unsolHandler = h; s_readerClosed = 0;
s_responsePrefix = NULL; s_smsPDU = NULL; sp_response = NULL;
pthread_attr_init (&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); //开启reader线程,读取上发过来的消息 ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);//又是线程运作
if (ret < 0) { perror ("pthread_create"); return -1; }
return 0; } |
进入reader线程
static void *readerLoop(void *arg) { for (;;) {//死循环 const char * line;
line = readline();//读取命令
if (line == NULL) { break; }
if(isSMSUnsolicited(line)) { char *line1; const char *line2;
// The scope of string returned by 'readline()' is valid only // till next call to 'readline()' hence making a copy of line // before calling readline again. line1 = strdup(line); line2 = readline();//猜测可能为参数
if (line2 == NULL) { break; }
if (s_unsolHandler != NULL) { s_unsolHandler (line1, line2);//URC消息可以直接发送给RILJ } free(line1); } else {//注意这个else对应的if是“if(isSMSUnsolicited(line))” processLine(line); } }
onReaderClosed();
return NULL; } |
上面的readerLoop就是在不断侦测Modem上报的消息,然后根据是否是URC消息来采用不同的处理方式。至于具体的判断依据,在两个地方可以体现:1、通过isSMSUnsolicited判断(如果以CMT/CDS/CBM开头则判断成立);2、也可以在processLine中判断(这是主要的判断依据)。
我们简要说一下processLine判断URC消息的依据。我们知道,如果不是URC消息,那就是非URC消息,也就是我们主动发送的请求,(之前的Solicited消息和UnSolicited消息在这里就对上了号),Modem是作为请求回应给我们发的消息,而在我们给Modem发送消息时,会注册各种的回调函数和用于放置Modem返回值的指针sp_response。而如果是URC消息,那么就没有回调函数,而且sp_response是为空,reference正是通过判断sp_response的内容来达到区分URC消息的目的。
在processLine中对于不同的消息有不同的处理流程:
static void processLine(const char *line) { pthread_mutex_lock(&s_commandmutex);
if (sp_response == NULL) {// sp_response为modem返回的指针,为null为URC消息 /* no command pending */ handleUnsolicited(line);//URC消息处理 } else if (isFinalResponseSuccess(line)) {//非URC消息处理,且success final sp_response->success = 1; //发送回应消息给EventLoop handleFinalResponse(line); } else if (isFinalResponseError(line)) {//非URC消息处理,且error final sp_response->success = 0; handleFinalResponse(line); } else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) { // See eg. TS 27.005 4.3 // Commands like AT+CMGS have a "> " prompt writeCtrlZ(s_smsPDU); s_smsPDU = NULL; } else switch (s_type) {//switch 消息类型 case NO_RESULT: handleUnsolicited(line); break; case NUMERIC: if (sp_response->p_intermediates == NULL && isdigit(line[0]) ) { addIntermediate(line); } else { /* either we already have an intermediate response or the line doesn't begin with a digit */ handleUnsolicited(line); } break; case SINGLELINE: if (sp_response->p_intermediates == NULL && strStartsWith (line, s_responsePrefix) ) { addIntermediate(line); } else { /* we already have an intermediate response */ handleUnsolicited(line); } break; case MULTILINE: if (strStartsWith (line, s_responsePrefix)) { addIntermediate(line); } else { handleUnsolicited(line); } break;
default: /* this should never be reached */ RLOGE("Unsupported AT command type %d\n", s_type); handleUnsolicited(line); break; }
pthread_mutex_unlock(&s_commandmutex); } |
可以看到,URC消息是通过handleUnsolicited处理的。
static void handleUnsolicited(const char *line) { if (s_unsolHandler != NULL) { s_unsolHandler(line, NULL);//这里和之前的函数是一样的处理函数 } } |
这里的s_unsolHandler来自于at_open时的参数,也就是reference-ril.c中的onUnsolicited:
/** * Called by atchannel when an unsolicited line appears * This is called on atchannel's reader thread. AT commands may * not be issued here */ static void onUnsolicited (const char *s, const char *sms_pdu) { char *line = NULL, *p; int err;
/* Ignore unsolicited responses until we're initialized. * This is OK because the RIL library will poll for initial state */ if (sState == RADIO_STATE_UNAVAILABLE) { return; }
if (strStartsWith(s, "%CTZV:")) { /* TI specific -- NITZ time */ char *response;
line = p = strdup(s); at_tok_start(&p);
err = at_tok_nextstr(&p, &response);
free(line); if (err != 0) { RLOGE("invalid NITZ line %s\n", s); } else { RIL_onUnsolicitedResponse ( RIL_UNSOL_NITZ_TIME_RECEIVED, response, strlen(response)); } } else if (strStartsWith(s,"+CRING:") || strStartsWith(s,"RING") || strStartsWith(s,"NO CARRIER") || strStartsWith(s,"+CCWA") ) { RIL_onUnsolicitedResponse ( RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED, NULL, 0); #ifdef WORKAROUND_FAKE_CGEV RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL); //TODO use new function #endif /* WORKAROUND_FAKE_CGEV */ } else if (strStartsWith(s,"+CREG:") || strStartsWith(s,"+CGREG:") ) { RIL_onUnsolicitedResponse ( RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, NULL, 0); #ifdef WORKAROUND_FAKE_CGEV RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL); #endif /* WORKAROUND_FAKE_CGEV */ } else if (strStartsWith(s, "+CMT:")) { RIL_onUnsolicitedResponse ( RIL_UNSOL_RESPONSE_NEW_SMS, sms_pdu, strlen(sms_pdu)); } else if (strStartsWith(s, "+CDS:")) { RIL_onUnsolicitedResponse ( RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT, sms_pdu, strlen(sms_pdu)); } else if (strStartsWith(s, "+CGEV:")) { /* Really, we can ignore NW CLASS and ME CLASS events here, * but right now we don't since extranous * RIL_UNSOL_DATA_CALL_LIST_CHANGED calls are tolerated */ /* can't issue AT commands here -- call on main thread */ RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL); #ifdef WORKAROUND_FAKE_CGEV } else if (strStartsWith(s, "+CME ERROR: 150")) { RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL); #endif /* WORKAROUND_FAKE_CGEV */ } else if (strStartsWith(s, "+CTEC: ")) { int tech, mask; switch (parse_technology_response(s, &tech, NULL)) { case -1: // no argument could be parsed. RLOGE("invalid CTEC line %s\n", s); break; case 1: // current mode correctly parsed case 0: // preferred mode correctly parsed mask = 1 << tech; if (mask != MDM_GSM && mask != MDM_CDMA && mask != MDM_WCDMA && mask != MDM_LTE) { RLOGE("Unknown technology %d\n", tech); } else { setRadioTechnology(sMdmInfo, tech); } break; } } else if (strStartsWith(s, "+CCSS: ")) { int source = 0; line = p = strdup(s); if (!line) { RLOGE("+CCSS: Unable to allocate memory"); return; } if (at_tok_start(&p) < 0) { free(line); return; } if (at_tok_nextint(&p, &source) < 0) { RLOGE("invalid +CCSS response: %s", line); free(line); return; } SSOURCE(sMdmInfo) = source; RIL_onUnsolicitedResponse(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, &source, sizeof(source)); } else if (strStartsWith(s, "+WSOS: ")) { char state = 0; int unsol; line = p = strdup(s); if (!line) { RLOGE("+WSOS: Unable to allocate memory"); return; } if (at_tok_start(&p) < 0) { free(line); return; } if (at_tok_nextbool(&p, &state) < 0) { RLOGE("invalid +WSOS response: %s", line); free(line); return; } free(line);
unsol = state ? RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE : RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE;
RIL_onUnsolicitedResponse(unsol, NULL, 0);
} else if (strStartsWith(s, "+WPRL: ")) { int version = -1; line = p = strdup(s); if (!line) { RLOGE("+WPRL: Unable to allocate memory"); return; } if (at_tok_start(&p) < 0) { RLOGE("invalid +WPRL response: %s", s); free(line); return; } if (at_tok_nextint(&p, &version) < 0) { RLOGE("invalid +WPRL response: %s", s); free(line); return; } free(line); RIL_onUnsolicitedResponse(RIL_UNSOL_CDMA_PRL_CHANGED, &version, sizeof(version)); } else if (strStartsWith(s, "+CFUN: 0")) { setRadioState(RADIO_STATE_OFF); } } |
可以看出,URC消息的处理流程基本上就是根据命令头的不同将其转化为不同的命令索引,然后调用RIL_onUnsolicitedResponse函数。最终还是会回到ril.cpp中。框架和处理方式由ril.c管理,差异化的AT命令由reference实现
extern "C" void RIL_onUnsolicitedResponse(int unsolResponse, const void *data, size_t datalen) #endif { int unsolResponseIndex; int ret; int64_t timeReceived = 0; bool shouldScheduleTimeout = false; RIL_RadioState newState; RIL_SOCKET_ID soc_id = RIL_SOCKET_1;
#if defined(ANDROID_MULTI_SIM) soc_id = socket_id; #endif
if (s_registerCalled == 0) { // Ignore RIL_onUnsolicitedResponse before RIL_register RLOGW("RIL_onUnsolicitedResponse called before RIL_register"); return; } //得到当前命令的请求码 unsolResponseIndex = unsolResponse - RIL_UNSOL_RESPONSE_BASE;
if ((unsolResponseIndex < 0) || (unsolResponseIndex >= (int32_t)NUM_ELEMS(s_unsolResponses))) { RLOGE("unsupported unsolicited response code %d", unsolResponse); return; }
// Grab a wake lock if needed for this reponse, // as we exit we'll either release it immediately // or set a timer to release it later. //从ril_unsol_commands.h文件中得到命令的类型 //根据不同命令的不同类型进行解析 switch (s_unsolResponses[unsolResponseIndex].wakeType) { case WAKE_PARTIAL: grabPartialWakeLock(); shouldScheduleTimeout = true; break;
case DONT_WAKE: default: // No wake lock is grabed so don't set timeout shouldScheduleTimeout = false; break; }
// Mark the time this was received, doing this // after grabing the wakelock incase getting // the elapsedRealTime might cause us to goto // sleep. if (unsolResponse == RIL_UNSOL_NITZ_TIME_RECEIVED) { timeReceived = elapsedRealtime(); }
appendPrintBuf("[UNSL]< %s", requestToString(unsolResponse));
Parcel p;
p.writeInt32 (RESPONSE_UNSOLICITED); p.writeInt32 (unsolResponse); //调用当前命令的打包函数进行数据打包 ret = s_unsolResponses[unsolResponseIndex] .responseFunction(p, const_cast<void*>(data), datalen); if (ret != 0) { // Problem with the response. Don't continue; goto error_exit; }
// some things get more payload switch(unsolResponse) { case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: newState = processRadioState(CALL_ONSTATEREQUEST(soc_id), soc_id); p.writeInt32(newState); appendPrintBuf("%s {%s}", printBuf, radioStateToString(CALL_ONSTATEREQUEST(soc_id))); break;
case RIL_UNSOL_NITZ_TIME_RECEIVED: // Store the time that this was received so the // handler of this message can account for // the time it takes to arrive and process. In // particular the system has been known to sleep // before this message can be processed. p.writeInt64(timeReceived); break; }
#if VDBG RLOGI("%s UNSOLICITED: %s length:%d", rilSocketIdToString(soc_id), requestToString(unsolResponse), p.dataSize()); #endif //把数据发送到RILJ中 ret = sendResponse(p, soc_id); if (ret != 0 && unsolResponse == RIL_UNSOL_NITZ_TIME_RECEIVED) {
// Unfortunately, NITZ time is not poll/update like everything // else in the system. So, if the upstream client isn't connected, // keep a copy of the last NITZ response (with receive time noted // above) around so we can deliver it when it is connected
if (s_lastNITZTimeData != NULL) { free (s_lastNITZTimeData); s_lastNITZTimeData = NULL; }
s_lastNITZTimeData = malloc(p.dataSize()); s_lastNITZTimeDataSize = p.dataSize(); memcpy(s_lastNITZTimeData, p.data(), p.dataSize()); }
// For now, we automatically go back to sleep after TIMEVAL_WAKE_TIMEOUT // FIXME The java code should handshake here to release wake lock
if (shouldScheduleTimeout) { // Cancel the previous request if (s_last_wake_timeout_info != NULL) { s_last_wake_timeout_info->userParam = (void *)1; }
s_last_wake_timeout_info = internalRequestTimedCallback(wakeTimeoutCallback, NULL, &TIMEVAL_WAKE_TIMEOUT); }
// Normal exit return;
error_exit: if (shouldScheduleTimeout) { releaseWakeLock(); } } |
上面的处理过程分为2步:1、调用当前命令对应的打包函数进行数据打包;2、将数据发送给RILJ。
数据打包的过程涉及到一个数组s_unsolResponses。s_unsolResponses是一个文件,文件中对所有的URC消息都有一个对应的数组相对应,每个数组分3部分:1、命令的请求码;2、命令的打包函数;3、命令的类型;我们要做的就是用当前Modem给出的命令,找到对应的请求码,然后得到相应的打包函数进行数据打包。
而发送的过程就是调用sendResponse把Parcel数据发送给RILJ。
在上面的过程中,用reference中通过AT头转换的命令值与RIL_UNSOL_RESPONSE_BASE相减得到在s_unsolResponses表中对应的命令索引。查找相应的wakeType类型去决定是否计算超时(shouldScheduleTimeout),之后就用s_unsolResponses中的responseFunction去解析命令,最后通过sendResponse将数据发送给RILJ:
上面介绍了URC消息的流程,下面分析一下更普遍的非URC消息的处理流程。
前面说道,非URC消息就是一种回应。当上层通过AT向Modem发送请求后,会一直处于阻塞状态等待回应,一旦readerLoop得到了非URC的消息,就会去唤醒Event端等待的进程。
我们在此主要介绍reference如何唤醒eventLoop端的线程。
非URC消息的上报流程也是从processLine开始的:
static void processLine(const char *line) { pthread_mutex_lock(&s_commandmutex);
if (sp_response == NULL) { /* no command pending */ handleUnsolicited(line); } else if (isFinalResponseSuccess(line)) { sp_response->success = 1; handleFinalResponse(line);//这里 } else if (isFinalResponseError(line)) { sp_response->success = 0; handleFinalResponse(line); } else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) { // See eg. TS 27.005 4.3 // Commands like AT+CMGS have a "> " prompt writeCtrlZ(s_smsPDU); s_smsPDU = NULL; } else switch (s_type) { case NO_RESULT: handleUnsolicited(line); break; case NUMERIC: if (sp_response->p_intermediates == NULL && isdigit(line[0]) ) { addIntermediate(line); } else { /* either we already have an intermediate response or the line doesn't begin with a digit */ handleUnsolicited(line); } break; case SINGLELINE: if (sp_response->p_intermediates == NULL && strStartsWith (line, s_responsePrefix) ) { addIntermediate(line); } else { /* we already have an intermediate response */ handleUnsolicited(line); } break; case MULTILINE: if (strStartsWith (line, s_responsePrefix)) { addIntermediate(line); } else { handleUnsolicited(line); } break;
default: /* this should never be reached */ RLOGE("Unsupported AT command type %d\n", s_type); handleUnsolicited(line); break; }
pthread_mutex_unlock(&s_commandmutex); } |
service ril-daemon /system/bin/rild class main socket rild stream 660 root radio socket rild-debug stream 660 radio system user root group radio cache inet misc audio log |
这里简要介绍以下Modem对于非URC消息的回复格式。消息一般大于2行,前几行是返回值的有效数据,最后一行是作为当前命令结束的标志位。如果是有效数据,那么当前数据就有一个s_type的类型与之相关联(从EventLoop发送给reference时决定)。因此就会在processLine中的switch中进入addIntermediate函数,而这个函数的作用就是把当前的数据放入到反馈数据的sp_response->p_intermediates里面。等到命令的最后,因为是标志位,就会走到processLine的handleFinalResponse中,将数据发送给Event侧。
一个完整的数据流应该包括以下四个步骤:
1、Eventloop接收RILJ的请求,并负责把请求发送给reference库:Eventloop--->reference
2、reference负责把命令转化为AT命令,然后发送给Modem:reference--->Modem
3、reference通过readerLoop得到Modem回应后把数据返回给Eventloop: Modem--->ReaderLoop
4、Eventloop再把数据返回给RILJ:ReaderLoop--->Eventloop
1.3 EventLoop
在讨论EventLoop前,需要对三个list加以分析。基本上EventLoop的所有处理逻辑都是围绕这三个链表进行的。
Timer_list在EventLoop的processTimeouts()函数中进行处理;
Watch_table子EventLoop的processReadReadies(&rfds, n)函数中处理;
Pending_list在EventLoop的firePending(); 函数中处理。
按顺序首先是Timer_list。可以看到processTimeouts将超时的ril_event让入了Pending_list。
static void processTimeouts() { dlog("~~~~ +processTimeouts ~~~~"); MUTEX_ACQUIRE(); struct timeval now; struct ril_event * tev = timer_list.next; struct ril_event * next;
getNow(&now); // walk list, see if now >= ev->timeout for any events
dlog("~~~~ Looking for timers <= %ds + %dus ~~~~", (int)now.tv_sec, (int)now.tv_usec); while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {//遍历Timer_list // Timer expired dlog("~~~~ firing timer ~~~~"); next = tev->next; removeFromList(tev);//移除 addToList(tev, &pending_list);//添加到pending_list tev = next; } MUTEX_RELEASE(); dlog("~~~~ -processTimeouts ~~~~"); } |
接着是检查Watch_table。可以看到也是将活动的fd对应的event放入pending_list。并且将persist为false的event移除Watch_table。
static void processReadReadies(fd_set * rfds, int n) { dlog("~~~~ +processReadReadies (%d) ~~~~", n); MUTEX_ACQUIRE();
for (int i = 0; (i < MAX_FD_EVENTS) && (n > 0); i++) { struct ril_event * rev = watch_table[i]; if (rev != NULL && FD_ISSET(rev->fd, rfds)) {//在fdSet的可读集合中 addToList(rev, &pending_list);//添加到Pending_list if (rev->persist == false) { removeWatch(rev, i); } n--; } }
MUTEX_RELEASE(); dlog("~~~~ -processReadReadies (%d) ~~~~", n); } |
最后在对Pending_list进行统一处理:调用每一个event的func,在startListen中可以看到,这个参数其实是一个函数指针,也就是listencallback。
static void firePending() { dlog("~~~~ +firePending ~~~~"); struct ril_event * ev = pending_list.next; while (ev != &pending_list) { struct ril_event * next = ev->next; removeFromList(ev); ev->func(ev->fd, 0, ev->param); ev = next; } dlog("~~~~ -firePending ~~~~"); } |
然而,如果func如果一直指向listencallback的话,源码就无法继续分析下去了。考虑到这里使用了socket,所以可以从一般的socket IO复用模型去分析。以服务器为例,它需要有一个监听socket(当然可以有多个),假设是socketListen,当这个socketListen监听到有连接请求时,其accpet()函数返回。需要注意的是select()函数不仅仅对accpet()返回的socket进行了fd可读性(或可写性)检测,而且对socketListen也同样进行了检测。当有连接请求时socketListen为可读状态。后面的就不用再描述了。
这里也类似。在startListen()中将监听socket封装成event添加到了watch_table(之后子循环中如果可读会被add进pending_list),并且将此event的回调函数指针func指向listencallback。当RILJ下发命令时(包括connect+write两个步骤)。首先检测到可读的应该是这个socketListen。因而其对应Event->func也就是listencallback被调用。在listencallback中有一个accept()。负责处理RILJ下发命令的connect阶段。Accpet返回一个Socket。这个socket真正负责请求或应答的传输!然而在本次循环中,你并不会找到有类似read()这样的函数调用。因为真正的read()是在下一次eventloop循环!本次循环只是将accpet返回的socket封装成event,添加到watch_table,而这一次添加的clientSocket对应的event->func指向了processCommandsCallback()。下一次循环时此clientsocket可读性被检测到,会将其从wathc_table转入pending_list,因而该回调函数被调用。需要注意的是在设置监听RILJ连接的监听socket对应的event中,persist变量被设置成了false。也就是说RILD这里只监听一次来自RILJ的连接就把这个event移除出了atch_table。当clientsocket断开连接或出错时候,监听event才会再次添加进watch_table监听连接。处理clientsocket的processCommandsCallback如下:
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);
p_rs = p_info->p_rs;
for (;;) { /* loop until EAGAIN/EINTR, end of stream, or other error */ 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 */ 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;
ril_event_del(p_info->commands_event);
record_stream_free(p_rs);
/* start listening for new connections again */
//命令处理完毕,将监听event放回watch_table,再次监听。这里写了管道,导致select返回 //这里本人觉得wakeup完全是多余的操作,此代码已经在eventloop线程中,select肯定没睡 //眠,Android这里可能仅仅是为了统一 rilEventAddWakeup(&s_listen_event);//命令处理完毕,需要再次监听
onCommandsSocketClosed(p_info->socket_id); } } |
接着进入processCommandBuffer
static int processCommandBuffer(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; /* Hook for current context */ /* pendingRequestsMutextHook refer to &s_pendingRequestsMutex */ pthread_mutex_t* pendingRequestsMutexHook = &s_pendingRequestsMutex; /* pendingRequestsHook refer to &s_pendingRequests */ RequestInfo** pendingRequestsHook = &s_pendingRequests;
p.setData((uint8_t *) buffer, buflen);//放入包裹类
// status checked at end status = p.readInt32(&request);//读取request,终于等到你(然而其实之前已经读到p中了) status = p.readInt32 (&token);//读取token
#if (SIM_COUNT >= 2) if (socket_id == RIL_SOCKET_2) { pendingRequestsMutexHook = &s_pendingRequestsMutex_socket2; pendingRequestsHook = &s_pendingRequests_socket2; } #if (SIM_COUNT >= 3) else if (socket_id == RIL_SOCKET_3) { pendingRequestsMutexHook = &s_pendingRequestsMutex_socket3; pendingRequestsHook = &s_pendingRequests_socket3; } #endif #if (SIM_COUNT >= 4) else if (socket_id == RIL_SOCKET_4) { pendingRequestsMutexHook = &s_pendingRequestsMutex_socket4; pendingRequestsHook = &s_pendingRequests_socket4; } #endif #endif
if (status != NO_ERROR) { RLOGE("invalid request block"); return 0; } //若果rquest有问题,可以很直接的做出response if (request < 1 || request >= (int32_t)NUM_ELEMS(s_commands)) { Parcel pErr; RLOGE("unsupported request code %d token %d", request, token); // FIXME this should perhaps return a response pErr.writeInt32 (RESPONSE_SOLICITED); pErr.writeInt32 (token); pErr.writeInt32 (RIL_E_GENERIC_FAILURE);
sendResponse(pErr, socket_id);//错误请求的response return 0; }
//通过了有效性过滤,开始处理 pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo)); //封装请求 pRI->token = token; pRI->pCI = &(s_commands[request]);//注意这里经过了转换,有点类似MFC的消息映射 pRI->socket_id = socket_id;
ret = pthread_mutex_lock(pendingRequestsMutexHook); assert (ret == 0);
pRI->p_next = *pendingRequestsHook; *pendingRequestsHook = pRI;
ret = pthread_mutex_unlock(pendingRequestsMutexHook); assert (ret == 0);
/* sLastDispatchedToken = token; */ //处理请求,p是RILJ与EventLoop的桥梁,pRI是请求信息 //下面的dispatchFunction函数其实是函数指针,查表后才能知道调用的具体函数 pRI->pCI->dispatchFunction(p, pRI);
return 0; } |
根据函数对应表的定义,dispatchXxx被正确调用。而基本上每一个dispatchXxx都会调用s_callbacks.onRequest()。此函数实质上是reference_ril.c种的onRequest()。
static void onRequest (int request, void *data, size_t datalen, RIL_Token t) { ATResponse *p_response; int err;
RLOGD("onRequest: %s", requestToString(request));
/* Ignore all requests except RIL_REQUEST_GET_SIM_STATUS * when RADIO_STATE_UNAVAILABLE. */ if (sState == RADIO_STATE_UNAVAILABLE && request != RIL_REQUEST_GET_SIM_STATUS ) { RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); return; }
/* Ignore all non-power requests when RADIO_STATE_OFF * (except RIL_REQUEST_GET_SIM_STATUS) */ if (sState == RADIO_STATE_OFF && !(request == RIL_REQUEST_RADIO_POWER || request == RIL_REQUEST_GET_SIM_STATUS) ) { RIL_onRequestComplete(t, RIL_E_RADIO_NOT_AVAILABLE, NULL, 0); return; }
switch (request) {//重点就是这句! case RIL_REQUEST_GET_SIM_STATUS: {//后面将以这个case为例 RIL_CardStatus_v6 *p_card_status; char *p_buffer; int buffer_size;
int result = getCardStatus(&p_card_status);//获得卡的状态,里面与Modem交互 if (result == RIL_E_SUCCESS) { p_buffer = (char *)p_card_status; buffer_size = sizeof(*p_card_status); } else { p_buffer = NULL; buffer_size = 0; } RIL_onRequestComplete(t, result, p_buffer, buffer_size); freeCardStatus(p_card_status); break; } case RIL_REQUEST_GET_CURRENT_CALLS: requestGetCurrentCalls(data, datalen, t); break; case RIL_REQUEST_DIAL: requestDial(data, datalen, t); break; case RIL_REQUEST_HANGUP: requestHangup(data, datalen, t); break; case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: // 3GPP 22.030 6.5.5 // "Releases all held calls or sets User Determined User Busy // (UDUB) for a waiting call." at_send_command("AT+CHLD=0", NULL);
/* success or failure is ignored by the upper layer here. it will call GET_CURRENT_CALLS and determine success that way */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); break; case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: // 3GPP 22.030 6.5.5 // "Releases all active calls (if any exist) and accepts // the other (held or waiting) call." at_send_command("AT+CHLD=1", NULL);
/* success or failure is ignored by the upper layer here. it will call GET_CURRENT_CALLS and determine success that way */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); break; case RIL_REQUEST_SWITCH_WAITING_OR_HOLDING_AND_ACTIVE: // 3GPP 22.030 6.5.5 // "Places all active calls (if any exist) on hold and accepts // the other (held or waiting) call." at_send_command("AT+CHLD=2", NULL);
#ifdef WORKAROUND_ERRONEOUS_ANSWER s_expectAnswer = 1; #endif /* WORKAROUND_ERRONEOUS_ANSWER */
/* success or failure is ignored by the upper layer here. it will call GET_CURRENT_CALLS and determine success that way */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); break; case RIL_REQUEST_ANSWER: at_send_command("ATA", NULL);
#ifdef WORKAROUND_ERRONEOUS_ANSWER s_expectAnswer = 1; #endif /* WORKAROUND_ERRONEOUS_ANSWER */
/* success or failure is ignored by the upper layer here. it will call GET_CURRENT_CALLS and determine success that way */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); break; case RIL_REQUEST_CONFERENCE: // 3GPP 22.030 6.5.5 // "Adds a held call to the conversation" at_send_command("AT+CHLD=3", NULL);
/* success or failure is ignored by the upper layer here. it will call GET_CURRENT_CALLS and determine success that way */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); break; case RIL_REQUEST_UDUB: /* user determined user busy */ /* sometimes used: ATH */ at_send_command("ATH", NULL);
/* success or failure is ignored by the upper layer here. it will call GET_CURRENT_CALLS and determine success that way */ RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); break;
case RIL_REQUEST_SEPARATE_CONNECTION: { char cmd[12]; int party = ((int*)data)[0];
// Make sure that party is in a valid range. // (Note: The Telephony middle layer imposes a range of 1 to 7. // It's sufficient for us to just make sure it's single digit.) if (party > 0 && party < 10) { sprintf(cmd, "AT+CHLD=2%d", party); at_send_command(cmd, NULL); RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); } else { RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); } } break;
case RIL_REQUEST_SIGNAL_STRENGTH: requestSignalStrength(data, datalen, t); break; case RIL_REQUEST_VOICE_REGISTRATION_STATE: case RIL_REQUEST_DATA_REGISTRATION_STATE: requestRegistrationState(request, data, datalen, t); break; case RIL_REQUEST_OPERATOR: requestOperator(data, datalen, t); break; case RIL_REQUEST_RADIO_POWER: requestRadioPower(data, datalen, t); break; case RIL_REQUEST_DTMF: { char c = ((char *)data)[0]; char *cmd; asprintf(&cmd, "AT+VTS=%c", (int)c); at_send_command(cmd, NULL); free(cmd); RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); break; } case RIL_REQUEST_SEND_SMS: case RIL_REQUEST_SEND_SMS_EXPECT_MORE: requestSendSMS(data, datalen, t); break; case RIL_REQUEST_CDMA_SEND_SMS: requestCdmaSendSMS(data, datalen, t); break; case RIL_REQUEST_IMS_SEND_SMS: requestImsSendSMS(data, datalen, t); break; case RIL_REQUEST_SETUP_DATA_CALL: requestSetupDataCall(data, datalen, t); break; case RIL_REQUEST_SMS_ACKNOWLEDGE: requestSMSAcknowledge(data, datalen, t); break;
case RIL_REQUEST_GET_IMSI: p_response = NULL; err = at_send_command_numeric("AT+CIMI", &p_response);
if (err < 0 || p_response->success == 0) { RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); } else { RIL_onRequestComplete(t, RIL_E_SUCCESS, p_response->p_intermediates->line, sizeof(char *)); } at_response_free(p_response); break;
case RIL_REQUEST_GET_IMEI: p_response = NULL; err = at_send_command_numeric("AT+CGSN", &p_response);
if (err < 0 || p_response->success == 0) { RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); } else { RIL_onRequestComplete(t, RIL_E_SUCCESS, p_response->p_intermediates->line, sizeof(char *)); } at_response_free(p_response); break;
case RIL_REQUEST_SIM_IO: requestSIM_IO(data,datalen,t); break;
case RIL_REQUEST_SEND_USSD: requestSendUSSD(data, datalen, t); break;
case RIL_REQUEST_CANCEL_USSD: p_response = NULL; err = at_send_command_numeric("AT+CUSD=2", &p_response);
if (err < 0 || p_response->success == 0) { RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); } else { RIL_onRequestComplete(t, RIL_E_SUCCESS, p_response->p_intermediates->line, sizeof(char *)); } at_response_free(p_response); break;
case RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC: at_send_command("AT+COPS=0", NULL); break;
case RIL_REQUEST_DATA_CALL_LIST: requestDataCallList(data, datalen, t); break;
case RIL_REQUEST_QUERY_NETWORK_SELECTION_MODE: requestQueryNetworkSelectionMode(data, datalen, t); break;
case RIL_REQUEST_OEM_HOOK_RAW: // echo back data RIL_onRequestComplete(t, RIL_E_SUCCESS, data, datalen); break;
case RIL_REQUEST_OEM_HOOK_STRINGS: { int i; const char ** cur;
RLOGD("got OEM_HOOK_STRINGS: 0x%8p %lu", data, (long)datalen);
for (i = (datalen / sizeof (char *)), cur = (const char **)data ; i > 0 ; cur++, i --) { RLOGD("> '%s'", *cur); }
// echo back strings RIL_onRequestComplete(t, RIL_E_SUCCESS, data, datalen); break; }
case RIL_REQUEST_WRITE_SMS_TO_SIM: requestWriteSmsToSim(data, datalen, t); break;
case RIL_REQUEST_DELETE_SMS_ON_SIM: { char * cmd; p_response = NULL; asprintf(&cmd, "AT+CMGD=%d", ((int *)data)[0]); err = at_send_command(cmd, &p_response); free(cmd); if (err < 0 || p_response->success == 0) { RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); } else { RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0); } at_response_free(p_response); break; }
case RIL_REQUEST_ENTER_SIM_PIN: case RIL_REQUEST_ENTER_SIM_PUK: case RIL_REQUEST_ENTER_SIM_PIN2: case RIL_REQUEST_ENTER_SIM_PUK2: case RIL_REQUEST_CHANGE_SIM_PIN: case RIL_REQUEST_CHANGE_SIM_PIN2: requestEnterSimPin(data, datalen, t); break;
case RIL_REQUEST_IMS_REGISTRATION_STATE: { int reply[2]; //0==unregistered, 1==registered reply[0] = s_ims_registered;
//to be used when changed to include service supporated info //reply[1] = s_ims_services;
// FORMAT_3GPP(1) vs FORMAT_3GPP2(2); reply[1] = s_ims_format;
RLOGD("IMS_REGISTRATION=%d, format=%d ", reply[0], reply[1]); if (reply[1] != -1) { RIL_onRequestComplete(t, RIL_E_SUCCESS, reply, sizeof(reply)); } else { RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); } break; }
case RIL_REQUEST_VOICE_RADIO_TECH: { int tech = techFromModemType(TECH(sMdmInfo)); if (tech < 0 ) RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0); else RIL_onRequestComplete(t, RIL_E_SUCCESS, &tech, sizeof(tech)); } break; case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: requestSetPreferredNetworkType(request, data, datalen, t); break;
case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: requestGetPreferredNetworkType(request, data, datalen, t); break;
case RIL_REQUEST_GET_CELL_INFO_LIST: requestGetCellInfoList(data, datalen, t); break;
case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: requestSetCellInfoListRate(data, datalen, t); break;
case RIL_REQUEST_GET_HARDWARE_CONFIG: requestGetHardwareConfig(data, datalen, t); break;
case RIL_REQUEST_SHUTDOWN: requestShutdown(t); break;
/* CDMA Specific Requests */ case RIL_REQUEST_BASEBAND_VERSION: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestCdmaBaseBandVersion(request, data, datalen, t); break; } // Fall-through if tech is not cdma
case RIL_REQUEST_DEVICE_IDENTITY: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestCdmaDeviceIdentity(request, data, datalen, t); break; } // Fall-through if tech is not cdma
case RIL_REQUEST_CDMA_SUBSCRIPTION: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestCdmaSubscription(request, data, datalen, t); break; } // Fall-through if tech is not cdma
case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestCdmaSetSubscriptionSource(request, data, datalen, t); break; } // Fall-through if tech is not cdma
case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestCdmaGetSubscriptionSource(request, data, datalen, t); break; } // Fall-through if tech is not cdma
case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestCdmaGetRoamingPreference(request, data, datalen, t); break; } // Fall-through if tech is not cdma
case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestCdmaSetRoamingPreference(request, data, datalen, t); break; } // Fall-through if tech is not cdma
case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE: if (TECH_BIT(sMdmInfo) == MDM_CDMA) { requestExitEmergencyMode(data, datalen, t); break; } // Fall-through if tech is not cdma
default: RLOGD("Request not supported. Tech: %d",TECH(sMdmInfo)); RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0); break; } } |
这里会出现这样的调用栈:
getCardStatus()->getSIMStatus()->at_send_command_singleline()->at_send_command_full()->at_send_command_full_nolock()。这里进入最后的at_send_command_full_nolock()。
static int at_send_command_full_nolock (const char *command, ATCommandType type, const char *responsePrefix, const char *smspdu, long long timeoutMsec, ATResponse **pp_outResponse) { int err = 0; #ifndef USE_NP struct timespec ts; #endif /*USE_NP*/
if(sp_response != NULL) { err = AT_ERROR_COMMAND_PENDING; goto error; }
err = writeline (command);//写入命令,重点其实就这一句
if (err < 0) { goto error; }
s_type = type; s_responsePrefix = responsePrefix; s_smsPDU = smspdu; sp_response = at_response_new();
#ifndef USE_NP if (timeoutMsec != 0) { setTimespecRelative(&ts, timeoutMsec); } #endif /*USE_NP*/
while (sp_response->finalResponse == NULL && s_readerClosed == 0) { if (timeoutMsec != 0) { #ifdef USE_NP err = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, timeoutMsec); #else err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts); #endif /*USE_NP*/ } else { err = pthread_cond_wait(&s_commandcond, &s_commandmutex); }
if (err == ETIMEDOUT) { err = AT_ERROR_TIMEOUT; goto error; } }
if (pp_outResponse == NULL) { at_response_free(sp_response); } else { /* line reader stores intermediate responses in reverse order */ reverseIntermediates(sp_response); *pp_outResponse = sp_response; }
sp_response = NULL;
if(s_readerClosed > 0) { err = AT_ERROR_CHANNEL_CLOSED; goto error; }
err = 0; error: clearPendingCommand();
return err; } |
此时已经完成了从RILJ到Modem的命令下发。在之前的启动分析中,开启了一个readerLoop线程。当Modem处理命令,开始向上上报结果时。
1.4 ReaderLoop
readerLoop线程的启动过程在之前对RIL启动过程的描述中已经介绍过了,它负责接收从Modem上报的消息。
static void *readerLoop(void *arg) { for (;;) { const char * line;
line = readline();
if (line == NULL) { break; }
if(isSMSUnsolicited(line)) { char *line1; const char *line2;
// The scope of string returned by 'readline()' is valid only // till next call to 'readline()' hence making a copy of line // before calling readline again. line1 = strdup(line); line2 = readline();
if (line2 == NULL) { break; }
if (s_unsolHandler != NULL) { s_unsolHandler (line1, line2); } free(line1); } else { processLine(line); } }
onReaderClosed();
return NULL; } |
其实readerLoop的处理逻辑相当好理解:readline,processline。
static void processLine(const char *line) { pthread_mutex_lock(&s_commandmutex);
if (sp_response == NULL) {//启动分析是已经知道此指针可以判别URC与否 /* no command pending */ handleUnsolicited(line);//URC消息处理 } else if (isFinalResponseSuccess(line)) { sp_response->success = 1; handleFinalResponse(line);//非URC消息处理 } else if (isFinalResponseError(line)) { sp_response->success = 0; handleFinalResponse(line); //非URC消息处理 } else if (s_smsPDU != NULL && 0 == strcmp(line, "> ")) { // See eg. TS 27.005 4.3 // Commands like AT+CMGS have a "> " prompt writeCtrlZ(s_smsPDU); s_smsPDU = NULL; } else switch (s_type) { case NO_RESULT: handleUnsolicited(line); break; case NUMERIC: if (sp_response->p_intermediates == NULL && isdigit(line[0]) ) { addIntermediate(line); } else { /* either we already have an intermediate response or the line doesn't begin with a digit */ handleUnsolicited(line); } break; case SINGLELINE: if (sp_response->p_intermediates == NULL && strStartsWith (line, s_responsePrefix) ) { addIntermediate(line); } else { /* we already have an intermediate response */ handleUnsolicited(line); } break; case MULTILINE: if (strStartsWith (line, s_responsePrefix)) { addIntermediate(line); } else { handleUnsolicited(line); } break;
default: /* this should never be reached */ RLOGE("Unsupported AT command type %d\n", s_type); handleUnsolicited(line); break; }
pthread_mutex_unlock(&s_commandmutex); } |
可以看到这里根据sp_response是否为null区分了URC与非URC消息。这里只分析非URC消息,也就是RILJ下发请求命令的应答消息。由handleFinalResponse()处理。
static void handleFinalResponse(const char *line) { //这里是将读取结果复制进了sp_response,它将在EventLoop中被解析 sp_response->finalResponse = strdup(line); //这里讲导致EventLoop线程从onRequest返回,继而执行RIL_onRequestComplete() pthread_cond_signal(&s_commandcond); } |
handleFinalResponse函数并没有做什么处理。仔细查找可以发现每个非URC消息的处理都有一个共同点:在reference_ril.c的onRequest在处理每一个RILRequest时都是先发AT命令等待Modem返回处理结果接着调用RIL_onRequestComplete()。当AT命令被执行并返回结果时,readerLoop首先读取数据,接着发送信号使得eventLoop从等待Modem返回执行结果的状态中结束,读取和解析sp_response并返回到onRequest(),执行后续的是RIL_onRequestComplete()。这其实是一个宏,指向了s_rilenv->OnRequestComplete()。在本博客的《ril框架分析中》的分析中会发现这个s_rilEnv是reference-ril.so调用ril.cpp函数的桥梁。因此这个函数其实就是RIL.cpp的RIL_onRequestComplete()函数。
extern "C" void RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) { RequestInfo *pRI; int ret; int fd = s_ril_param_socket.fdCommand; size_t errorOffset; RIL_SOCKET_ID socket_id = RIL_SOCKET_1;
pRI = (RequestInfo *)t;
if (!checkAndDequeueRequestInfo(pRI)) { RLOGE ("RIL_onRequestComplete: invalid RIL_Token"); return; }
socket_id = pRI->socket_id; #if (SIM_COUNT >= 2) if (socket_id == RIL_SOCKET_2) { fd = s_ril_param_socket2.fdCommand; } #if (SIM_COUNT >= 3) if (socket_id == RIL_SOCKET_3) { fd = s_ril_param_socket3.fdCommand; } #endif #if (SIM_COUNT >= 4) if (socket_id == RIL_SOCKET_4) { fd = s_ril_param_socket4.fdCommand; } #endif #endif #if VDBG RLOGD("RequestComplete, %s", rilSocketIdToString(socket_id)); #endif
if (pRI->local > 0) { // Locally issued command...void only! // response does not go back up the command socket RLOGD("C[locl]< %s", requestToString(pRI->pCI->requestNumber));
goto done; }
appendPrintBuf("[%04d]< %s", pRI->token, requestToString(pRI->pCI->requestNumber));
if (pRI->cancelled == 0) { Parcel p;
p.writeInt32 (RESPONSE_SOLICITED); p.writeInt32 (pRI->token); errorOffset = p.dataPosition();
p.writeInt32 (e);
if (response != NULL) { // there is a response payload, no matter success or not. ret = pRI->pCI->responseFunction(p, response, responselen);//调用responseX
/* if an error occurred, rewind and mark it */ if (ret != 0) { RLOGE ("responseFunction error, ret %d", ret); p.setDataPosition(errorOffset); p.writeInt32 (ret); } }
if (e != RIL_E_SUCCESS) { appendPrintBuf("%s fails by %s", printBuf, failCauseToString(e)); }
if (fd < 0) { RLOGD ("RIL onRequestComplete: Command channel closed"); } sendResponse(p, socket_id);//将P发送到上层RILJ }
done: free(pRI); } |
可以进入sendResponse()看下。
static int sendResponse (Parcel &p, RIL_SOCKET_ID socket_id) { printResponse; return sendResponseRaw(p.data(), p.dataSize(), socket_id); } |
sendResponse()转调了sendResponseRaw()。
static int sendResponseRaw (const void *data, size_t dataSize, RIL_SOCKET_ID socket_id) { int fd = s_ril_param_socket.fdCommand; int ret; uint32_t header; pthread_mutex_t * writeMutexHook = &s_writeMutex;
#if VDBG RLOGE("Send Response to %s", rilSocketIdToString(socket_id)); #endif
#if (SIM_COUNT >= 2) if (socket_id == RIL_SOCKET_2) { fd = s_ril_param_socket2.fdCommand; writeMutexHook = &s_writeMutex_socket2; } #if (SIM_COUNT >= 3) else if (socket_id == RIL_SOCKET_3) { fd = s_ril_param_socket3.fdCommand; writeMutexHook = &s_writeMutex_socket3; } #endif #if (SIM_COUNT >= 4) else if (socket_id == RIL_SOCKET_4) { fd = s_ril_param_socket4.fdCommand; writeMutexHook = &s_writeMutex_socket4; } #endif #endif if (fd < 0) { return -1; }
if (dataSize > MAX_COMMAND_BYTES) { RLOGE("RIL: packet larger than %u (%u)", MAX_COMMAND_BYTES, (unsigned int )dataSize);
return -1; }
pthread_mutex_lock(writeMutexHook);
header = htonl(dataSize);
ret = blockingWrite(fd, (void *)&header, sizeof(header));//这一句才是重点
if (ret < 0) { pthread_mutex_unlock(writeMutexHook); return ret; }
ret = blockingWrite(fd, data, dataSize);
if (ret < 0) { pthread_mutex_unlock(writeMutexHook); return ret; }
pthread_mutex_unlock(writeMutexHook);
return 0; } |
接着进入blockingwrite()。
static int blockingWrite(int fd, const void *buffer, size_t len) { size_t writeOffset = 0; const uint8_t *toWrite;
toWrite = (const uint8_t *)buffer;
while (writeOffset < len) { ssize_t written; do { written = write (fd, toWrite + writeOffset,//终于等到你 len - writeOffset); } while (written < 0 && ((errno == EINTR) || (errno == EAGAIN)));
if (written >= 0) { writeOffset += written; } else { // written < 0 RLOGE ("RIL Response: unexpected error on write errno:%d", errno); close(fd); return -1; } } #if VDBG RLOGE("RIL Response bytes written:%d", writeOffset); #endif return 0; } |
这里通过fd,也就是之前的clientsocket将应答上发到了RILJ。对于URC消息的上发过程基本类似就不再详述了。
1.5 RIL数据流向总结
以下为本人在http://blog.csdn.net/u010961631/article/details/9446377的基础上稍作修改所绘RIL的数据流向图。本文很多内容也是在此博文的基础上修改、提炼、细化,外加自己的揣摩推测而成。
水平有限,本文难免有错误,纰漏。敬请指正。