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

Android 4.1 Netd详细分析(五)代码分析3

2012-11-13 10:25 405 查看
个人邮箱:xiaokeweng@gmail.com

上一篇我们按照函数的调用流程,完成了由NetlinkManager,NetlinkHandler,NetlinkListener,SocketListener组成的,从kernel到framework的单项消息通路。主要是通过内部的socket实现的通信。通过设置socket监听过滤属性,来接收kernel发出的event,(其中kernel发出的event部分不用了解,可以理解为是自发的)。并通过对于公共库的函数SocketListener的继承,函数重写。实现事件分析,并传递给framework层。

接下来我们来实现,从framework层主动下发的以netd为终点的,以操作物理接口基本属性为目的的命令,

接下来回到主函数部分,我们先越过 DnsProxyListener,MDnsSdListener 因为两部分为两个完全独立的部分,他们自身便可满足自己功能上需要的全部上下层通信,而 NetlinkManager 需要与CommandListener 相结合才能实现相关功能,两部分是不可分割的。

接下来,代码到达 cl 部分,该部分主要实习了同 Framework 层建立双向的联系,不但要接受来自Framework 的功能命令,也要向 Framework 回复相应的处理结果和 kernel 状态,向 Framework 层注册可用命令等等。两个层之间同样是通过 socket 通信。

//****  mian.cpp  ****
if (cl->startListener()) {
ALOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}

在 cl 实例化的部分 cl = new CommandListener(); 中通过 FramewoekListener()实现了socket 的创建其中 socket 名字为 netd,使用了有链接的 socket,并通过 registerCmd 实现命令的注册。

//CommandListener::commandListener()
// FrameworkListener("netd", true)
// 使用了TCP 有链接的socket
CommandListener::CommandListener() :
FrameworkListener("netd", true) {
//向framework层注册 操作函数
//全部为CommandListener中的类
registerCmd(new InterfaceCmd());
registerCmd(new IpFwdCmd());
registerCmd(new TetherCmd());
registerCmd(new NatCmd());
registerCmd(new ListTtysCmd());
registerCmd(new PppdCmd());
registerCmd(new PanCmd());
registerCmd(new SoftapCmd());
registerCmd(new BandwidthControlCmd());
registerCmd(new IdletimerControlCmd());
registerCmd(new ResolverCmd());
registerCmd(new RouteCmd());
registerCmd(new RtSolCmd());
……
……


//FrameworkListener::FrameworkListener()
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
SocketListener(socketName, true, withSeq) {
init(socketName, withSeq);
}


//SocketListener::SocketListener
//构造函数
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {
init(socketName, -1, listen, useCmdNum);
}
//socket初始化
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
mListen = listen;
mSocketName = socketName;
mSock = socketFd;
mUseCmdNum = useCmdNum;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection();
}

至此我们又回到了公共库中,如上面代码所示 所示,设定将要将要使用的 socket 名字和基本属性等,这写属性将要作为参数和限定等,将在真正的 socket 创建,链接,通信中使用到。而从上面可见,本次创建的 socket 属性中 mListen 为 TRUE 即为有链接的 socket 通信。

接下来回到main函数中提及到的 startListener 函数,同样的是公共库中的 SocketListener,其中继承关系为Commandlistener → FrameworkListener → socketListener, 接下来的关系与前文的如下所示 startListener 函数基本相同,使用了 fd_set,select
等,但是建立链接与无链接的过程不同,使用到了 connect()和 accept()函数请参考相关资料。在此不做赘述。

//****  SocketListener::startListener()  ****
int SocketListener::startListener() {

if (!mSocketName && mSock == -1) {
SLOGE("Failed to start unbound listener");
errno = EINVAL;
return -1;
} else if (mSocketName) {
if ((mSock = android_get_control_socket(mSocketName)) < 0) {
SLOGE("Obtaining file descriptor socket '%s' failed: %s",
mSocketName, strerror(errno));
return -1;

}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
}

if (mListen && listen(mSock, 4) < 0) {	//有链接(tcp)
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)			//无链接(udp)
mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));

if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}

if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}

return 0;
}

而后同样触发 onDataAvaliable 函数,但此时是在子类 FrameworkListener 中的实现,不同与NetlinkListener 中的 onEvent 在这里使用了 dispatchCommand 函数作处理.

//**** FrameworkListener::onDataAvailable ****
bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[255];
int len;

len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));
if (len < 0) {
SLOGE("read() failed (%s)", strerror(errno));
return false;
} else if (!len)
return false;

int offset = 0;
int i;

for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
/* IMPORTANT: dispatchCommand() expects a zero-terminated string */
dispatchCommand(c, buffer + offset);
offset = i + 1;
}
}
return true;
}

dispathCommand 函数直接按照字符串格式解析,因为命令源为 framework 层的NetworkManagerService 通过调用 NativeDaemonConnector 里面的的 doCommand 函数下发字符串命令,例如 mConnector.doCommand("tether interface add_upstream " + iface);后面会详细介绍命令的源头


