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

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

 

Android 4.1 Netd详细分析(五)代码分析3_第1张图片
 
 Android 4.1 Netd详细分析(五)代码分析3_第2张图片

你可能感兴趣的:(Android 4.1 Netd详细分析(五)代码分析3)