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 通信。

[cpp] view plaincopy
  1. //****  mian.cpp  ****  
  2.     if (cl->startListener()) {  
  3.         ALOGE("Unable to start CommandListener (%s)", strerror(errno));  
  4.         exit(1);  
  5.     }  

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

[cpp] view plaincopy
  1. //CommandListener::commandListener()  
  2. // FrameworkListener("netd", true)  
  3. // 使用了TCP 有链接的socket  
  4. CommandListener::CommandListener() :  
  5.                  FrameworkListener("netd"true) {  
  6.   //向framework层注册 操作函数  
  7.   //全部为CommandListener中的类  
  8.     registerCmd(new InterfaceCmd());  
  9.     registerCmd(new IpFwdCmd());  
  10.     registerCmd(new TetherCmd());  
  11.     registerCmd(new NatCmd());  
  12.     registerCmd(new ListTtysCmd());  
  13.     registerCmd(new PppdCmd());  
  14.     registerCmd(new PanCmd());  
  15.     registerCmd(new SoftapCmd());  
  16.     registerCmd(new BandwidthControlCmd());  
  17.     registerCmd(new IdletimerControlCmd());  
  18.     registerCmd(new ResolverCmd());  
  19.     registerCmd(new RouteCmd());  
  20.     registerCmd(new RtSolCmd());   
  21. ……  
  22. ……  

 

[cpp] view plaincopy
  1. //FrameworkListener::FrameworkListener()  
  2. FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :  
  3.                             SocketListener(socketName, true, withSeq) {  
  4.     init(socketName, withSeq);  
  5. }  

 

[cpp] view plaincopy
  1. //SocketListener::SocketListener  
  2. //构造函数  
  3. SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {  
  4.     init(socketName, -1, listen, useCmdNum);  
  5. }  
  6. //socket初始化  
  7. void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {  
  8.     mListen = listen;  
  9.     mSocketName = socketName;  
  10.     mSock = socketFd;  
  11.     mUseCmdNum = useCmdNum;  
  12.     pthread_mutex_init(&mClientsLock, NULL);  
  13.     mClients = new SocketClientCollection();  
  14. }  

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

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

[cpp] view plaincopy
  1. //****  SocketListener::startListener()  ****  
  2. int SocketListener::startListener() {  
  3.   
  4.     if (!mSocketName && mSock == -1) {  
  5.         SLOGE("Failed to start unbound listener");  
  6.         errno = EINVAL;  
  7.         return -1;  
  8.     } else if (mSocketName) {  
  9.         if ((mSock = android_get_control_socket(mSocketName)) < 0) {  
  10.             SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
  11.                  mSocketName, strerror(errno));  
  12.             return -1;  
  13.   
  14.         }  
  15.         SLOGV("got mSock = %d for %s", mSock, mSocketName);  
  16.     }  
  17.       
  18.     if (mListen && listen(mSock, 4) < 0) {   //有链接(tcp)  
  19.         SLOGE("Unable to listen on socket (%s)", strerror(errno));  
  20.         return -1;  
  21.     } else if (!mListen)            //无链接(udp)  
  22.         mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));  
  23.   
  24.     if (pipe(mCtrlPipe)) {            
  25.         SLOGE("pipe failed (%s)", strerror(errno));  
  26.         return -1;  
  27.     }  
  28.   
  29.     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  
  30.         SLOGE("pthread_create (%s)", strerror(errno));  
  31.         return -1;  
  32.     }  
  33.   
  34.     return 0;  
  35. }  

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

[cpp] view plaincopy
  1. //**** FrameworkListener::onDataAvailable ****  
  2. bool FrameworkListener::onDataAvailable(SocketClient *c) {  
  3.     char buffer[255];  
  4.     int len;  
  5.   
  6.     len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));  
  7.     if (len < 0) {  
  8.         SLOGE("read() failed (%s)", strerror(errno));  
  9.         return false;  
  10.     } else if (!len)  
  11.         return false;  
  12.   
  13.     int offset = 0;  
  14.     int i;  
  15.   
  16.     for (i = 0; i < len; i++) {  
  17.         if (buffer[i] == '\0') {  
  18.             /* IMPORTANT: dispatchCommand() expects a zero-terminated string */  
  19.             dispatchCommand(c, buffer + offset);  
  20.             offset = i + 1;  
  21.         }  
  22.     }  
  23.     return true;  
  24. }  

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