void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
FrameworkCommandCollection::iterator i;
int argc = 0;
char *argv[FrameworkListener::CMD_ARGS_MAX];
char tmp[255];
char *p = data;
char *q = tmp;
char *qlimit = tmp + sizeof(tmp) - 1;
bool esc = false;
bool quote = false;
int k;
bool haveCmdNum = !mWithSeq;

memset(argv, 0, sizeof(argv));
memset(tmp, 0, sizeof(tmp));
while(*p) {
if (*p == '\\') {                //if (*p == '\')
if (esc) {
if (q >= qlimit)
goto overflow;
*q++ = '\\';             //*q = *p++
esc = false;
} else
esc = true;
p++;
continue;
} else if (esc) {
if (*p == '"') {             //if (*p == '"')
if (q >= qlimit)
goto overflow;
*q++ = '"';	             //*q = *p++
} else if (*p == '\\') {
if (q >= qlimit)
goto overflow;
*q++ = '\\';             //*q = *p++
} else {
cli->sendMsg(500, "Unsupported escape sequence", false);
goto out;
}
p++;
esc = false;
continue;
}

if (*p == '"') {
if (quote)
quote = false;
else
quote = true;
p++;
continue;
}

if (q >= qlimit)
goto overflow;
*q = *p++;
if (!quote && *q == ' ') {
*q = '\0';
if (!haveCmdNum) {
char *endptr;
int cmdNum = (int)strtol(tmp, &endptr, 0);
if (endptr == NULL || *endptr != '\0') {
cli->sendMsg(500, "Invalid sequence number", false);
goto out;
}
cli->setCmdNum(cmdNum);
haveCmdNum = true;
} else {
if (argc >= CMD_ARGS_MAX)
goto overflow;
argv[argc++] = strdup(tmp);
}
memset(tmp, 0, sizeof(tmp));
q = tmp;
continue;
}
q++;
}

*q = '\0';
if (argc >= CMD_ARGS_MAX)
goto overflow;
argv[argc++] = strdup(tmp);
#if 0
for (k = 0; k < argc; k++) {
SLOGD("arg[%d] = '%s'", k, argv[k]);
}
#endif

if (quote) {
cli->sendMsg(500, "Unclosed quotes error", false);
goto out;
}

if (errorRate && (++mCommandCount % errorRate == 0)) {
/* ignore this command - let the timeout handler handle it */
SLOGE("Faking a timeout");
goto out;
}
//只要是注册进来的命令都会进行调用rumcommand()
for (i = mCommands->begin(); i != mCommands->end(); ++i) {
FrameworkCommand *c = *i;
if (!strcmp(argv[0], c->getCommand())) {
////////////////////////////////////////////////////////////////////////////
if (c->runCommand(cli, argc, argv)) {//    runcommand()	////
////////////////////////////////////////////////////////////////////////////
SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
}
goto out;
}
}

cli->sendMsg(500, "Command not recognized", false);
out:
int j;
for (j = 0; j < argc; j++)
free(argv[j]);
return;

overflow:
LOG_EVENT_INT(78001, cli->getUid());
cli->sendMsg(500, "Command too long", false);
goto out;//错误处理
}

经过解析匹配选择处理后调用 runCommand 函数进行处理,该函数为定义在FrameworkCommand 中的纯虚函数,为子类提供接口,具体的实现在 Commandlistnener 的各个XXXCmd 内部类中,例如下面的一个实例。

//CommandListener::XXXCmd.runCommand()
int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
int argc, char **argv) {
int rc = 0;

if (argc < 2) {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
return 0;
}

if (!strcmp(argv[1], "status")) {
char *tmp = NULL;

asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled"));
cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);
free(tmp);
return 0;
} else if (!strcmp(argv[1], "enable")) {
rc = sTetherCtrl->setIpFwdEnabled(true);
} else if (!strcmp(argv[1], "disable")) {
rc = sTetherCtrl->setIpFwdEnabled(false);
} else {
cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd", false);
return 0;
}

if (!rc) {
cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded", false);
} else {
cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed", true);
}

return 0;
}

从上面代码的例子中可以看到调用了实际处理函数,一般为系统调用,他们将作用于系统,并将处理结果返回给 Framework 层,并且至此我们又回到了 Netd/ 下。具体的命令的执行、实现。如何作用于系统都在 Netd 本地文件中的 XXXController {.cpp | .h}中实现,涉及到 iptables,网络适配文件的读写等等。

至此关于 Netlinkmanager + CommandListener 整体的工作原理和工作流程就介绍完了,关于DnsProxyLis-tener 和 MDnsSdListener 两部分大体的构架相同也同样使用到了 socket 与上下两层进行通信,关于它们的的详细工作流程另做报告。关于以上部分使用到的主要的几大类,绘制了如下的粗略的类图:



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