Android 4.1 Netd详细分析(五)代码分析3
来源:互联网 发布:长沙淘宝主播招聘 编辑:程序博客网 时间:2024/05/18 02:30
上一篇我们按照函数的调用流程,完成了由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 与上下两层进行通信,关于它们的的详细工作流程另做报告。关于以上部分使用到的主要的几大类,绘制了如下的粗略的类图:
- Android 4.1 Netd详细分析(五)代码分析3
- Android 4.1 Netd详细分析(五)代码分析3
- Android 4.1 Netd详细分析(三)代码分析1
- Android 4.1 Netd详细分析(四)代码分析2
- Android 4.1 Netd详细分析(三)代码分析1
- Android 4.1 Netd详细分析(四)代码分析2
- Android 4.1 Netd 详细分析系列
- Android Netd详细分析(一)概述
- Android 4.1 Netd详细分析(六)DnsProxyListener
- Android 4.1 Netd详细分析(六)DnsProxyListener
- Android 4.1 Netd详细分析(一)概述与应用实例
- Android 4.1 Netd详细分析(二)源文件/模块/基础类统领
- Android 4.1 Netd详细分析(一)概述与应用实例
- Android 4.1 Netd详细分析(二)源文件/模块/基础类统领
- Android 4.1 Netd详细分析(一)概述与应用实例
- android netd守护进程机制 --- netd分析
- Android中的网络管理源码分析--netd
- Android中的网络管理源码分析--netd
- 青春路上,岁月如烟
- 黑马程序员-day14-集合框架(Colection)
- About Me
- C++习题 输入输出--公用继承
- 解决error 1045: Access denied for user: 'root@localhost' (Using password: YES)
- Android 4.1 Netd详细分析(五)代码分析3
- ASP.NET MVC URL重写与优化(初级篇)-使用Global路由表定制URL
- iOS 响应者链
- mysql数据库 c api(一)
- Android 4.1 Netd详细分析(六)DnsProxyListener
- 压缩感知和稀疏信号处理
- 郝萌主之疯狂的蝌蚪
- Android中ViewPager的左右滑动以及轮播功能
- 《python源码剖析》笔记 python环境初始化