您的位置:首页 > 移动开发 > Android开发

非启动界面 Android RIL模块联网

2011-12-28 15:57 218 查看
Android的RIL(Radio Interface Layer)模块提供Android telephony服务和radio硬件之间的一个抽象层。RIL模块的架构如下图所示



最上面的是Android的应用程序,比如拨号、短信息等程序,这些程序调用Application Framework层的telephony API,即可以完成打电话、发短信等操作。

以上都是虚拟机以上层面的,rild和vender ril是linux层的用户程序(库),rild是一个daemon程序,在init.rc中可以看到rild程序的启动

service ril-daemon /system/bin/rild

socket rild stream 660 root radio

socket rild-debug stream 660 radio system

user root

group radio cache inet misc audio

同时注意到rild在启动的时候,还开启了两个以名称做标识的socket,rild和rild-debug,rild socket就是上层的framework和rild程序通信的手段,而rild-debug socket是留给radiooptions等程序调试使用的,后来这个rild-debug的socket起到了很重要的作用。

这次我没有从java端的代码入手,而是直接先开始看rild的代码。rild的代码只有rild.c一个,本身并不长,下面是rild的main函数

int main(int argc, char **argv)
{
const char * rilLibPath = NULL;
char **rilArgv;
void *dlHandle;
const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **);
const RIL_RadioFunctions *funcs;
char libPath[PROPERTY_VALUE_MAX];
unsigned char hasLibArgs = 0;

int i;

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 {
usage(argv[0]);
}
}

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[3];
static char   arg_device[32];
int           done = 0;

#define  REFERENCE_RIL_PATH  "/system/lib/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) {
LOGD("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) {
LOGD("could not read /proc/cmdline:%s", strerror(errno));
close(fd);
goto OpenLib;
}
close(fd);

if (strstr(buffer, "android.qemud=") != NULL)
{
/* 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"
while (1) {
int  fd;

sleep(1);

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;
}
LOGD("could not connect to %s socket: %s",
QEMUD_SOCKET_NAME, strerror(errno));
if (--tries == 0)
break;
}
if (!done) {
LOGE("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;

LOGD("overriding with %s %s", arg_overrides[1], arg_overrides[2]);
}
}
OpenLib:
#endif
switchUser();

dlHandle = dlopen(rilLibPath, RTLD_NOW);

if (dlHandle == NULL) {
fprintf(stderr, "dlopen failed: %s\n", dlerror());
exit(-1);

RIL_startEventLoop();

rilInit = (const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))dlsym(dlHandle, "RIL_Init");

if (rilInit == NULL) {
fprintf(stderr, "RIL_Init not defined or exported in %s\n", rilLibPath);
exit(-1);
}

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);
}

// Make sure there's a reasonable argv[0]
rilArgv[0] = argv[0];

funcs = rilInit(&s_rilEnv, argc, rilArgv);

RIL_register(funcs);

done:

while(1) {
// sleep(UINT32_MAX) seems to return immediately on bionic
sleep(0x00ffffff);
}
}


RIL_开头的结构和函数都是libril中的,我们先不管这些,从上面的main函数看来,rild并不干活,它只是把ril-lib-path的so文件载入,然后调用一下其中的RIL_Init函数,传入一下参数,然后再将RIL_Init得到的结果调用libril中的RIL_register,就进入死循环了。

我们在去看一下RIL_Init和RIL_register都是干什么的呢?

/*include/telephony/ril.h*/
typedef struct {
int version;        /* set to RIL_VERSION */
RIL_RequestFunc onRequest;
RIL_RadioStateRequest onStateRequest;
RIL_Supports supports;
RIL_Cancel onCancel;
RIL_GetVersion getVersion;
} RIL_RadioFunctions;

/*reference-ril/reference-ril.c*/
static void onRequest (int request, void *data, size_t datalen, RIL_Token t);
static RIL_RadioState currentState();
static int onSupports (int requestCode);
static void onCancel (RIL_Token t);
static const char *getVersion();

/*** Static Variables ***/
static const RIL_RadioFunctions s_callbacks = {
RIL_VERSION,
onRequest,
currentState,
onSupports,
onCancel,
getVersion
};

const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{
//blabla...
return &s_callbacks;
}

/*libril/ril.cpp*/
extern "C" void
RIL_register (const RIL_RadioFunctions *callbacks) {
int ret;
int flags;

if (callbacks == NULL || ((callbacks->version != RIL_VERSION)
&& (callbacks->version != 2))) { // Remove when partners upgrade to version 3
LOGE(
"RIL_register: RIL_RadioFunctions * null or invalid version"
" (expected %d)", RIL_VERSION);
return;
}
if (callbacks->version < 3) {
LOGE ("RIL_register: upgrade RIL to version 3 current version=%d", callbacks->version);
}

if (s_registerCalled > 0) {
LOGE("RIL_register has been called more than once. "
"Subsequent call ignored");
return;
}

memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions));