[cpp] view plaincopy
  1. void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
  2.     FrameworkCommandCollection::iterator i;  
  3.     int argc = 0;  
  4.     char *argv[FrameworkListener::CMD_ARGS_MAX];  
  5.     char tmp[255];  
  6.     char *p = data;  
  7.     char *q = tmp;  
  8.     char *qlimit = tmp + sizeof(tmp) - 1;  
  9.     bool esc = false;  
  10.     bool quote = false;  
  11.     int k;  
  12.     bool haveCmdNum = !mWithSeq;  
  13.   
  14.     memset(argv, 0, sizeof(argv));  
  15.     memset(tmp, 0, sizeof(tmp));  
  16.     while(*p) {  
  17.         if (*p == '\\') {                //if (*p == '\')  
  18.             if (esc) {            
  19.                 if (q >= qlimit)   
  20.                     goto overflow;    
  21.                 *q++ = '\\';             //*q = *p++  
  22.                 esc = false;          
  23.             } else            
  24.                 esc = true;       
  25.             p++;  
  26.             continue;  
  27.         } else if (esc) {  
  28.             if (*p == '"') {             //if (*p == '"')  
  29.                 if (q >= qlimit)  
  30.                     goto overflow;  
  31.                 *q++ = '"';              //*q = *p++  
  32.             } else if (*p == '\\') {      
  33.                 if (q >= qlimit)  
  34.                     goto overflow;  
  35.                 *q++ = '\\';             //*q = *p++  
  36.             } else {  
  37.                 cli->sendMsg(500, "Unsupported escape sequence"false);  
  38.                 goto out;  
  39.             }  
  40.             p++;  
  41.             esc = false;  
  42.             continue;  
  43.         }  
  44.   
  45.         if (*p == '"') {  
  46.             if (quote)  
  47.                 quote = false;  
  48.             else  
  49.                 quote = true;  
  50.             p++;  
  51.             continue;  
  52.         }  
  53.   
  54.         if (q >= qlimit)  
  55.             goto overflow;  
  56.         *q = *p++;  
  57.         if (!quote && *q == ' ') {  
  58.             *q = '\0';  
  59.             if (!haveCmdNum) {  
  60.                 char *endptr;  
  61.                 int cmdNum = (int)strtol(tmp, &endptr, 0);  
  62.                 if (endptr == NULL || *endptr != '\0') {  
  63.                     cli->sendMsg(500, "Invalid sequence number"false);  
  64.                     goto out;  
  65.                 }  
  66.                 cli->setCmdNum(cmdNum);  
  67.                 haveCmdNum = true;  
  68.             } else {  
  69.                 if (argc >= CMD_ARGS_MAX)  
  70.                     goto overflow;  
  71.                 argv[argc++] = strdup(tmp);  
  72.             }  
  73.             memset(tmp, 0, sizeof(tmp));  
  74.             q = tmp;  
  75.             continue;  
  76.         }  
  77.         q++;  
  78.     }  
  79.   
  80.     *q = '\0';  
  81.     if (argc >= CMD_ARGS_MAX)  
  82.         goto overflow;  
  83.     argv[argc++] = strdup(tmp);  
  84. #if 0  
  85.     for (k = 0; k < argc; k++) {  
  86.         SLOGD("arg[%d] = '%s'", k, argv[k]);  
  87.     }  
  88. #endif  
  89.   
  90.     if (quote) {  
  91.         cli->sendMsg(500, "Unclosed quotes error"false);  
  92.         goto out;  
  93.     }  
  94.   
  95.     if (errorRate && (++mCommandCount % errorRate == 0)) {  
  96.         /* ignore this command - let the timeout handler handle it */  
  97.         SLOGE("Faking a timeout");  
  98.         goto out;  
  99.     }  
  100.     //只要是注册进来的命令都会进行调用rumcommand()  
  101.     for (i = mCommands->begin(); i != mCommands->end(); ++i) {  
  102.         FrameworkCommand *c = *i;  
  103.         if (!strcmp(argv[0], c->getCommand())) {  
  104. ////////////////////////////////////////////////////////////////////////////  
  105.             if (c->runCommand(cli, argc, argv)) {//    runcommand()  ////  
  106. ////////////////////////////////////////////////////////////////////////////  
  107.                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
  108.             }  
  109.             goto out;  
  110.         }  
  111.     }  
  112.   
  113.     cli->sendMsg(500, "Command not recognized"false);  
  114. out:  
  115.     int j;  
  116.     for (j = 0; j < argc; j++)  
  117.         free(argv[j]);  
  118.     return;  
  119.   
  120. overflow:  
  121.     LOG_EVENT_INT(78001, cli->getUid());  
  122.     cli->sendMsg(500, "Command too long"false);  
  123.     goto out;//错误处理  
  124. }  

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

[cpp] view plaincopy
  1. //CommandListener::XXXCmd.runCommand()  
  2. int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,  
  3.                                                       int argc, char **argv) {  
  4.     int rc = 0;  
  5.   
  6.     if (argc < 2) {  
  7.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument"false);  
  8.         return 0;  
  9.     }  
  10.   
  11.     if (!strcmp(argv[1], "status")) {  
  12.         char *tmp = NULL;  
  13.   
  14.         asprintf(&tmp, "Forwarding %s", (sTetherCtrl->getIpFwdEnabled() ? "enabled" : "disabled"));  
  15.         cli->sendMsg(ResponseCode::IpFwdStatusResult, tmp, false);  
  16.         free(tmp);  
  17.         return 0;  
  18.     } else if (!strcmp(argv[1], "enable")) {  
  19.         rc = sTetherCtrl->setIpFwdEnabled(true);  
  20.     } else if (!strcmp(argv[1], "disable")) {  
  21.         rc = sTetherCtrl->setIpFwdEnabled(false);  
  22.     } else {  
  23.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown ipfwd cmd"false);  
  24.         return 0;  
  25.     }  
  26.   
  27.     if (!rc) {  
  28.         cli->sendMsg(ResponseCode::CommandOkay, "ipfwd operation succeeded"false);  
  29.     } else {  
  30.         cli->sendMsg(ResponseCode::OperationFailed, "ipfwd operation failed"true);  
  31.     }  
  32.   
  33.     return 0;  
  34. }  

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

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

 


 
 

0 0
原创粉丝点击