💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
1. 建立对外通信的链路 RIL_register函数将创建两个监听端socket,它们的名字分别是: - “rild”:这个socket用来和Java层的应用通信。这一点与Vold中的MountService类似。 - “rild-debug”:这个socket用来接收测试程序的测试命令。 下面来看RIL_register函数的代码,如下所示: **Ril.cpp** ~~~ extern "C" void RIL_register (constRIL_RadioFunctions *callbacks) { //RIL_RadioFunctions结构体由RefRil库输出 intret; intflags; ......//版本检测 if(s_registerCalled > 0) { return; } //拷贝这个结构体的内容到s_callbacks变量中。 memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions)); s_registerCalled = 1; //Rild定义了一些Command,这里做一个小小的检查 for(int i = 0; i < (int)NUM_ELEMS(s_commands); i++) { assert(i == s_commands[i].requestNumber); } for(int i = 0; i < (int)NUM_ELEMS(s_unsolResponses); i++) { assert(i + RIL_UNSOL_RESPONSE_BASE == s_unsolResponses[i].requestNumber); } ...... //start listen socket #if 0 ...... #else //SOCKET_NAME_RIL的值为“Ril”,这个socket由init进程根据init.rc的配置创建 s_fdListen = android_get_control_socket(SOCKET_NAME_RIL); ...... //监听 ret =listen(s_fdListen, 4); ...... #endif /* 构造一个非超时任务,处理函数是listenCallback。这个任务会保存在监控表中,一旦它的FD 可读就会导致eventLoop的select函数返回。根据前面的介绍可知,listen端的socket 可读表示有客户connect上。由于该任务的persist被设置为false,待listenCallback 处理完后,这个任务就会从监控表中移除。也就是说下一次select的readFDs中将不会有 这个监听socket了,这表明Rild只支持一个客户端的连接。 */ ril_event_set (&s_listen_event, s_fdListen, false, listenCallback, NULL); //触发eventLoop工作 rilEventAddWakeup (&s_listen_event); #if 1 /* Rild为了支持调试,还增加了一个Ril_debug的socket,这个socket专门用于 测试程序发送测试命令 */ s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG); ret =listen(s_fdDebug, 4); ...... //添加一个非超时任务,该任务对应的处理函数是debugCallback,它是专门用来处理测试命令的。 ril_event_set (&s_debug_event, s_fdDebug, true, debugCallback, NULL); rilEventAddWakeup (&s_debug_event); #endif } ~~~ 根据上面的分析,如果有一个客户端connect上Rild,eventLoop就会被触发,并且对应的处理函数listenCallback会被调用,下面就去看看这个函数的实现。 **Ril.cpp** ~~~ static void listenCallback (int fd, short flags,void *param) { intret; interr; intis_phone_socket; RecordStream *p_rs; structsockaddr_un peeraddr; socklen_t socklen = sizeof (peeraddr); structucred creds; socklen_t szCreds = sizeof(creds); structpasswd *pwd = NULL; //接收一个客户端的连接,并将返回的socket保存在s_fdCommand中 s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr,&socklen); ...... errno= 0; is_phone_socket = 0;//权限控制,判断连接的客户端有没有对应的权限。 ......//如果没有对应的权限则中止后面的流程 //设置这个socket为非阻塞,所以后续的send/recv调用都不会阻塞 ret =fcntl(s_fdCommand, F_SETFL, O_NONBLOCK); ...... /* p_rs为RecordStream类型,它内部会分配一个缓冲区来存储客户端发来的数据, 这些都是socket编程常用的做法。 */ p_rs =record_stream_new(s_fdCommand, MAX_COMMAND_BYTES); /* 构造一个新的非超时任务,这样在收到来自客户端的数据后就会由eventLoop调用对应的 处理函数processCommandsCallback了。 */ ril_event_set (&s_commands_event, s_fdCommand, 1, processCommandsCallback, p_rs); rilEventAddWakeup (&s_commands_event); onNewCommandConnect();//作一些后续处理,有兴趣的读者可以看看。 } ~~~ 2. RIL_register小结 RIL_register函数的主要功能是初始化了两个用来和外部进程通信的socket,并且向eventLoop添加了对应的任务。 至此,Rild的main函数就都分析完了。下面对main函数进行总结。