s_registerCalled = 1;

// Little self-check

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);
}

// New rild impl calls RIL_startEventLoop() first
// old standalone impl wants it here.

if (s_started == 0) {
RIL_startEventLoop();
}

// start listen socket

#if 0
ret = socket_local_server (SOCKET_NAME_RIL,
ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);

if (ret < 0) {
LOGE("Unable to bind socket errno:%d", errno);
exit (-1);
}
s_fdListen = ret;

#else
s_fdListen = android_get_control_socket(SOCKET_NAME_RIL);
if (s_fdListen < 0) {
LOGE("Failed to get socket '" SOCKET_NAME_RIL "'");
exit(-1);
}

ret = listen(s_fdListen, 4);

if (ret < 0) {
LOGE("Failed to listen on control socket '%d': %s",
s_fdListen, strerror(errno));
exit(-1);
}
#endif

/* note: non-persistent so we can accept only one connection at a time */
ril_event_set (&s_listen_event, s_fdListen, false,
listenCallback, NULL);

rilEventAddWakeup (&s_listen_event);

#if 1
// start debug interface socket

s_fdDebug = android_get_control_socket(SOCKET_NAME_RIL_DEBUG);
if (s_fdDebug < 0) {
LOGE("Failed to get socket '" SOCKET_NAME_RIL_DEBUG "' errno:%d", errno);
exit(-1);
}

ret = listen(s_fdDebug, 4);

if (ret < 0) {
LOGE("Failed to listen on ril debug socket '%d': %s",
s_fdDebug, strerror(errno));
exit(-1);
}

ril_event_set (&s_debug_event, s_fdDebug, true,
debugCallback, NULL);

rilEventAddWakeup (&s_debug_event);
#endif

}


看到上面三个文件的代码片段,相信大家有个大概的认识了把,vendor ril的RIL_Init函数返回的是真正干活的函数数组的指针,而libril中的RIL_register将这些函数指针存着供自己用,至于什么时候用?在RIL_register中可以看到其尝试获得rild以及rild-debug两个socket的控制socket,于是便可以得知,libril是监听这些socket的信息,然后调用传入的函数指针来处理这些信息,在ril.cpp中随处可见s_callbacks.onRequest的调用,s_callbacks就是callbacks的拷贝。

