Personal email: xiaokeweng@gmail.com
In the previous article, we completed a single message path from the kernel to the framework consisting of NetlinkManager, NetlinkHandler, NetlinkListener, and SocketListener according to the function call process. Communication is implemented through an internal socket. You can set the filter attribute of the socket listener to receive the event sent by the kernel,(The event part sent by the kernel does not need to be understood. It can be understood as spontaneous). The function is rewritten by inheriting the SocketListener function of the public library. Implements event analysis and passes it to the framework layer.
Next we will implement the commands that take the netd as the destination and operate the basic attributes of the physical interface as the destination actively issued by the framework layer,
Next, let's go back to the main function section. We first go over the DnsProxyListener. Because the MDnsSdListener is two completely independent parts, they can satisfy all the upper-and lower-layer communication needs on their own functions, netlinkManager must be combined with CommandListener to implement relevant functions. The two parts are inseparable.
Next, the Code arrives at the cl part. This part is mainly used to establish two-way connections with the Framework layer, not only to accept functional commands from the Framework, you also need to reply the corresponding processing result and kernel Status to the Framework, and register available commands with the Framework layer. The two layers also communicate through socket.
//**** mian.cpp **** if (cl->startListener()) { ALOGE("Unable to start CommandListener (%s)", strerror(errno)); exit(1); }
In cl instantiation, cl = new CommandListener (); uses FramewoekListener () to create a socket. The socket name is netd, and the connected socket is used, you can use registerCmd to register commands.
// CommandListener: commandListener () // FrameworkListener ("netd", true) // uses the socket listener: CommandListener (): FrameworkListener ("netd ", true) {// register the operation function with the framework layer // all are the class registerCmd (new InterfaceCmd () in CommandListener; 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 // constructor SocketListener: SocketListener (const char * socketName, bool listen, bool use1_num) {init (socketName,-1, listen, use1_num );} // socket initialize void SocketListener: init (const char * socketName, int socketFd, bool listen, bool use1_num) {mListen = listen; mSocketName = socketName; mSock = socketFd; musesponnum = userentnum; pthread_mutex_init (& mClientsLock, NULL); mClients = new SocketClientCollection ();}
Now we have returned to the public database, as shown in the code above, set the socket name and basic attribute to be used, and this write attribute will be used as a parameter and limitation, it will be used in real socket creation, connection, and communication. As can be seen from the above, in the socket attribute created this time, if mListen is TRUE, it indicates a socket communication with a link.
Next, return to the startListener function mentioned in the main function, which is also the SocketListener in the Public Library. The inheritance relationship isCommandlistener → FrameworkListener → socketListenerThe following relationship is basically the same as the startListener function shown in the previous article. fd_set, select
But the process of establishing a link is different from that without a link. For the connect () and accept () functions, see related materials. I will not repeat it here.
// ***** 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) {// link (tcp) SLOGE ("Un Able to listen on socket (% s) ", strerror (errno); return-1;} else if (! MListen) // No link (udp) mClients-> push_back (new SocketClient (mSock, false, musew.num); 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 ;}
The onDataAvaliable function is also triggered, but it is implemented in the subclass FrameworkListener. Different from the onEvent in NetlinkListener, The dispatchCommand function is used here for processing.
//**** 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;}
The dispathCommand function is directly parsed according to the string format, because the NetworkManagerService at the framework layer calls the doCommand function in NativeDaemonConnector to issue string commands, such as mConnector. doCommand ("tether interface add_upstream" + iface); the command source will be detailed later
.
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 occurrence num = (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> = pai_args_max) goto overflow; argv [argc ++] = strdup (tmp);} memset (tmp, 0, sizeof (tmp )); q = tmp; continue;} q ++;} * q = '\ 0'; if (argc> = pai_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;} // any registered command will call 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; // handle errors}
After resolution matching and selection, call the runCommand function for processing. This function is a pure virtual function defined in FrameworkCommand and provides interfaces for sub-classes. The specific implementation is in the XXXCmd internal classes of Commandlistnener, for example, the following instance.
//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;}
From the example of the code above, we can see that the actual processing function is called, which is generally a system call. They act on the system and return the processing result to the Framework layer, and now we are back at Netd. The execution and implementation of specific commands. How to act on the system is implemented in XXXController {. cpp |. h} in the local Netd file, involving iptables, network-adapted file read/write, and so on.
Now we have finished introducing the overall working principle and workflow of Netlinkmanager + CommandListener. The general architecture of DnsProxyLis-tener and MDnsSdListener is the same, and the socket is used to communicate with the upper and lower layers, detailed workflows for them are also reported. The following rough class diagram is drawn for the main categories used in the above section: