点击打开链接

一、基于Rild的通信架构

一般智能手机的硬件架构都是两个处理器: 
一个处理器用来运行操作系统,上面运行应用程序,这个处理器称作Application Processor,简称AP;另一个处理负责和射频无线通信相关的工作,叫Baseband Processor,简称BP。

在Android系统中,Rild运行在AP上,它是AP和BP在软件层上通信的中枢。

目前通过Rild,AP和BP的通信方式可以分为两种: 
第一种是AP发送请求给BP,BP响应并回复AP。此时,BP通过Rild回复的请求被称为solicited Response。 
第二种是BP主动发送信息给AP。在这种情况下,BP通过Rild发送的请求被称为unsolicited Response。

基于Rild进程的整个通信架构,基本上如上图所示。 
从图中我们可以看出: 
1、android框架部分,是通过Phone进程与Rild进程通信的。它们之间的通信方式采用的是socket。 
在前面介绍PhoneApp启动时,我们知道Phone进程中有两个Phone对象。每个Phone对象持有一个socket,与对应的Rild进程通信。因此,我们知道手机中实际上启动了两个Rild进程(双卡手机)。

shell:/ $ ps | grep rild
radio     572   1     113732 14792 hrtimer_na 0000000000 S /system/bin/rild
radio     869   1     109604 13944 hrtimer_na 0000000000 S /system/bin/rildshell:/ $ ps | grep phone
radio     2621  605   2019984 74424 SyS_epoll_ 0000000000 S com.android.phoneshell:/ $ ps | grep init
root      1     0     9648   1712  SyS_epoll_ 0000000000 S /initshell:/ $ ps | grep zygote
root      605   1     2195280 70956 poll_sched 0000000000 S zygote64
root      606   1     1610708 59144 poll_sched 0000000000 S zygote
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

我们通过usb连接手机后,通过adb shell进入终端,通过ps和grep命令,可以得到上述结果。 
明显可以看到一个Phone进程对应者两个Rild进程;同时Rild进程由init进程加载,Phone进程由zygote进程加载。

2、Rild与BP之间并没有直接通信,而是引入了厂商的动态库。 
这种设计应该是为了保证灵活性吧。 
用面向对象的思想来看,我们可以认为Rild是一个接口,定义了AP、BP双向通信时需要使用的最基本的函数。不同的厂商都需要满足这个接口,以提供手机最基本的通信功能。 
至于具体如何实现,是完全独立和自由的。

二、Rild的启动 
在hardware/ril/rild/rild.rc中定义了Rild启动时对应的选项:

service ril-daemon /system/bin/rildclass mainsocket rild stream 660 root radiosocket sap_uim_socket1 stream 660 bluetooth bluetoothsocket rild-debug stream 660 radio systemuser rootgroup radio cache inet misc audio log readproc wakelock
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在Android 7.0之前的版本中,该文件的内容是被定义在init.rc中的。 
到了Android7.0 之后,init.rc文件中的许多内容均被移出,添加到各个进程中。如前面分析Vold进程时,对应的启动文件定义于vold.rc中。 
个人猜测这些文件应该会在编译时,重新集成起来,毕竟在在rild对应的Android.mk中增加了下述字段:

.......
LOCAL_MODULE:= rild
LOCAL_MODULE_TAGS := optional
//新增字段
LOCAL_INIT_RC := rild.rc
.......
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

目前手边没有Android7.0的机器,还不好验证,以后有机会再做尝试。

init进程根据rild.rc文件启动一个Rild进程,还需要根据厂商定义的rc文件启动另一个Rild进程。 
厂商定义的rc文件中,与Rild进程相关的主要内容与rild.rc相似,就是socket名称不同。对于第二个Rild进程,其socket名应该为rild2。

现在我们看看Rild进程的main函数,定义于rild.c中:

int main(int argc, char **argv) {//rilLibPath用于指定动态库的位置const char * rilLibPath = NULL;........//Rild规定动态库必须实现一个叫做Ril_init的函数,这个函数的第一个参数指向结构体RIL_Env//而它的返回值指向结构体RIL_RadioFunctionsconst RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);........const RIL_RadioFunctions *funcs;char libPath[PROPERTY_VALUE_MAX];//解析参数........if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) {strlcat(rild, clientId, MAX_SOCKET_NAME_LENGTH);//注意此处调用了ril.cpp中的函数,保存了Rild进程对应socket的名字,后文还会提到RIL_setRilSocketName(rild);}if (rilLibPath == NULL) {//读取系统属性,LIB_PATH_PROPERTY的值为rild.libpath//原生的属性值定义于build/target/board/generic/system.prop文件中//实际的手机中将会使用厂商指定的system.prop文件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;}}..........//根据动态库位置,利用dlopen打开动态库dlHandle = dlopen(rilLibPath, RTLD_NOW);..........//1、启动EventLoop,事件处理RIL_startEventLoop()//从动态库中的到RIL_Init函数的地址rilInit =(const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");......//2、调用RIL_init函数funcs = rilInit(&s_rilEnv, argc, rilArgv);RLOGD("RIL_Init rilInit completed");//3、注册funcs到Rild中RIL_register(funcs);........
done:RLOGD("RIL_Init starting sleep loop");while (true) {sleep(UINT32_MAX);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

根据Rild的main函数,我们可以看出主要就进行了三件事:启动Event Loop、调用RIL_Init函数和注册库函数。 
接下来我们分别分析一下主要事件对应的流程。

1、 RIL_startEventLoop 
RIL_startEventLoop定义于hardware/ril/libril/ril.cpp中:

extern "C" void
RIL_startEventLoop(void) {/* spin up eventLoop thread and wait for it to get started */s_started = 0;.........//创建工作线程,线程ID存入s_tid_dispatch,对应执行函数为eventLoopint result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);.........//工作线程eventLoop运行后,会设置s_started为1,并触发s_startupCond//这里的等待的目的是保证RIL_startEventLoop返回前,工作线程创建并运行成功while (s_started == 0) {pthread_cond_wait(&s_startupCond, &s_startupMutex);}.........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

我们需要跟进eventLoop函数:

static void *
eventLoop(void *param) {int ret;int filedes[2];//1、初始化内部数据结构ril_event_init();pthread_mutex_lock(&s_startupMutex);//通知RIL_startEventLoop本线程已经创建并成功运行了s_started = 1;pthread_cond_broadcast(&s_startupCond);pthread_mutex_unlock(&s_startupMutex);//创建匿名管道ret = pipe(filedes);........s_fdWakeupRead = filedes[0];s_fdWakeupWrite = filedes[1];//设置读端口为非阻塞的fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);//2、创建一个ril_eventril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);//3、将创建出的ril_event加入到event队列中rilEventAddWakeup (&s_wakeupfd_event);//4、进入事件等待循环中ril_event_loop();.........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

1.1 初始化内部数据结构 
我们先看看ril_event_init函数:

void ril_event_init()
{MUTEX_INIT();FD_ZERO(&readFds);//初始化timer_list,任务插入时按时间排序init_list(&timer_list);//初始化pending_list,保存每次需要执行的任务init_list(&pending_list);//初始化监控表memset(watch_table, 0, sizeof(watch_table));
}static void init_list(struct ril_event * list)
{memset(list, 0, sizeof(struct ril_event));list->next = list;list->prev = list;list->fd = -1;
}//MAX_FD_EVENTS为8
//watchtable将用于保存FD加入到readFDs中的ril_event
static struct ril_event * watch_table[MAX_FD_EVENTS];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

可以看出ril_event_init就是初始化readFds、timer_list、pending_list和watch_table,其中后三种数据结构均是用来存放ril_event的。

根据前文的代码,我们知道Rild的main函数中,通过调用RIL_startEventLoop单独启动了一个线程运行eventLoop,这是一个工作线程。 
这个工作线程就是靠ril_event结构体来描述自己需要执行的任务,并且它将多个任务按时间顺序组织起来,保存在任务队列中。 
ril_event的数据结构如下:

struct ril_event {struct ril_event *next;struct ril_event *prev;int fd;int index;bool persist;struct timeval timeout;ril_event_cb func;void *param;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

如果从设计模式的角度来理解Rild的工作线程,易于看出,这其实是比较典型的命令模式。 
就如同之前博客分析vold进程一样,CommandListener收到数据后,调用对应Command的runCommand方法进行处理。 
此处,工作线程收到ril_event后,加入队列中,当需要处理时,调用ril_event对应的处理函数func。

1.2 创建wakeupfd ril_event 
工作线程完成数据结构的初始化后,创建了第一个ril_event:

........
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,processWakeupCallback, NULL);
........
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
// Initialize an event
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
{dlog("~~~~ ril_event_set %x ~~~~", (unsigned int)ev);memset(ev, 0, sizeof(struct ril_event));ev->fd = fd;ev->index = -1;ev->persist = persist;ev->func = func;ev->param = param;fcntl(fd, F_SETFL, O_NONBLOCK);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

从上面的代码可以看出,创建的第一个ril_event的fd为管道的读端、回调函数为processWakeupCallback,同时persist属性为true。

1.3 将创建出的ril_event加入到event队列中

static void rilEventAddWakeup(struct ril_event *ev) {ril_event_add(ev);triggerEvLoop();
}// Add event to watch list
void ril_event_add(struct ril_event * ev)
{dlog("~~~~ +ril_event_add ~~~~");MUTEX_ACQUIRE();for (int i = 0; i < MAX_FD_EVENTS; i++) {//找到第一个空闲索引加入if (watch_table[i] == NULL) {watch_table[i] = ev;//rilev->index = i;dlog("~~~~ added at %d ~~~~", i);dump_event(ev);//将ril_event对应的fd加入到readFdsFD_SET(ev->fd, &readFds);//select的限制,第一个参数为监听总数+1if (ev->fd >= nfds) nfds = ev->fd+1;dlog("~~~~ nfds = %d ~~~~", nfds);break;}}MUTEX_RELEASE();dlog("~~~~ -ril_event_add ~~~~");
}static void triggerEvLoop() {int ret;//pthread_self返回调用线程的线程ID//这里调用triggerEvLoop的就是eventLoop,因此不进入该分支if (!pthread_equal(pthread_self(), s_tid_dispatch)) {/* trigger event loop to wakeup. No reason to do this,* if we're in the event loop thread */do {//但看代码我们知道,如果其它线程调用rilEventAddWakeup加入ril_event时,就会向pipe的写端写入数据ret = write (s_fdWakeupWrite, " ", 1);} while (ret < 0 && errno == EINTR);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

1.4 进入事件等待循环中 
接下来工作线程进入到事件等待循环中:

void ril_event_loop() {........for (;;) {// make local copy of read fd_setmemcpy(&rfds, &readFds, sizeof(fd_set));//根据timer_list来计算select函数等待的时间,timer_list已经按任务的执行时间排序if (-1 == calcNextTimeout(&tv)) {// no pending timers; block indefinitelydlog("~~~~ no timers; blocking indefinitely ~~~~");ptv = NULL;} else {dlog("~~~~ blocking for %ds + %dus ~~~~", (int)tv.tv_sec, (int)tv.tv_usec);ptv = &tv;}............n = select(nfds, &rfds, NULL, NULL, ptv);//将timer_list中超时的任务加入到pending_list中processTimeouts();//将watchtables中收到的任务加入到pending_list中processReadReadies(&rfds, n);//处理pendinglist中的任务firePending();}
}static int calcNextTimeout(struct timeval * tv)
{struct ril_event * tev = timer_list.next;struct timeval now;//利用clock_gettime获取当前时间getNow(&now);// Sorted list, so calc based on first nodeif (tev == &timer_list) {// no pending timersreturn -1;}if (timercmp(&tev->timeout, &now, >)) {//计算出等待时间timersub(&tev->timeout, &now, tv);} else {// timer already expired.tv->tv_sec = tv->tv_usec = 0;}return 0;
}static void processTimeouts()
{............struct timeval now;struct ril_event * tev = timer_list.next;struct ril_event * next;getNow(&now);............//目前还没提及timer_list,实际上调用ril_timer_add函数时,可以将对时间有要求的ril_event加入到timer_list中,按照超时时间从小到大排列while ((tev != &timer_list) && (timercmp(&now, &tev->timeout, >))) {// Timer expireddlog("~~~~ firing timer ~~~~");next = tev->next;removeFromList(tev);//轮询timerlist表,将timer_list中的任务加入到pending_list中addToList(tev, &pending_list);tev = next;}
}static void processReadReadies(fd_set * rfds, int n)
{..........//前面代码已提过,当调用ril_event_add时,新加入的ril_event将存入watch_tablefor (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)) {addToList(rev, &pending_list);//persist值为false时,才会移除//记得么?在eventLoop调用ril_event_loop前,加入了一个s_wakeupfd_event,其persist值为true,永不移除if (rev->persist == false) {removeWatch(rev, i);}n--;}}..........
}static void firePending() {...........struct ril_event * ev = pending_list.next;while (ev != &pending_list) {struct ril_event * next = ev->next;removeFromList(ev);//执行对对应的执行函数//每次循环时s_wakeupfd_event的执行函数processWakeupCallback都会被调用ev->func(ev->fd, 0, ev->param);ev = next;}..........
}static void processWakeupCallback(int fd, short flags, void *param) {......./* empty our wakeup socket out */do {//当有其它线程调用triggerEvLoop时,会向s_fdWakeupWrite中写入数据//s_wakeupfd_event被触发时,负责清空缓存ret = read(s_fdWakeupRead, &buff, sizeof(buff));} while (ret > 0 || (ret < 0 && errno == EINTR));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112

至此,RIL_startEventLoop的工作介绍完毕,虽然还没有实际开始工作,但它搭建出了整个事件的处理框架。这里涉及的代码比较繁杂,我们还是借助于图形总结一下整个过程: 

1.4.1 整体架构 
如上图所示,Rild的main函数中调用RIL_startEventLoop。在RIL_startEventLoop中创建出工作线程,执行eventLoop函数: 
Step 1、利用ril_event_init函数初始化数据结构,主要包括readFds、timer_list、pending_list和watch_table;

Step 2、创建出一个pipe对象;

Step 3、创建s_wakeupfd_event,该event的fd指定为pipe的读端;这个event将被加入到watch_table,同时pipe的读端将被加入到readFds中;注意这个event的persist属性为true,于是将永远存在于watch_table中;

Step 4、调用ril_event_loop开始监听事件的到来。

先在我们结合图形,举几个例子看看,整个事件处理框架是如何工作的。 
注意到初始时,timer_list为空,因此ril_event_loop中将不限时地等待readFds。

1.4.2 ril_event加入到timer_list 
当其它线程调用ril_timer_add函数(定义于ril_event.cpp中)填加ril_event事件时: 
Step 1、新到来的ril_event将按超时时间,由小到大加入到timer_list中;同时,其它线程一般会调用triggerEvLoop,该函数将会向pipe的写端写入数据。

Step 2、于是,pipe的读端将会收到数据;由于初始时pipe读端已经加入到来readFds,因此ril_event_loop将从等待中唤醒。

Step 3、此时,ril_event_loop将执行timer_list和watch_table中存储的事件。注意到在timer_list中,只有超时的事件才会被处理;在watch_table中,只有对应fd已经存入readFds(此时使用的是拷贝对象)的事件才会被执行。 
注意到初始时加入watch_table的s_wakeupfd_event,永远满足执行条件;因此,每次ril_event_loop被唤醒时,该事件都被添加到pending_list。 
s_wakeupfd_event对应的执行函数,将会清空pipe的buffer。

Step 4、 处理完加入到pending_list中的事件后,ril_event_loop将根据timer_list中事件的超时时间,决定等待readFds的时间。 
如果在等待超时之前,没有其它事件到来,那么ril_event_loop将在等待超时后处理timer_list中的事件;否则,仅会处理新到来的事件,不会处理timer_list事件。

1.4.3 ril_event加入到watch_table 
当其它线程调用ril_event_add函数(定义于ril_event.cpp中)增加ril_event事件时: 
Step 1、当watch_table有空位时,新加入的ril_event将被加入到watch_table中,同时对应的fd被添加到readFds;同时,其它线程可能会调用triggerEvLoop,以唤醒ril_event_loop。

Step 2、 ril_event_loop被唤醒后,并不会执行新加入到watch_table中的ril_event,因为它们的fd才刚被加入到readFds中。 
从代码里我们可以看到,ril_event_loop当次循环处理的是readFds的拷贝对应的数据,因此新加入watch_table的ril_event在下次唤醒时才能够被处理。

Step 3、由于加入ril_event对应的fd被加入到readFds中,因此如果对应的fd写入数据时,也会唤醒ril_event_loop。

至此,RIL_startEventLoop的主要流程介绍完毕,可以看到它的主要工作就是启动工作线程,然后等待事件的添加 
那么接下来我们可以看看下一个Rild中下一个重要操作,即调用RIL_Init函数。

2、 RIL_Init 
RIL_Init定义于动态库中,考虑到厂商的保密性,我们只能分析Android原生的Reference-ril库。 
在Android的原生库中,RIL_Init定义于hardware/ril/reference-ril/reference-ril.c中。

const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{.............s_rilenv = env;//参数处理...........pthread_attr_init (&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);return &s_callbacks;
}/*** Static Variables ***/
static const RIL_RadioFunctions s_callbacks = {RIL_VERSION,//以下皆是函数指针onRequest,currentState,onSupports,onCancel,getVersion
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

从代码上来看RIL_Init函数比较简单,就干了三件事:保存Rild传入的RIL_Env结构体;创建s_tid_mainloop线程,执行函数为mainLoop;返回RIL_RadioFunctions结构体。 
这里需要注意的是:RIL_Env和RIL_RadioFunctions结构体,就是Rild架构中用来隔离通用代码和厂商相关代码的接口。即动态库通过RIL_Env调用Rild中的接口,Rild通过RIL_RadioFunctions调用动态库中的接口。

2.1 通信接口 
我们先看看RIL_RadioFunctions结构体:

//此处略去函数指针的定义
typedef struct {int version;        /* set to RIL_VERSION *///用于向BP提交一个请求RIL_RequestFunc onRequest;//用于查询BP的状态RIL_RadioStateRequest onStateRequest;//用于判断动态库是否支持某人requestCodeRIL_Supports supports;//用于取消一个提交给BP的请求RIL_Cancel onCancel;//查询动态库版本RIL_GetVersion getVersion;
} RIL_RadioFunctions;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这里需要重点关注的函数是onRequest,它被Rild用来向动态库提交一个请求。 
Rild架构采用的是异步请求/处理的通信方式,Rild通过onRequest向动态库提交一个请求,然后返回进行自己的工作;动态库处理这个请求,当处理完请求后,通过回调的方式将结果通知给Rild。

我们再来看看RIL_Env结构体:

struct RIL_Env {//动态库完成一个请求后,通过OnRequestComplete通知处理结果,RIL_Token用于标明是哪个请求的处理结果void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,void *response, size_t responselen);//动态库主动上报时,调用的接口
#if defined(ANDROID_MULTI_SIM)void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id);
#elsevoid (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen);
#endif  //给rild提交一个超时任务void (*RequestTimedCallback) (RIL_TimedCallback callback,void *param, const struct timeval *relativeTime);//对于同步的请求,发送应答消息时,使用该接口,目前没看到使用void (*OnRequestAck) (RIL_Token t);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在RIL_Env的结构体中,主要需要关注的是OnRequestComplete和OnUnsolicitedResponse。

2.2 mainLoop 
动态库的RIL_Init被调用后,将会创建一个工作线程,其运行函数为mainLoop:

static void *
mainLoop(void *param __unused)
{........//为AT模块设置一些回调函数。//对于Reference-Ril库而言,AT模块就是对串口设备通信的封装,用于和BP通信at_set_on_reader_closed(onATReaderClosed);at_set_on_timeout(onATTimeout);for (;;) {fd = -1;while(fd < 0) {//得到串口设备的文件描述符.......}......//打开AT设备,传入回调函数ret = at_open(fd, onUnsolicited);.......//向Rild提交一个超时任务,该任务的处理函数为initializeCallbackRIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);.......//如果AT模块被关闭,则waitForClose返回,但是该线程不会退出,而是从for循环那开始重新执行一次//因此AT模块一旦被关闭,将会重新被打开waitForClose();.......}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

从上面的代码可以看出,mainLoop的工作其实就是初始化并监控AT模块,一但AT模块被关闭,那么mainLoop就要重新打开并初始化它。

2.2.1 at_open

int at_open(int fd, ATUnsolHandler h)
{........pthread_attr_init (&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);.........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在at_open中创建了一个工作线程,运行函数为readLoop:

static void *readerLoop(void *arg)
{for (;;) {const char * line;//从串口设备读取数据line = readline();......if(isSMSUnsolicited(line)) {.......//调用回调函数if (s_unsolHandler != NULL) {s_unsolHandler (line1, line2);}.......} else {//根据line中的数据,调用不同的回调函数processLine(line);}}//如果从for循环退出,则通知mainLoop AT设备关闭onReaderClosed();...........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

从上面的代码,我们知道at_open函数其实就是启动一个工作线程,用于接收AT设备的数据,然后进行处理。

2.2.1 initializeCallback 
调用at_open后,main利用RIL_requestTimedCallback向Rild发送一个超时任务。

#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)
  • 1
  • 1

可以看到RIL_requestTimedCallback是一个宏,实际上还是通过RIL_Env中RequestTimedCallback函数发送超时任务。

我们看看ril.cpp中,该函数的实现:

extern "C" void
RIL_requestTimedCallback (RIL_TimedCallback callback, void *param,const struct timeval *relativeTime) {internalRequestTimedCallback (callback, param, relativeTime);
}static UserCallbackInfo *
internalRequestTimedCallback (RIL_TimedCallback callback, void *param,const struct timeval *relativeTime)
{struct timeval myRelativeTime;UserCallbackInfo *p_info;p_info = (UserCallbackInfo *) calloc(1, sizeof(UserCallbackInfo));.........//回调p_info->p_callback = callback;//参数p_info->userParam = param;if (relativeTime == NULL) {/* treat null parameter as a 0 relative time */memset (&myRelativeTime, 0, sizeof(myRelativeTime));} else {/* FIXME I think event_add's tv param is really const anyway *///时间memcpy (&myRelativeTime, relativeTime, sizeof(myRelativeTime));}//构造ril_eventril_event_set(&(p_info->event), -1, false, userTimerCallback, p_info);//加入到timer_list中ril_timer_add(&(p_info->event), &myRelativeTime);//触发EventLoop处理triggerEvLoop();return p_info;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

当Rild中的eventLoop处理该超时任务时,就会回调Reference-ril库中的initializeCallback:

static void initializeCallback(void *param __unused)
{........//同步radio状态setRadioState (RADIO_STATE_OFF);//不断地尝试发送AT指令给BP并等待回复,以确定AT channel正常at_handshake();//下发一系列的AT指令,完成modem初始化..........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

至此,我们以Reference-ril库为例,分析了RIL_Init函数的基本功能。 
如上图所示,RIL_Init的主要工作包括: 
1、创建一个mainLoop工作线程,该线程用于完成实际的工作。 
2、在mainLoop线程中,通过at_open开启AT模块,同时启动readLoop工作线程。readLoop工作线程负责从AT设备中读取信息,并执行对应的函数调用。 
3、调用at_open后,mainLoop线程向Rild进程发送一个超时事件,该事件被Rild处理后,将调用initializeCallback函数。initializeCallback函数,将完成Modem的初始化工作。 
其实上,mainLoop可以直接进行Modem初始化的工作;这里发送超时事件给Rild,通过回调进行初始化,可能是为了确保RIL_startEventLoop已经执行成功。 
4、mainLoop最后通过waitForClose监控AT模块,一旦AT模块被关闭,mainLoop将重新进行初始化AT模块的工作。

3、RIL_register 
现在我们分析一下Rild的main函数中,最后一个关键函数RIL_register:

extern "C" void
RIL_register (const RIL_RadioFunctions *callbacks) {.........//判断之前是否初始化过if (s_registerCalled > 0) {RLOGE("RIL_register has been called more than once. ""Subsequent call ignored");return;}........./* Initialize socket1 parameters */s_ril_param_socket = {RIL_SOCKET_1,             /* socket_id */-1,                       /* fdListen */-1,                       /* fdCommand */PHONE_PROCESS,            /* processName */&s_commands_event,        /* commands_event */&s_listen_event,          /* listen_event */processCommandsCallback,  /* processCommandsCallback */NULL                      /* p_rs */};............s_registerCalled = 1;............// start listen socket1startListen(RIL_SOCKET_1, &s_ril_param_socket);//代码中的SIM_COUNT宏并未定义,略去下文...........
}typedef struct SocketListenParam {RIL_SOCKET_ID socket_id;int fdListen;int fdCommand;char* processName;struct ril_event* commands_event;struct ril_event* listen_event;void (*processCommandsCallback)(int fd, short flags, void *param);RecordStream *p_rs;RIL_SOCKET_TYPE type;
} SocketListenParam;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

从上面的代码可以看出,RIL_register实际就是初始化监听socket所需的参数,然后开始监听socket是否有数据到来。

在前面已经提到过,Android中会创建出两个Rild进程,每个Rild进程均会调用RIL_register函数。 
虽然s_registerCalled为一个静态变量,但在进程的维度上,它是私有的。因此,每个Rild进程均会成功调用一次RIL_register。

接下来,我们看看startListen函数:

static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) {.........switch(socket_id) {case RIL_SOCKET_1://注意此处的RIL_getRilSocketNamestrncpy(socket_name, RIL_getRilSocketName(), 9);break;..........}//根据socket_name获取对应的文件描述符fdListen = android_get_control_socket(socket_name);..............//使Rild进程变成被动服务进程ret = listen(fdListen, 4);..............socket_listen_p->fdListen = fdListen;/* note: non-persistent so we can accept only one connection at a time *///构造一个非超时任务加,注意persist为false,处理函数为listenCallbackril_event_set (socket_listen_p->listen_event, fdListen, false,listenCallback, socket_listen_p);//加入队列,并triggerrilEventAddWakeup (socket_listen_p->listen_event);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

3.1 RIL_getRilSocketName 
startListen函数中,通过调用RIL_getRilSocketName得到需监听的socket的名称。

static char * RIL_getRilSocketName() {return rild;
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

RIL_getRilSocketName的内容很简单,就是返回变量rild。 
那么rild变量又是何时设置的呢?

对于第一个Rild进程,在ril.cpp中,定义了rild的内容为“rild”。

.........
extern "C"
char rild[MAX_SOCKET_NAME_LENGTH] = SOCKET_NAME_RIL;
........#define SOCKET_NAME_RIL "rild"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

对于第二个Rild进程,在Rild启动后,对应的main函数中,利用RIL_setRilSocketName修改rild的内容:

int main(int argc, char **argv) {........//第二个Rild进程,clientId不为0if (strncmp(clientId, "0", MAX_CLIENT_ID_LENGTH)) {strlcat(rild, clientId, MAX_SOCKET_NAME_LENGTH);RIL_setRilSocketName(rild);}........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
extern "C"
void RIL_setRilSocketName(const char * s) {strncpy(rild, s, MAX_SOCKET_NAME_LENGTH);
}
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

从上面的代码,我们可以看出两个Rild进程确实监听的是不同的socket。

3.2 listenCallback 
利用listen函数将Rild变成监听进程后,start_listen通过ril_event_set构造了一个非超时的任务,并利用rilEventAddWakeup将该任务加入到watch_table中。 
注意到ril_event的fd为待监听的socket,因此当ril_event被加入到watch_table后,该socket对应的fd将被加入到readFds中。

一旦该socket可读(即客户端connect成功),那么eventLoop中的select函数将会返回,执行listenCallback函数。 
实际上,由于该任务的persist属性为false,因此执行完毕后,ril_event将从watch_table中移除,socket对应的fd也将被从readFds中移除。 
这表明,Rild进程不会再监听socket对应的connect请求,只支持一个客户端的连接,仅会调用一次listenCallback函数。

static void listenCallback (int fd, short flags, void *param) {........//保存配置的socket监听参数SocketListenParam *p_info = (SocketListenParam *)param;........//接收客户端连接,并将返回的socket保存起来fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen);//进行权限控制........//设置socket为非阻塞的ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK);........if(NULL == sapSocket) {........p_info->fdCommand = fdCommand;//为socket分配一个接收缓存p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES);p_info->p_rs = p_rs;//构造一个新的非超时任务,此时persist属性为true(1),于是eventLoop将一致select监听socket是否有数据到来//当有数据到来时,将调用processCommandsCallback进行处理ril_event_set (p_info->commands_event, p_info->fdCommand, 1,p_info->processCommandsCallback, p_info);rilEventAddWakeup (p_info->commands_event);//向客户端发送主动上报信息,即向RIL.java上报信息onNewCommandConnect(p_info->socket_id);} else {..........}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

至此,RIL_register的主要工作介绍完毕。从上述分析,我们可以看出RIL_register其实就是创建出与RIL.Java通信的服务端socket,然后监听客户端请求。一旦监听到客户端请求后,利用accept分配出对应的通信用socket。然后,再监听该分配出的socket,以处理客户端发来的数据。

4、 Rild main函数总结 
现在我们已经分析完Rild main函数的主要流程了,回过头来看看Rild整体的设计思路: 

1、利用RIL_startEventLoop,初始化通信框架。不论是初始化AT设备,还是接收来自RIL.java的请求,都依赖于Rild进程的通信架构,因此在main函数的最开始,对通信框架进行了初始化。 
2、利用RIL_Init开启AT设备,并完成modem的初始化。AP侧利用RIL.java下发指令,最终还是需要利用AT传给modem来执行。因此,在通信框架初始化完毕后,首先就要完成AT和modem的配置。 
3、利用RIL_register将Rild进程变成服务进程,等待RIL.java中socket的连接;连接成功后,开始处理来自RIL.java的数据。

三、实例分析 
我们已经分析了Rild进程的工作,现在来结合数据业务拨号,看看实际过程中,Rild的工作情况。

1、RIL.java 
首先,在之前的博客中,介绍数据业务基础类的创建时,我们提到过RIL.java在PhoneFactory的makeDefaultPhone中创建:

public static void makeDefaultPhone(Context context) {.......for (int i = 0; i < numPhones; i++) {........sCommandsInterfaces[i] = new RIL(context, networkModes[i],cdmaSubscription, i);}......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们看看RIL的构造函数:

public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {this(context, preferredNetworkType, cdmaSubscription, null);
}public RIL(Context context, int preferredNetworkType,int cdmaSubscription, Integer instanceId) {........mSenderThread = new HandlerThread("RILSender" + mInstanceId);mSenderThread.start();Looper looper = mSenderThread.getLooper();//负责向Rild发送消息mSender = new RILSender(looper);ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {riljLog("Not starting RILReceiver: wifi-only");} else {........//负责接收Rild发送的消息mReceiver = new RILReceiver();mReceiverThread = new Thread(mReceiver, "RILReceiver" + mInstanceId);mReceiverThread.start();........}..........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2、 RILReceiver 
我们看看RILReceiver相关的函数:

class RILReceiver implements Runnable {byte[] buffer;RILReceiver() {buffer = new byte[RIL_MAX_COMMAND_BYTES];}@Overridepublic void run() {int retryCount = 0;String rilSocket = "rild";try {for(;;) {LocalSocket s = null;LocalSocketAddress l;//根据InstanceId决定连接哪个Rild进程的socketif (mInstanceId == null || mInstanceId == 0 ) {rilSocket = SOCKET_NAME_RIL[0];} else {rilSocket = SOCKET_NAME_RIL[mInstanceId];}try {s = new LocalSocket();l = new LocalSocketAddress(rilSocket,LocalSocketAddress.Namespace.RESERVED);//连接rild进程s.connect(l);} catch(IOException ex) {.........}retryCount = 0;//连接Rild进程socket后,保留创建出的socketmSocket = s;try {InputStream is = mSocket.getInputStream();for (;;) {Parcel p;//不断读取到来的数据length = readRilMessage(is, buffer);//解析字节流......//进行处理processResponse(p);......} catch (java.io.IOException ex) {.......} catch (Throwable tr) {.......}//异常断开,执行关闭socket的操作.......}} catch (Throwable tr) {..........}}.........
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

从RILReceiver的代码可以看出,其主要功能就是完成与Rild进程中server socket的连接,然后接收并处理Rild进程发来的数据。

3、 setupDataCall 
之前的博客介绍数据业务拨号流程时,我们知道在DataConnection中,最终将通过调用RIL的setupDataCall函数,将消息发往modem:

@Override
public void setupDataCall(....) {//构造一个request,有唯一的serialNumber,RIL_REQUEST_SETUP_DATA_CALL表明该Request的目的RILRequest rr = RILRequest.obtain(RIL_REQUEST_SETUP_DATA_CALL, result);//将参数写入到RILRequest的Parcel对象中........send(rr);
}private void send(RILRequest rr) {Message msg;//RILReceiver中已经创建出mSocket,同时连接了Rild进程if (mSocket == null) {rr.onError(RADIO_NOT_AVAILABLE, null);rr.release();return;}//构造消息发送给RILSendermsg = mSender.obtainMessage(EVENT_SEND, rr);acquireWakeLock(rr, FOR_WAKELOCK);msg.sendToTarget();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

我们看看RILSender:

class RILSender extends Handler implements Runnable {.......@Override public voidhandleMessage(Message msg) {RILRequest rr = (RILRequest)(msg.obj);RILRequest req = null;switch (msg.what) {case EVENT_SEND:case EVENT_SEND_ACK:try {LocalSocket s;s = mSocket;//将数据打包到data,发往Rild进程.........s.getOutputStream().write(dataLength);s.getOutputStream().write(data);.....catch(IOException ex) {.......} catch (RuntimeException exc) {.......}break;........}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

4、processCommandsCallback 
根据前面对Rild进程的分析,我们知道当Rild进程收到RIL.java中发送来的数据后,将利用processCommandsCallback进行处理:

static void processCommandsCallback(int fd, short flags, void *param) {............for (;;) {/* loop until EAGAIN/EINTR, end of stream, or other error *///record_stream_get_next将socket中接收的数据全部读取到缓冲区//当缓冲区还有数据时,优先解析缓冲区数据;缓冲区无数据时,才从socket再次readret = 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处理processCommandBuffer(p_record, recordlen, p_info->socket_id);}}//错误处理............
}static int
processCommandBuffer(void *buffer, size_t buflen, RIL_SOCKET_ID socket_id) {//判断参数有效性,解析数据等操作..........pRI = (RequestInfo *)calloc(1, sizeof(RequestInfo));.........pRI->token = token;//决定了对应的执行函数pRI->pCI = &(s_commands[request]);pRI->socket_id = socket_id;...........pRI->pCI->dispatchFunction(p, pRI);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

上面的代码中,出现了一个s_commands数组,它保存了一些CommandInfo结构,这个结构封装了Rild对AT指令的处理函数。另外,Rild还定义了一个s_unsolResponses数组,它封装了unsolicited Response对应的一些处理函数。

static CommandInfo s_commands[] = {
#include "ril_commands.h"
};static UnsolResponseInfo s_unsolResponses[] = {
#include "ril_unsol_commands.h"
};typedef struct {//请求号int requestNumber;//请求处理函数void (*dispatchFunction) (Parcel &p, struct RequestInfo *pRI);//结果处理函数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;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

这里我们重点看一下s_commands数组。 
CommandInfo按照requestNumber的先后顺序加入到s_commands中,因此requestNumber就是对应CommandInfo的索引。 
我们看看ril_commands.h:

{0, NULL, NULL},
......
{RIL_REQUEST_SETUP_DATA_CALL, dispatchDataCall, responseSetupDataCall},
......
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

在RIL.java中,指定了拨号对应的请求号为RIL_REQUEST_SETUP_DATA_CALL,因此Rild中对应的处理函数为dispatchDataCall。

5、dispatchDataCall

static void dispatchDataCall(Parcel& p, RequestInfo *pRI) {.........//转换输入参数后处理if (s_callbacks.version < 4 && numParams > numParamsRilV3) {..........dispatchStrings(p2, pRI);} else {.........dispatchStrings(p, pRI);}
}static void
dispatchStrings (Parcel &p, RequestInfo *pRI) {........startRequest;//处理输入参数.........removeLastChar;closeRequest;//CALL_ONREQUEST是个宏,实际上调用s_callbacks的onRequest函数//s_callbacks的类型为RIL_RadioFunctions,在rild.c的main函数中,利用动态库的RIL_Init函数得到;利用RIL_register函数保存CALL_ONREQUEST(pRI->pCI->requestNumber, pStrings, datalen, pRI, pRI->socket_id);.............
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

6、onRequest 
此处,我们以Reference-ril中的onRequest为例,进行分析:

static void
onRequest (int request, void *data, size_t datalen, RIL_Token t) {//判断当前radio状态,是否能够下发AT指令.......switch (request) {.......case RIL_REQUEST_SETUP_DATA_CALL://下发AT指令,完成拨号requestSetupDataCall(data, datalen, t);break;.......}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

7、RIL_onRequestComplete 
当指令处理完毕后,Reference-ril将调用RIL_onRequestComplete通知RIL.java处理结果。

#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
  • 1
  • 1

可以看到RIL_onRequestComplete是一个宏,实际上调用的是s_rilenv的OnRequestComplete函数。 
在Rild进程的main函数中,调用RIL_Init时传入了s_rilEnv,我们看看ril.cpp中的RIL_onRequestComplete:

extern "C" void
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {........//从参数中,知道请求是从哪个socket发过来的socket_id = pRI->socket_id;fd = findFd(socket_id);........if (pRI->cancelled == 0) {........if (response != NULL) {// there is a response payload, no matter success or not.//调用responseDataCall,在返回结果中加入ip地址等信息ret = pRI->pCI->responseFunction(p, response, responselen);........}........sendResponse(p, socket_id);}........
}static int
sendResponse (Parcel &p, RIL_SOCKET_ID socket_id) {printResponse;//利用write函数,将数据发往RIL.javareturn sendResponseRaw(p.data(), p.dataSize(), socket_id);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

8、processResponse 
在前面介绍RILReceiver时,我们知道RILReceiver与Rild连接成功后,将会一直监听发来的数据,并调用processResponse进行处理。

private void
processResponse (Parcel p) {int type;type = p.readInt();if (type == RESPONSE_UNSOLICITED || type == RESPONSE_UNSOLICITED_ACK_EXP) {//处理主动上报processUnsolicited (p, type);} else if (type == RESPONSE_SOLICITED || type == RESPONSE_SOLICITED_ACK_EXP) {RILRequest rr = processSolicited (p, type);.......} else if (type == RESPONSE_SOLICITED_ACK) {.......}
}private RILRequest
processSolicited (Parcel p, int type) {.......//根据serialNumber,将队列中的记录移除rr = findAndRemoveRequestFromList(serial);.......if (error == 0 || p.dataAvail() > 0) {try {switch (rr.mRequest) {........case RIL_REQUEST_SETUP_DATA_CALL: ret =  responseSetupDataCall(p); break;........}....}.......if (error == 0) {.......if (rr.mResult != null) {AsyncResult.forMessage(rr.mResult, ret, null);//将结果返回给DataConnectionrr.mResult.sendToTarget();}}.......
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

在理解了Rild搭建的通信架构后,分析底层拨号的流程还是比较简单的。

Android7.0 Rild工作流程相关推荐

  1. Android 7.0 Vold工作流程

    一.Vold工作机制 Vold是Volume Daemon的缩写,它是Android平台中外部存储系统的管控中心,是管理和控制Android平台外部存储设备的后台进程.其功能主要包括:SD卡的插拔事件 ...

  2. android Telephony学习 --- 第七篇 android7.0 来电(MT)流程

    我们先看下7.0来电大体流程: Framework modem接收到来电通知消息后,以AT指令的方式上报RIL层,RIL层通过sokcet将消息发送给RILJ, 上报事件ID: RIL_UNSOL_R ...

  3. android 7.0 解锁亮屏,Android7.0亮屏流程分析

    亮屏的本质是改变屏幕的电源状态,经过一系列的调用会来到PowerManagerService中的updatePowerStateLocked() 1.PowerManagerService到Displ ...

  4. Android 7.0 WifiMonitor工作流程分析

    2019独角兽企业重金招聘Python工程师标准>>> 在wifi启动扫描的分析过程中,出现了多次WifiMonitor的操作,在此分析一下这个函数是如何工作的. 在Android的 ...

  5. android7 init,Android7.0 init.rc流程分析

    在http://blog.csdn.net/kc58236582/article/details/52247547这篇博客中,我们分析了init进程的流程,现在我们结合代码主要分析init.rc脚本的 ...

  6. OAuth2.0 工作流程

    重要术语   Authorization Server:授权服务器,能够成功验证资源拥有者和获取授权,并在此之后分发令牌的服务器: Resource Server:资源服务器,存储用户的数据资源,能够 ...

  7. ART工作流程及特性

    首发于我的博客网站(prajna.top) 欢迎大家前去交流,有pdf版本. ART (Android Runtime)是运行于 Android 5.0(API 21)及以上的默认运行时环境,用来替换 ...

  8. Mapreduce工作流程与简介

    最近几天一直在学习关于大数据方面的相关技术,今天学习了MapReduce的工作流程,让我对数据地处理有了新的认识,接下来我分享一下关于MapReduce2.0的工作流程 Mapreduce简介 Had ...

  9. Android7.0 PowerManagerService(2) WakeLock的使用及流程

    作为移动终端,电量是一种稀缺资源,需要尽可能的节省.于是,Android系统在空闲时,会主动进入到休眠状态.  我们知道整个Android系统中运行着很多个进程,因此必须有一种机制能够知道每个进程是否 ...

最新文章

  1. qt5编程入门 第2版_2小时入门SparkSQL编程
  2. Testng 测试框架源码阅读(二)
  3. 给某社区技术写作大赛当评委,我的个人资料
  4. python函数调用外部变量_Python基础
  5. 一文带你彻底搞懂C++中一些常见指针(形如*p)的用法
  6. 【Python】Python实战从入门到精通之七 -- 教你深入理解异常处理
  7. IDEA 快速创建 SpringBoot 项目
  8. 2021年SWPUACM暑假集训day5单调栈算法
  9. java.lang.NoClassDefFoundError: org/apache/juli/logging/LogFactory的解决(转)
  10. 项目管理(PM)简介
  11. bugzilla mysql_Centos 7 搭建Bugzilla5.0.4
  12. Unity3d 游戏汉化之IL注入文本替换--木石世纪
  13. 一篇文章“简单”认识《生成对抗网络》(GAN)
  14. HTML+CSS基础学习笔记1
  15. 多次办理这项公积金业务都涉及到查询信用报告,是否会影响将来申请贷款?
  16. CPU,GPU,NPU的架构差异对比
  17. 猜数字小游戏(JAVA)
  18. if中return语句作用/条件判断中如何退出函数
  19. 面向接口编程:打印机
  20. C++随记之模板(黑马程序员学习笔记)

热门文章

  1. 8位数控分频器的设计_eda设计数控分频器(实现2~16)分频
  2. 039_CSS3边框
  3. 学生兴趣爱好管理系统 c语言,《学生兴趣爱好系统.doc
  4. linux安全狗 nginx,linux 下 safedog 防护 Nginx
  5. java免安装版配置,Tomcat(免安装版)的安装与配置 配置成windows服务
  6. ERC721关于NFT的学习和理解
  7. feign 第一次调用超时_Feign ,3步搞定 HTTP 请求
  8. 计算机语言中display翻译,display的意思在线翻译,解释display中文英文含义,短语词组,音标读音,例句,词源,同义词,反义词【澳典网ODict.Net】...
  9. java取geosever数据,终于搞定了GeoServer的WFS查询
  10. Zabbix的snmp监控