在ril.cpp的main函数的109行,我们看见这里注册了一个rild socket的listener,是函数listenCallback,这个函数就用来处理向rild发送的命令,
static void listenCallback (int fd, short flags, void *param) {
int ret;
int err;
int is_phone_socket;
RecordStream *p_rs;

struct sockaddr_un peeraddr;
socklen_t socklen = sizeof (peeraddr);

struct ucred creds;
socklen_t szCreds = sizeof(creds);

struct passwd *pwd = NULL;

assert (s_fdCommand < 0);
assert (fd == s_fdListen);

s_fdCommand = accept(s_fdListen, (sockaddr *) &peeraddr, &socklen);

if (s_fdCommand < 0 ) {
LOGE("Error on accept() errno:%d", errno);
/* start listening for new connections again */
rilEventAddWakeup(&s_listen_event);
return;
}

/* check the credential of the other side and only accept socket from
* phone process
*/
errno = 0;
is_phone_socket = 0;

err = getsockopt(s_fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);

if (err == 0 && szCreds > 0) {
errno = 0;
pwd = getpwuid(creds.uid);
if (pwd != NULL) {
if (strcmp(pwd->pw_name, PHONE_PROCESS) == 0) {
is_phone_socket = 1;
} else {
LOGE("RILD can't accept socket from process %s", pwd->pw_name);
}
} else {
LOGE("Error on getpwuid() errno: %d", errno);
}
} else {
LOGD("Error on getsockopt() errno: %d", errno);
}

if ( !is_phone_socket ) {
LOGE("RILD must accept socket from %s", PHONE_PROCESS);

close(s_fdCommand);
s_fdCommand = -1;

onCommandsSocketClosed();

/* start listening for new connections again */
rilEventAddWakeup(&s_listen_event);

return;
}

ret = fcntl(s_fdCommand, F_SETFL, O_NONBLOCK);

if (ret < 0) {
LOGE ("Error setting O_NONBLOCK errno:%d", errno);
}

LOGI("libril: new connection");

p_rs = record_stream_new(s_fdCommand, MAX_COMMAND_BYTES);

ril_event_set (&s_commands_event, s_fdCommand, 1,
processCommandsCallback, p_rs);

rilEventAddWakeup (&s_commands_event);

onNewCommandConnect();
}


但是仔细看这个这个函数的35行到48行,发现rild的socket并不是谁都可以连的,必须是PHONE_PROCESS才可以连接,也就是说如果是其他process通过rild socket发命令libril是不会处理,直接报错的,其实这里应该可以直接注释掉,但是为了尽量不破坏原来的代码,我就没有走这条路。向下稍微看几行就发现了debugCallback函数,在RIL_register中通过ril_event_set
(&s_debug_event, s_fdDebug, true, debugCallback, NULL);将debug socket的处理函数设定成debugCallback,观察debugCallback中的几个处理函数,发下下面几个可以用来联网的函数。
case 5:
LOGI("Debug port: Radio On");
data = 1;
issueLocalRequest(RIL_REQUEST_RADIO_POWER, &data, sizeof(int));
sleep(2);
// Set network selection automatic.
issueLocalRequest(RIL_REQUEST_SET_NETWORK_SELECTION_AUTOMATIC, NULL, 0);
break;
case 6:
LOGI("Debug port: Setup Data Call, Apn :%s\n", args[1]);
actData[0] = args[1];
issueLocalRequest(RIL_REQUEST_SETUP_DATA_CALL, &actData,
sizeof(actData));
break;
case 7:
LOGI("Debug port: Deactivate Data Call");
issueLocalRequest(RIL_REQUEST_DEACTIVATE_DATA_CALL, &deactData,
sizeof(deactData));
break;


Radio on是将radio模块激活,Setup Data Call就是建立数据连接(gprs,td,wcdma之类的),有这两个功能的话就可以保证连上网了。

其实看到debugCallback的处理函数,我感到很眼熟,为什么呢?因为在rild的文件夹中有一个radiooptions.c的程序,这个程序的用法是这样的
static void print_usage() {
perror("Usage: radiooptions [option] [extra_socket_args]\n\
0 - RADIO_RESET, \n\
1 - RADIO_OFF, \n\
2 - UNSOL_NETWORK_STATE_CHANGE, \n\
3 - QXDM_ENABLE, \n\
4 - QXDM_DISABLE, \n\
5 - RADIO_ON, \n\
6 apn- SETUP_PDP apn, \n\
7 - DEACTIVE_PDP, \n\
8 number - DIAL_CALL number, \n\
9 - ANSWER_CALL, \n\
10 - END_CALL \n");
}


这样就正好对应上了,radiooptions程序就是官方用来调试ril模块的一个小程序,debugCallback就是在libril中给这个小程序开的“后门”。

于是事情就好办了,前面的一切工作似乎都是白费的,只要调用几下radiooptions不久好了么?

转自:http://yangyangzhao.blog.163.com/blog/static/175816366201011542451166/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: