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