Android 4.1 Netd詳細分析(五)程式碼分析3

來源:互聯網
上載者:User

個人郵箱: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 有連結的socketCommandListener::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 與上下兩層進行通訊,關於它們的的詳細工作流程另做報告。關於以上部分使用到的主要的幾大類,繪製了如下的粗略的類圖:

 


 
 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.