个人邮箱:[email protected]
上一篇我们按照函数的调用流程,完成了由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 与上下两层进行通信,关于它们的的详细工作流程另做报告。关于以上部分使用到的主要的几大类,绘制了如下的粗略的类图: