Personal email: xiaokeweng@gmail.com
We first instantiate NetlinkManager according to the execution sequence of the main function code. The following code is used.
If (! (Nm = NetlinkManager: Instance () {// instantiate the object nm ALOGE ("Unable to create NetlinkManager"); exit (1 );}; cl = new CommandListener (); // instantiate the object cl // set the mBroadcaster of nm to cl // so that the nm can broadcast the message to the framework layer nm-> setBroadcaster (SocketListener *) cl); if (nm-> start () {// nm start thread ALOGE ("Unable to start NetlinkManager (% s)", strerror (errno )); exit (1 );}
First, instantiate NetlinkManager. From NetlinkManager. cpp, you can see that the instantiated content is as follows. The mBroadcaster type is SocketListener *.
NetlinkManager *NetlinkManager::Instance() { if (!sInstance) sInstance = new NetlinkManager(); return sInstance;}NetlinkManager::NetlinkManager() { mBroadcaster = NULL;}
Next, instantiate the CommandListener and assign values to the setBroadcaster of nm. Through this step, the nm can be called to the socket of cl for broadcast, it means that the processing result of nm is submitted to the Framework through internal broadcast, which will be detailed later.
Then nm calls its start () method, as shown below. The function creates three listening threads. Depending on the setupSocket parameter, you can limit the kernel module events listened to by the socket and start them respectively.
//// // Netlinkmanager. start () // int NetlinkManager :: start () {// listening thread 1: // family: NETLINK_KOBJECT_UEVENT // group: 0 xffffffff (all) // format: NETLINK_FORMAT_ASCII if (mUeventHandler = setupSocket (& mUeventSock, NETLINK_KOBJECT_UEVENT, 0 xffffffff, NetlinkListener: NETLINK_FORMAT_ASCII) = NULL) {return-1;} // listener thread 2: communication with the kernel module-> route table/family: NETLINK_ROUTE // group: RTMGRP_LINK (RouTeMsg GRouP) // format: custom if (mRouteHandler = setupSocket (& mRouteSock, NETLINK_ROUTE, RTMGRP_LINK, NetlinkListener: callback) = NULL) {return-1;} // listening thread 3: // family: NETLINK_NFLOG // group: NETLINK_FORMAT_BINARY if (mQuotaHandler = setupSocket (& mQuotaSock, NETLINK_NFLOG, NFLOG_QUOTA_GROUP, NetlinkListener: NETLINK_FORMAT_BINARY) = NULL) {ALOGE ("Unable to open quota2 logging socket"); // TODO: return-1 once the emulator gets a new kernel .} return 0 ;}
The following formula uses the setupSocket () function to create a socket, set properties, and start the thread to start listening. The return value of the function is of the Netlinkhandler type.
// Parameter description // sock: returned socket operator // netlinkFamily: Specify to communicate with the write kernel module // groups: multicast group mask // format: create the parameter specified by NetlinkHandler and specify the evt decoding method NetlinkHandler * NetlinkManager: setupSocket (int * sock, int netlinkFamily, int groups, int format) {struct sockaddr_nl nladdr; int sz = 64*1024; int on = 1; memset (& nladdr, 0, sizeof (nladdr); nladdr. nl_family = AF_NETLINK; nladdr. nl_pid = getpid (); nladdr. nl_groups = groups ;// Multicast Group /// create socket // if (* sock = socket (PF_NETLINK, SOCK_DGRAM, netlinkFamily) <0) {ALOGE ("Unable to create netlink socket: % s ", strerror (errno); return NULL;} // recive buffer // buff if (setsockopt (* sock, SOL_SOCKET, SO_RCVBUFFORCE, & sz, sizeof (sz) <0) {ALOGE ("Unable to set uevent socket SO_RCVBUFFORCE option: % s", strerror (errno); close (* sock); return NULL ;} // pass credentials Opt // about credentials (certificate) if (setsockopt (* sock, SOL_SOCKET, SO_PASSCRED, & on, sizeof (on) <0) {SLOGE ("Unable to set uevent socket SO_PASSCRED option: % s", strerror (errno); close (* sock); return NULL;} // bind function, bind the socket to a unique property & Address & port ...... // (Because sockaddr_nl is different, that is, the property when the socket is created) if (bind (* sock, (struct sockaddr *) & nladdr, sizeof (nladdr) <0) {ALOGE ("Unable to bind netlink socket: % s", strerror (errno); close (* sock); return NULL ;}// NetlinkHandler. start () // inheritance link // NetlinkHandler-> NetlinkListener-> SocketListener-> start () NetlinkHandler * handler = new NetlinkHandler (this, * sock, format ); if (handler-> start ()) {// ****************** handler-> start ALOGE ("Unable to start NetlinkHandler: % s ", strerror (errno); close (* sock); return NULL;} return handler ;}
During this period, socket () is also used to create socket, and setsockopt () is used to set basic attributes, buffer, credentials, and bind () functions. Finally, call the start () method of NetlinkHandler. The inheritance relationship of the period, NetlinkHandler
→ NetlinkListener → SocketListener. However, NetlinkListener does not include startListener (). This interface exists only in SocketListener.
As a result, it enters the Socket public library. This part of code is used for many system configurations and provides many interface functions. For example, Vold also uses a structure extremely similar to Netd.
Related path:
/System/core/include/sysutils
/System/core/libsysutils/src
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 ;}int SocketListener: stopListener () {char c = 0; int rc; rc = TEMP_FAILURE_RETRY (write (mCtrlPi Pe [1], & c, 1); if (rc! = 1) {SLOGE ("Error writing to control pipe (% s)", strerror (errno); return-1;} void * ret; if (pthread_join (mThread, & ret) {SLOGE ("Error joining to listener thread (% s)", strerror (errno); return-1;} close (mCtrlPipe [0]); close (mCtrlPipe [1]); mCtrlPipe [0] =-1; mCtrlPipe [1] =-1; if (mSocketName & mSock>-1) {close (mSock ); mSock =-1;} SocketClientCollection: iterator it; for (it = mClient S-> begin (); it! = MClients-> end ();) {delete (* it); it = mClients-> erase (it);} return 0 ;}
Call startListener () as shown above. In the listener Kernel layer, the connection-free (udp) socket is used because this part of socket only involves one-way communication, then call the pthread_create function to create a new thread and pass the this pointer to it to officially start thread listening.
Void * SocketListener: threadStart (void * obj) {SocketListener * me = reinterpret_cast <SocketListener *> (obj); // get upper layer // irrelevant type conversion, get the exact same bit me-> runListener (); pthread_exit (NULL); return NULL ;}
RunListener () is the real processing function. It uses the fd_set structure and the select function to determine whether event is triggered in the kernel-related part of the listener. If yes, the onDataAvailable function is called for processing (as follows ). The main function of this function is to decode, judge, and call the processing function for the event. This function is a pure virtual function in SocketListener. It only provides interfaces based on the specific inheritance relationship and usage.
The socket type (with Link, no link, and socketpair) is used for different processing. Because the current analysis is called through NetlinkListener.
Void SocketListener: runListener () {SocketClientCollection * pendingList = new SocketClientCollection (); while (1) {SocketClientCollection: iterator it; fd_set read_fds; // fd_set int rc = 0; int max =-1; FD_ZERO (& read_fds); // mListener is used to determine whether there is a link (TCP) or no link (UDP) if (mListen) {max = mSock; FD_SET (mSock, & read_fds);} FD_SET (mCtrlPipe [0], & read_fds); if (mCtrlPipe [0]> max) max = mCtrlPipe [0]; // link s Press the ocket into the mClients queue pthread_mutex_lock (& mClientsLock); for (it = mClients-> begin (); it! = MClients-> end (); ++ it) {int fd = (* it)-> getSocket (); FD_SET (fd, & read_fds); if (fd> max) max = fd;} pthread_mutex_unlock (& mClientsLock); SLOGV ("mListen = % d, max = % d, mSocketName = % s", mListen, max, mSocketName ); // select function // return value: the number of bits in fd that meet the condition. if (rc = select (max + 1, & read_fds, NULL, NULL, NULL) <0) {if (errno = EINTR) continue; SLOGE ("select failed (% s) mListen = % d, max = % d", stre Rror (errno), mListen, max); sleep (1); continue; // <0 error} else if (! Rc) // = 0 no data can be returned; // ******************** if (FD_ISSET (mCtrlPipe [0], & read_fds) break; // TCP and the listening port status changes to readable if (mListen & FD_ISSET (mSock, & read_fds) {struct sockaddr addr; socklen_t alen; int c; do {alen = sizeof (addr); c = accept (mSock, & addr, & alen); // The accept function blocks SLOGV ("% s got % d from accept ", mSocketName, c) ;}while (c <0 & errno = EINTR); if (c <0) {SLOGE ("accept failed (% s) ", Strerror (errno); sleep (1); continue;} // Add the connected socket to mClients pthread_mutex_lock (& mClientsLock); mClients-> push_back (new SocketClient (c, true, mUseCmdNum); pthread_mutex_unlock (& mClientsLock);}/* Add all active clients to the pending list first * // Add all to pending pendingList-> clear (); pthread_mutex_lock (& mClientsLock); for (it = mClients-> begin (); it! = MClients-> end (); ++ it) {int fd = (* it)-> getSocket (); if (FD_ISSET (fd, & read_fds )) {pendingList-> push_back (* it) ;}} pthread_mutex_unlock (& mClientsLock);/* Process the pending list, since it is owned by the thread, * there is no need to lock it */while (! PendingList-> empty () {/* Pop the first item from the list */it = pendingList-> begin (); SocketClient * c = * it; pendingList-> erase (it);/* Process it, if false is returned and our sockets are * connection-based, remove and destroy it */if (! OnDataAvailable (c) & mListen) {// After the onDataAvailable function is successfully decoded, // In netlinkListener (UDP) or // In frameworkListener // return TRUE/* Remove the client from our array */SLOGV ("going to zap % d for % s", c-> getSocket (), mSocketName); pthread_mutex_lock (& mClientsLock); for (it = mClients-> begin (); it! = MClients-> end (); ++ it) {if (* it = c) {mClients-> erase (it ); // Delete this SocketClient because the processing has completed break;} pthread_mutex_unlock (& mClientsLock ); /* Remove our reference to the client * // delete its property c-> decRef () ;}// end of while (! PendingList-> empty ()} delete pendingList ;}
So far, the implementation of the specific listening socket from netd/Netlinkmanager to the public library has become a top-down listener, which achieves the establishment of the connection between the Kernel part and the Netd part. OnDataAvaliable (below) in the NetlinkListener called above ). In onDataAvaiable (),
The method decodes the received event in the required format. After successful decoding, The onEvent method will be called for processing. Here, the onEvent function is also a pure virtual function, which only serves as an excuse for specific implementation, netd/NetlinkHandler. cpp.
/// // NetlinnkListener. onDataAvailable () // bool NetlinkListener: onDataAvailable (SocketClient * cli) {int socket = cli-> getSocket (); ssize_t count; uid_t uid =-1; count = TEMP_FAILURE_RETRY (accept (// multicast accepts socket, mBuffer, sizeof (mBuffer ), & uid); if (count <0) {if (uid> 0) LOG_EVENT_INT (65537, uid); SLOGE ("recvmsg failed (% s)", strerror (err No); return false;} // returns true NetlinkEvent * evt = new NetlinkEvent (); if (! Evt-> decode (mBuffer, count, mFormat) {SLOGE ("Error decoding NetlinkEvent");} else {// pure virtual function onEvent (evt);} delete evt; return true ;}
Now, the processing process goes back to the netd part (), and the event from the Kernel is decoded as a string. Then, the netd part is processed, and submit the processing result to the Framework layer. The onEvent function is selected based on different events. The corresponding processing functions are called as follows.
// ************************ // Const int NetlinkEvent :: nlActionUnknown = 0; // const int NetlinkEvent: NlActionAdd = 1; // const int NetlinkEvent: NlActionRemove = 2; // const int NetlinkEvent: NlActionChange = 3; // const int NetlinkEvent: NlActionLinkUp = 4; // const int NetlinkEvent: NlActionLinkDown = 5; //************************************** * ****** void NetlinkHandler:: onEvent (Ne TlinkEvent * evt) {const char * subsys = evt-> getSubsystem (); if (! Subsys) {ALOGW ("No subsystem found in netlink event"); return ;}// mainly processes the net part if (! Strcmp (subsys, "net") {int action = evt-> getAction (); const char * iface = evt-> findParam ("INTERFACE "); if (action = evt-> NlActionAdd) {policyinterfaceadded (iface);} else if (action = evt-> NlActionRemove) {policyinterfaceremoved (iface );} else if (action = evt-> NlActionChange) {evt-> dump (); yyinterfacechanged ("nana", true);} else if (action = evt-> NlActionLinkUp) {policyinterfacelinkchanged (ifa Ce, true);} else if (action = evt-> NlActionLinkDown) {yyinterfacelinkchanged (iface, false);} // end of "net" else if (! Strcmp (subsys, "qlog") {const char * alertName = evt-> findParam ("ALERT_NAME"); const char * iface = evt-> findParam ("INTERFACE "); policyquotalimitreached (alertName, iface);} else if (! Strcmp (subsys, "xt_idletimer") {int action = evt-> getAction (); const char * iface = evt-> findParam ("INTERFACE "); const char * state = evt-> findParam ("STATE"); if (state) yyinterfaceactivity (iface ,! Strcmp ("active", state); # if! LOG_NDEBUG // The uevent is ignored? // Network management is involved. The screen is displayed and the connection is closed. The Wi-Fi connection is maintained ~ Therefore, you also need to capture uevent} else if (strcmp (subsys, "platform") & strcmp (subsys, "backlight ")) {/* It is not a VSYNC or a backlight event */ALOGV ("unexpected event from subsystem % s", subsys); # endif }}
Generally, events sent from the kernel are status reports, check and pull events, and the main purpose is to send feedback to the upper layer. The following calls the sendBroadcast method of getBroadcaster of mNm. This method is mentioned by the main function in the previous article. The purpose of nm to assign values to mBroadcaster using cl is as follows. Send the corresponding operation code (all in the ResponseCode class are operation codes) and a message string to the Framework layer. At this time, cl should have passed
The startlistener method establishes a connection with the Framework layer.
At this point, even if the basic process of capturing the kernel event and giving feedback to the above layer is analyzed, we will understand the kernel-netd-framework half.