看看写写 之 Android上Ethernet和netd那点事

陆陆续续接触Android已经一年有余了,记得最初开始接触的是Honeycomb版本,当时那种青涩啊,那种看到sp<>,看到aidl时的惶恐还记忆犹新。。

算了,快写成抒情文了

我喜欢在PC上跑Android(效果还不是一般的好),最近因为要调试一些网络的东西,所以需要联网

三种主流选择:

1是WIFI

2是mobile

3是Ethernet

Mobile当然没有了,没有选择WIFI是因为X86上需要WIFI设备,还需要热点于是想用Ethernet在网上搜了下Android ethernet的方案,都貌似是froyo上面的,以前的版本貌似还没有ethernet支持,大神们做了很多工作

在新的代码上搜了一遍,居然找到了EthernetDataTracker.java这个文件

(说明一下,笔者所用的代码都是谷歌的原生Android代码,版本为4.1)

http://source.android.com/source/downloading.html

#repo init -u https://android.googlesource.com/platform/manifest -b android-4.1.1_r4

#repo sync

回到正题

有这个文件至少说明谷歌是针对了以太网的,所以可能不需要移植什么的(到底是不是呢?惶恐啊)总得找个切入点

我用鼠标点了下桌面上的默认的google search

看了看后面的Logcat,看到了一个小清新类:GoogleSuggestClient

又发现这个类有个巨牛X的方法:isNetworkConnected()

再到ConnectivityManager.getActiveNetworkInfo()一看又是那套service模式了

getActiveNetworkInfo说明系统可能有很多网络连接,但只能使用一个(要么以太网要么手机网络要么wifi)

继续跟踪到service吧

ConnectivityService.java

[java]  view plain copy
  1. public NetworkInfo getActiveNetworkInfo() {  
  2.     enforceAccessPermission();  
  3.     final int uid = Binder.getCallingUid();  
  4.     return getNetworkInfo(mActiveDefaultNetwork, uid);  
  5. }  
  6.   
  7.   
  8. private NetworkInfo getNetworkInfo(int networkType, int uid) {  
  9.     NetworkInfo info = null;  
  10.     if (isNetworkTypeValid(networkType)) {  
  11.         final NetworkStateTracker tracker = mNetTrackers[networkType];  
  12.         if (tracker != null) {  
  13.             info = getFilteredNetworkInfo(tracker, uid);  
  14.         }  
  15.     }  
  16.     return info;  
  17. }  
这里有一组NetworkStateTracker,想想肯定是一组可用的连接了

还有一个mActiveDefaultNetwork,这一定是默认连接了

一开始打印了下返回去的NetworkInfo,居然是个Null,这也难怪,我一个PC上面WIFI,MOBILE网络都没有嘛

但是我的以太网是好的(用ubuntu可以上网),这说明上面的framework可能和下面没有接上。

想法设法证实这一点吧

入口点当然是这组NetworkStateTracker是在哪初始化的

第一个想到的当然是ConnectivityService的构造了

至于构造又是在哪调用的,应该是大名鼎鼎的SystemServer了


看看构造

ConnectivityService.java

[java]  view plain copy
  1. <span style="white-space:pre">  </span>String[] raStrings = context.getResources().getStringArray(  
  2.                 com.android.internal.R.array.radioAttributes);  
  3.         for (String raString : raStrings) {  
  4.             RadioAttributes r = new RadioAttributes(raString);  
  5.             ......  
  6.             mRadioAttributes[r.mType] = r;  
  7.         }  
  8.   
  9.         String[] naStrings = context.getResources().getStringArray(  
  10.                 com.android.internal.R.array.networkAttributes);  
  11.         for (String naString : naStrings) {  
  12.             try {  
  13.                 NetworkConfig n = new NetworkConfig(naString);  
  14.                 ......  
  15.                 mNetConfigs[n.type] = n;  
  16.                 mNetworksDefined++;  
  17.             } catch(Exception e) {  
  18.                 // ignore it - leave the entry null  
  19.             }  
  20.         }  
[java]  view plain copy
  1. <span style="white-space:pre">  </span>for (int netType : mPriorityList) {  
  2.             switch (mNetConfigs[netType].radio) {  
  3.             case ConnectivityManager.TYPE_WIFI:  
  4.                 mNetTrackers[netType] = new WifiStateTracker(netType,  
  5.                         mNetConfigs[netType].name);  
  6.                 mNetTrackers[netType].startMonitoring(context, mHandler);  
  7.                break;  
  8.             case ConnectivityManager.TYPE_MOBILE:  
  9.                 mNetTrackers[netType] = new MobileDataStateTracker(netType,  
  10.                         mNetConfigs[netType].name);  
  11.                 mNetTrackers[netType].startMonitoring(context, mHandler);  
  12.                 break;  
  13.             ......  
  14.             case ConnectivityManager.TYPE_ETHERNET:  
  15.                 mNetTrackers[netType] = EthernetDataTracker.getInstance();  
  16.                 mNetTrackers[netType].startMonitoring(context, mHandler);  
  17.                 break;  
  18.             default:  
  19.                 loge("Trying to create a DataStateTracker for an unknown radio type " +  
  20.                         mNetConfigs[netType].radio);  
  21.                 continue;  
  22.             }  
  23.             mCurrentLinkProperties[netType] = null;  
  24.             if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {  
  25.                 mNetTrackers[netType].reconnect();  
  26.             }  
  27.         }  
这里先从xml读出来两个属性

com.android.internal.R.array.radioAttributes

com.android.internal.R.array.networkAttributes

分别保存到

mRadioAttributes和mNetConfigs两个数组里面

后面的初始化就靠这两个东西了

找到xml文件

/frameworks/base/core/res/res/values/config.xml

[html]  view plain copy
  1. <string-array translatable="false" name="networkAttributes">  
  2.     <item>"wifi,1,1,1,-1,true"</item>  
  3.     <item>"mobile,0,0,0,-1,true"</item>  
  4.     <item>"mobile_mms,2,0,2,60000,true"</item>  
  5.     <item>"mobile_supl,3,0,2,60000,true"</item>  
  6.     <item>"mobile_hipri,5,0,3,60000,true"</item>  
  7.     <item>"mobile_fota,10,0,2,60000,true"</item>  
  8.     <item>"mobile_ims,11,0,2,60000,true"</item>  
  9.     <item>"mobile_cbs,12,0,2,60000,true"</item>  
  10.     <item>"wifi_p2p,13,1,0,-1,true"</item>  
  11. </string-array>  

[html]  view plain copy
  1. <string-array translatable="false" name="radioAttributes">  
  2.     <item>"1,1"</item>  
  3.     <item>"0,1"</item>  
  4. </string-array>  

看到上面那个没,貌似只有wifi和mobile的定义,简直无视了以太网

赶紧补上,

networkAttributes:

[html]  view plain copy
  1. <span style="white-space:pre">  </span><item>"eth,9,9,4,60000,true"</item>  
之所以这么补两个9是因为后面初始化的时候是这么弄的,后面的4是优先级,再后面两个参数就没这么管了,照着前面写

radioAttributes

[html]  view plain copy
  1. <span style="white-space:pre">  </span><item>"9,1"</item>  

看这个分支:

[java]  view plain copy
  1. case ConnectivityManager.TYPE_ETHERNET:  
  2.     mNetTrackers[netType] = EthernetDataTracker.getInstance();  
  3.     mNetTrackers[netType].startMonitoring(context, mHandler);  
  4.     break;  

继续追踪到startMonitoring

EthernetDataTracker.java

[java]  view plain copy
  1. public void startMonitoring(Context context, Handler target) {  
  2.     mContext = context;  
  3.     mCsHandler = target;  
  4.   
  5.     // register for notifications from NetworkManagement Service  
  6.     IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);  
  7.     mNMService = INetworkManagementService.Stub.asInterface(b);  
  8.   
  9.     mInterfaceObserver = new InterfaceObserver(this);  
  10.   
  11.     sIfaceMatch = context.getResources().getString(  
  12.         com.android.internal.R.string.config_ethernet_iface_regex);  
  13.     try {  
  14.         final String[] ifaces = mNMService.listInterfaces();  
  15.         for (String iface : ifaces) {  
  16.             if (iface.matches(sIfaceMatch)) {  
  17.                 mIface = iface;  
  18.                 mNMService.setInterfaceUp(iface);  
  19.                 InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);  
  20.                 mLinkUp = config.isActive();  
  21.                 if (config != null && mHwAddr == null) {  
  22.                     mHwAddr = config.getHardwareAddress();  
  23.                     if (mHwAddr != null) {  
  24.                         mNetworkInfo.setExtraInfo(mHwAddr);  
  25.                     }  
  26.                 }  
  27.                 reconnect();  
  28.                 break;  
  29.             }  
  30.         }  
  31.     } catch (RemoteException e) {  
  32.         Log.e(TAG, "Could not get list of interfaces " + e);  
  33.     }  
  34. }  

这里先取出一个xml属性

com.android.internal.R.string.config_ethernet_iface_regex

放到sIfaceMatch中,这个一看就是个匹配字符串

这里匹配的是所有eth\\d,查了下JAVA的正则表达式,就是eth0,eth1什么的

mNMService.listInterfaces()

这个什么NMService不是尼玛Service,而是NetworkManagerService

显然有call到一个管网络连接的Service去了


跟踪到NetworkManagerService.java

[java]  view plain copy
  1. public String[] listInterfaces() {  
  2.     mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);  
  3.     try {  
  4.         return NativeDaemonEvent.filterMessageList(  
  5.                 mConnector.executeForList("interface""list"), InterfaceListResult);  
  6.     } catch (NativeDaemonConnectorException e) {  
  7.         throw e.rethrowAsParcelableException();  
  8.     }  
  9. }  

mConnector:NativeDaemonConnector

继续跟踪:

[java]  view plain copy
  1. public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)  
  2.             throws NativeDaemonConnectorException {  
  3.         final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();  
  4.   
  5.         final int sequenceNumber = mSequenceNumber.incrementAndGet();  
  6.         final StringBuilder cmdBuilder =  
  7.                 new StringBuilder(Integer.toString(sequenceNumber)).append(' ');  
  8.         final long startTime = SystemClock.elapsedRealtime();  
  9.   
  10.         makeCommand(cmdBuilder, cmd, args);  
  11.   
  12.         final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */  
  13.   
  14.         synchronized (mDaemonLock) {  
  15.             if (mOutputStream == null) {  
  16.                 throw new NativeDaemonConnectorException("missing output stream");  
  17.             } else {  
  18.                 try {  
  19.                     mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));  
  20.                 } catch (IOException e) {  
  21.                     throw new NativeDaemonConnectorException("problem sending command", e);  
  22.                 }  
  23.             }  
  24.         }  
  25. <span style="white-space:pre">  </span>......  
[java]  view plain copy
  1. }  
参数cmd 就是"list"

参数args就是"interface"

这里把两个字符串组成了一个一定格式的command

最后发送出去:

mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));

mOutputStream是哪来的啊。。命令到底发送到哪里去了??


只好跟踪mOutputStream

[java]  view plain copy
  1. private void listenToSocket() throws IOException {  
  2.     LocalSocket socket = null;  
  3.   
  4.     try {  
  5.         socket = new LocalSocket();  
  6.         LocalSocketAddress address = new LocalSocketAddress(mSocket,  
  7.                 LocalSocketAddress.Namespace.RESERVED);  
  8.   
  9.         socket.connect(address);  
[java]  view plain copy
  1.             InputStream inputStream = socket.getInputStream();  
  2.             synchronized (mDaemonLock) {  
  3.                 mOutputStream = socket.getOutputStream();  
  4.             }  

原来NativeDaemonConnector会去启动一个线程,这个线程首先会call到native去初始化一个socket

android_net_LocalSocketImpl.cpp

[cpp]  view plain copy
  1. static jobject  
  2. socket_create (JNIEnv *env, jobject object, jboolean stream)  
  3. {  
  4.     int ret;  
  5.   
  6.     ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0);  
  7.   
  8.     if (ret < 0) {  
  9.         jniThrowIOException(env, errno);  
  10.         return NULL;  
  11.     }  
  12.   
  13.     return jniCreateFileDescriptor(env,ret);  
  14. }  
有了socket当然会去connect,这里有个address,它的初始化的第一个参数mSocket是一个字符串

这个mSocket是NativeDaemonConnector初始化的时候就有了的

NativeDaemonConnector是NetworkManagerService的小弟

[java]  view plain copy
  1. private NetworkManagementService(Context context) {  
  2.     mContext = context;  
  3.     mConnector = new NativeDaemonConnector(  
  4.             new NetdCallbackReceiver(), "netd"10, NETD_TAG, 160);  
  5.     mThread = new Thread(mConnector, NETD_TAG);  
  6. }  
那个"netd"就是mSocket了

再看看connect函数,烂七八糟的一直会call到JNI去

[cpp]  view plain copy
  1. static void  
  2. socket_connect_local(JNIEnv *env, jobject object,  
  3.                         jobject fileDescriptor, jstring name, jint namespaceId)  
  4. {  
  5.     int ret;  
  6.     const char *nameUtf8;  
  7.     int fd;  
  8.   
  9.     nameUtf8 = env->GetStringUTFChars(name, NULL);  
  10.   
  11.     fd = jniGetFDFromFileDescriptor(env, fileDescriptor);  
  12.   
  13.     ret = socket_local_client_connect(  
  14.                 fd,  
  15.                 nameUtf8,  
  16.                 namespaceId,  
  17.                 SOCK_STREAM);  
  18.   
  19.     env->ReleaseStringUTFChars(name, nameUtf8);  
  20. }  
[cpp]  view plain copy
  1. int socket_local_client_connect(int fd, const char *name, int namespaceId,   
  2.         int type)  
  3. {  
  4.     struct sockaddr_un addr;  
  5.     socklen_t alen;  
  6.     size_t namelen;  
  7.     int err;  
  8.   
  9.   
  10.     err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);  
  11.   
  12.   
  13.     if (err < 0) {  
  14.         goto error;  
  15.     }  
  16.   
  17.   
  18.     if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {  
  19.         goto error;  
  20.     }  
  21.   
  22.   
  23.     return fd;  
  24.   
  25.   
  26. error:  
  27.     return -1;  
  28. }  
[cpp]  view plain copy
  1. int socket_make_sockaddr_un(const char *name, int namespaceId,   
  2.         struct sockaddr_un *p_addr, socklen_t *alen)  
  3. {  
  4.     memset (p_addr, 0, sizeof (*p_addr));  
  5.     size_t namelen;  
  6.     switch (namespaceId) {  
  7.         case ANDROID_SOCKET_NAMESPACE_RESERVED:  
  8.             namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);  
  9.             /* unix_path_max appears to be missing on linux */  
  10.             if (namelen > sizeof(*p_addr)   
  11.                     - offsetof(struct sockaddr_un, sun_path) - 1) {  
  12.                 goto error;  
  13.             }  
  14.             strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);  
  15.             strcat(p_addr->sun_path, name);  
  16.         break;  
  17.   
  18.         default:  
  19.             // invalid namespace id  
  20.             return -1;  
  21.     }  
  22.   
  23.   
  24.     p_addr->sun_family = AF_LOCAL;  
  25.     *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;  
  26.     return 0;  
  27. error:  
  28.     return -1;  
  29. }  

这里使用的是UNIX域的socket,在最后的socket_make_sockaddr_un函数中

参数name便是"netd"

ANDROID_RESERVED_SOCKET_PREFIX是"/dev/socket"

所以合起来的socket地址是"/dev/socket/netd"

之前的送的"list interface"就是送到这个socket去了,同时也发现,这个NativeDaemonConnector也在不停的倾听这个socket,了解那边发生了什么

在这里猜到,“那边”有个东西可以接收我们的命令,然后返回结果

"那边"的这个东西也可能主动向我们汇报一些事件


神奇的那边

我又搜索了一下代码

发现那边就是netd这个守护进程。。


哎 第一次认真写博客,发现还不是一般的累

netd下次再写吧。。搞几把DOTA。。

向所有的原创bloger致敬!


凡是有始有终 继续写吧

netd = net daemon

目的是为了监视网络状况,比如带宽变化,网络设备的增加/移除

netd时候在init执行的时候被启动的

看看init.rc有这么一段:

[cpp]  view plain copy
  1.   

service netd /system/bin/netd

  class main

socket netd stream 0660 root system

socket dnsproxyd stream 0660 root system

socket mdns stream 0660 root system

init程序解释执行这一段时会执行service_start

[cpp]  view plain copy
  1. void service_start(struct service *svc, const char *dynamic_args)  
  2. {  
  3.     ... ...  
  4.   
  5.     NOTICE("starting '%s'\n", svc->name);  
  6.   
  7.     pid = fork();  
  8.   
  9.     if (pid == 0) {  
  10.         struct socketinfo *si;  
  11.         struct svcenvinfo *ei;  
  12.         char tmp[32];  
  13.         int fd, sz;  
  14.   
  15.         umask(077);  
  16.   
  17.         for (si = svc->sockets; si; si = si->next) {  
  18.             int socket_type = (  
  19.                     !strcmp(si->type, "stream") ? SOCK_STREAM :  
  20.                         (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));  
  21.             int s = create_socket(si->name, socket_type,  
  22.                                   si->perm, si->uid, si->gid);  
  23.             if (s >= 0) {  
  24.                 publish_socket(si->name, s);  
  25.             }  
  26.         }  
  27.   
  28.         setpgid(0, getpid());  
  29.   
  30.     /* as requested, set our gid, supplemental gids, and uid */  
  31.   
  32.         if (!dynamic_args) {  
  33.             if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {  
  34.                 ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));  
  35.             }  
  36.         } else {  
  37.             char *arg_ptrs[INIT_PARSER_MAXARGS+1];  
  38.             int arg_idx = svc->nargs;  
  39.             char *tmp = strdup(dynamic_args);  
  40.             char *next = tmp;  
  41.             char *bword;  
  42.   
  43.             /* Copy the static arguments */  
  44.             memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));  
  45.   
  46.             while((bword = strsep(&next, " "))) {  
  47.                 arg_ptrs[arg_idx++] = bword;  
  48.                 if (arg_idx == INIT_PARSER_MAXARGS)  
  49.                     break;  
  50.             }  
  51.             arg_ptrs[arg_idx] = '\0';  
  52.             execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);  
  53.         }  
  54.         _exit(127);  
  55.     }  
  56.   
  57.     ......  
  58.     if (properties_inited())  
  59.         notify_service_state(svc->name, "running");  
  60. }  

首先会fork一个子进程,然后创建相应的socket

socket netd stream 0660 root system

表示创建一个"/dev/socket/netd"这样一个Unix域的socket,这正好和前面的NativeDaemonConnector的socket对应

貌似有点眉目了

初始化好之后就是exec家族的系统调用了,这里的是/system/bin/netd


看看main函数吧

/system/netd/main.cpp

[cpp]  view plain copy
  1. int main() {  
  2.   
  3.     CommandListener *cl;  
  4.     NetlinkManager *nm;  
  5.     DnsProxyListener *dpl;  
  6.     MDnsSdListener *mdnsl;  
  7.   
  8.     if (!(nm = NetlinkManager::Instance())) {  
  9.         ALOGE("Unable to create NetlinkManager");  
  10.         exit(1);  
  11.     };  
  12.   
  13.   
  14.     cl = new CommandListener();  
  15.     nm->setBroadcaster((SocketListener *) cl);  
  16.   
  17.     if (nm->start()) {  
  18.         ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));  
  19.         exit(1);  
  20.     }  
  21.   
  22.     dpl = new DnsProxyListener();  
  23.     if (dpl->startListener()) {  
  24.         ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));  
  25.         exit(1);  
  26.     }  
  27.   
  28.     mdnsl = new MDnsSdListener();  
  29.     if (mdnsl->startListener()) {  
  30.         ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));  
  31.         exit(1);  
  32.     }  
  33.   
  34.     if (cl->startListener()) {  
  35.         ALOGE("Unable to start CommandListener (%s)", strerror(errno));  
  36.         exit(1);  
  37.     }  
  38.   
  39.     // Eventually we'll become the monitoring thread  
  40.     while(1) {  
  41.         sleep(1000);  
  42.     }  
  43.   
  44.     ALOGI("Netd exiting");  
  45.     exit(0);  

先实例化一个NetlinkManager

Netlink。。貌似是跟内核打交道的

看看定义

[cpp]  view plain copy
  1. class NetlinkManager {  
  2. private:  
  3.     static NetlinkManager *sInstance;  
  4.   
  5. private:  
  6.     SocketListener       *mBroadcaster;  
  7.     NetlinkHandler       *mUeventHandler;  
  8.     NetlinkHandler       *mRouteHandler;  
  9.     NetlinkHandler       *mQuotaHandler;  
  10.     NetlinkHandler       *mIfaceIdleTimerHandler;  
  11.     int                  mUeventSock;  
  12.     int                  mRouteSock;  
  13.     int                  mQuotaSock;  
  14.     int                  mIfaceIdleTimerSock;  
[cpp]  view plain copy
  1. }  
单例模式

定义很明显了,有一个SocketListener

SocketListener,看来就是Socket的server端。

然后定义了四个handler和四个socket

看到uevent几乎可以确定肯定是和内核相关了。

继续看Main吧

cl = new CommandListener();

class CommandListener : public FrameworkListener;

class FrameworkListener : public SocketListener;

根据继承关系CommandListener其实就是一个SocketListener

CommandListener构造简直就是一对bull shit

[cpp]  view plain copy
  1. CommandListener::CommandListener() :  
  2.                  FrameworkListener("netd"true) {  
  3.     registerCmd(new InterfaceCmd());  
  4.     registerCmd(new IpFwdCmd());  
  5.     ......  
  6.     if (!sSecondaryTableCtrl)  
  7.         sSecondaryTableCtrl = new SecondaryTableController();  
  8.     if (!sTetherCtrl)  
  9.         sTetherCtrl = new TetherController();  
  10.     ......  
  11. }  

太不科学了,貌似是注册了一大堆cmd,放在mCommands里面

看看父类FrameworkListener:

[cpp]  view plain copy
  1. FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :  
  2.                             SocketListener(socketName, true, withSeq) {  
  3.     init(socketName, withSeq);  
  4. }  

简洁多了,就是先创建一个SocketListener然后init

[cpp]  view plain copy
  1. SocketListener::SocketListener(const char *socketName, bool listen) {  
  2.     init(socketName, -1, listen, false);  
  3. }  
  4. void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {  
  5.     mListen = listen;  
  6.     mSocketName = socketName;  
  7.     mSock = socketFd;  
  8.     mUseCmdNum = useCmdNum;  
  9.     pthread_mutex_init(&mClientsLock, NULL);  
  10.     mClients = new SocketClientCollection();  
  11. }  


[cpp]  view plain copy
  1.   
貌似也没干什么,就是把netd这个名字保存在mSocketName中,然后建立了一个mClients的集合
[cpp]  view plain copy
  1. void FrameworkListener::init(const char *socketName, bool withSeq) {  
  2.     mCommands = new FrameworkCommandCollection();  
  3.     errorRate = 0;  
  4.     mCommandCount = 0;  
  5.     mWithSeq = withSeq;  
  6. }  

这个就是创建cmd集合

其实整个结构也比较清晰,就是初始化了一个Unix域的一个名叫“netd”的socket,这个socket负责接收client(也就是NativeDaemonConnector)发来的消息

怎么处理消息呢,就定义了一组cmd,不通的消息交给不同的cmd处理,然后把结果返回给client。


回到main

nm->setBroadcaster((SocketListener *) cl);

这个没什么好说的,就是把NetlinkManager和CommandListener连系起来,一起搅基啊,怎么搅呢?

这是因为如果底下网络设备有变化的话,比如设备增加,带宽变化,当然不能等client一直主动询问有没啥变化。

这就多了个这个叫Broadcaster的东西,有个玩意儿(NetlinkHandler)不停的轮询内核事件,一有消息就去找Broadcaster

Broadcaster实质就是client连过来的fd嘛。


nm->start();

[cpp]  view plain copy
  1. int NetlinkManager::start() {  
  2.     if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,  
  3.          0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {  
  4.         return -1;  
  5.     }  
  6.   
  7.     if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE, RTMGRP_LINK,  
  8.          NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {  
  9.         return -1;  
  10.     }  
  11.   
  12.     if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,  
  13.         NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {  
  14.         ALOGE("Unable to open quota2 logging socket");  
  15.         // TODO: return -1 once the emulator gets a new kernel.  
  16.     }  
  17.   
  18.     return 0;  
  19. }  
要start了,一副关键代码的样子

这里分别用setupSocket初始化了三个handler,随便跟进去一个吧:

[cpp]  view plain copy
  1. NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,  
  2.     int groups, int format) {  
  3.   
  4.     struct sockaddr_nl nladdr;  
  5.     int sz = 64 * 1024;  
  6.     int on = 1;  
  7.   
  8.     memset(&nladdr, 0, sizeof(nladdr));  
  9.     nladdr.nl_family = AF_NETLINK;  
  10.     nladdr.nl_pid = getpid();  
  11.     nladdr.nl_groups = groups;  
  12.   
  13.     if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {  
  14.         ALOGE("Unable to create netlink socket: %s", strerror(errno));  
  15.         return NULL;  
  16.     }  
  17.   
  18.   
  19.     if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {  
  20.         ALOGE("Unable to bind netlink socket: %s", strerror(errno));  
  21.         close(*sock);  
  22.         return NULL;  
  23.     }  
  24.   
  25.     NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);  
  26.     if (handler->start()) {  
  27.         ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));  
  28.         close(*sock);  
  29.         return NULL;  
  30.     }  
  31.   
  32.     return handler;  
  33. }  
终于看到socket系统调用了,这里是个PF_NETLINK, family 是 NETLINK_KOBJECT_UEVENT

这个是啥意思俺也不大明白,大致就是内核有NETLINK_KOBJECT_UEVENT这种事件就回报上来

后面又是bind系统调用,怎么不见listen??

后面新建了一个NetlinkHandler

这个NetlinkHandler继承NetlinkListener

NetlinkListener又继承SocketListener

啊。。又是SocketListener

很明显,它要倾听来自内核的声音。。内核就像一个client,发送数据给它,只不过这些都是在kernel里面实现的吧。。什么原理也不清楚

看看handler->start()

一直会call到SocketListener的start方法:

[cpp]  view plain copy
  1. int SocketListener::startListener() {  
  2.     if (mListen && listen(mSock, 4) < 0) {  
  3.         SLOGE("Unable to listen on socket (%s)", strerror(errno));  
  4.         return -1;  
  5.     } else if (!mListen)  
  6.         mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));  
  7.   
  8.     if (pipe(mCtrlPipe)) {  
  9.         SLOGE("pipe failed (%s)", strerror(errno));  
  10.         return -1;  
  11.     }  
  12.   
  13.     if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {  
  14.         SLOGE("pthread_create (%s)", strerror(errno));  
  15.         return -1;  
  16.     }  
  17.   
  18.     return 0;  
  19. }  


跟进去SocketListener的threadStart方法,最后会call到

[cpp]  view plain copy
  1. void SocketListener::runListener() {  
  2.   
  3.     SocketClientCollection *pendingList = new SocketClientCollection();  
  4.   
  5.     while(1) {  
  6.         SocketClientCollection::iterator it;  
  7.         fd_set read_fds;  
  8.         int rc = 0;  
  9.         int max = -1;  
  10.   
  11.         FD_ZERO(&read_fds);  
  12.   
  13.         if (mListen) {  
  14.             max = mSock;  
  15.             FD_SET(mSock, &read_fds);  
  16.         }  
  17.   
  18.         FD_SET(mCtrlPipe[0], &read_fds);  
  19.         if (mCtrlPipe[0] > max)  
  20.             max = mCtrlPipe[0];  
  21.   
  22.         pthread_mutex_lock(&mClientsLock);  
  23.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  24.             int fd = (*it)->getSocket();  
  25.             FD_SET(fd, &read_fds);  
  26.             if (fd > max)  
  27.                 max = fd;  
  28.         }  
  29.         pthread_mutex_unlock(&mClientsLock);  
  30.         SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);  
  31.         if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {  
  32.             if (errno == EINTR)  
  33.                 continue;  
  34.             SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);  
  35.             sleep(1);  
  36.             continue;  
  37.         } else if (!rc)  
  38.             continue;  
  39.   
  40.         if (FD_ISSET(mCtrlPipe[0], &read_fds))  
  41.             break;  
  42.         if (mListen && FD_ISSET(mSock, &read_fds)) {  
  43.             struct sockaddr addr;  
  44.             socklen_t alen;  
  45.             int c;  
  46.   
  47.             do {  
  48.                 alen = sizeof(addr);  
  49.                 c = accept(mSock, &addr, &alen);  
  50.                 SLOGV("%s got %d from accept", mSocketName, c);  
  51.             } while (c < 0 && errno == EINTR);  
  52.             if (c < 0) {  
  53.                 SLOGE("accept failed (%s)", strerror(errno));  
  54.                 sleep(1);  
  55.                 continue;  
  56.             }  
  57.             pthread_mutex_lock(&mClientsLock);  
  58.             mClients->push_back(new SocketClient(c, true, mUseCmdNum));  
  59.             pthread_mutex_unlock(&mClientsLock);  
  60.         }  
  61.   
  62.         /* Add all active clients to the pending list first */  
  63.         pendingList->clear();  
  64.         pthread_mutex_lock(&mClientsLock);  
  65.         for (it = mClients->begin(); it != mClients->end(); ++it) {  
  66.             int fd = (*it)->getSocket();  
  67.             if (FD_ISSET(fd, &read_fds)) {  
  68.                 pendingList->push_back(*it);  
  69.             }  
  70.         }  
  71.         pthread_mutex_unlock(&mClientsLock);  
  72.   
  73.         /* Process the pending list, since it is owned by the thread, 
  74.          * there is no need to lock it */  
  75.         while (!pendingList->empty()) {  
  76.             /* Pop the first item from the list */  
  77.             it = pendingList->begin();  
  78.             SocketClient* c = *it;  
  79.             pendingList->erase(it);  
  80.             /* Process it, if false is returned and our sockets are 
  81.              * connection-based, remove and destroy it */  
  82.             if (!onDataAvailable(c) && mListen) {  
  83.                 /* Remove the client from our array */  
  84.                 SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);  
  85.                 pthread_mutex_lock(&mClientsLock);  
  86.                 for (it = mClients->begin(); it != mClients->end(); ++it) {  
  87.                     if (*it == c) {  
  88.                         mClients->erase(it);  
  89.                         break;  
  90.                     }  
  91.                 }  
  92.                 pthread_mutex_unlock(&mClientsLock);  
  93.                 /* Remove our reference to the client */  
  94.                 c->decRef();  
  95.             }  
  96.         }  
  97.     }  
  98.     delete pendingList;  
  99. }  
这个函数真实复杂无比。。。我的内心实在承受不了

就看了几个关键:

rc = select(max + 1, &read_fds, NULL, NULL, NULL);

c = accept(mSock, &addr, &alen);

mClients->push_back(new SocketClient(c, true, mUseCmdNum));

onDataAvailable(c);


select的fd集合就是内核事件

accpet之后有个onDataAvailable

跟进去!

[cpp]  view plain copy
  1. bool NetlinkListener::onDataAvailable(SocketClient *cli)  
  2. {  
  3.     int socket = cli->getSocket();  
  4.     ssize_t count;  
  5.     uid_t uid = -1;  
  6.   
  7.     count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(  
  8.                                        socket, mBuffer, sizeof(mBuffer), &uid));  
  9.     if (count < 0) {  
  10.         if (uid > 0)  
  11.             LOG_EVENT_INT(65537, uid);  
  12.         SLOGE("recvmsg failed (%s)", strerror(errno));  
  13.         return false;  
  14.     }  
  15.   
  16.     NetlinkEvent *evt = new NetlinkEvent();  
  17.     if (!evt->decode(mBuffer, count, mFormat)) {  
  18.         SLOGE("Error decoding NetlinkEvent");  
  19.     } else {  
  20.         onEvent(evt);  
  21.     }  
  22.   
  23.     delete evt;  
  24.     return true;  
  25. }  

既然内核已经通知有事件了,就靠uevent_kernel_multicast_uid_recv先读出来

然后decode,不用管怎么个decode,反正就是一定格式嘛

最后onEvent

函数也很长,就取前一点点吧

[cpp]  view plain copy
  1. void NetlinkHandler::onEvent(NetlinkEvent *evt) {  
  2.     const char *subsys = evt->getSubsystem();  
  3.     if (!subsys) {  
  4.         ALOGW("No subsystem found in netlink event");  
  5.         return;  
  6.     }  
  7.   
  8.     if (!strcmp(subsys, "net")) {  
  9.         int action = evt->getAction();  
  10.         const char *iface = evt->findParam("INTERFACE");  
  11.   
  12.         if (action == evt->NlActionAdd) {  
  13.             notifyInterfaceAdded(iface);  

[cpp]  view plain copy
  1. void NetlinkHandler::notifyInterfaceAdded(const char *name) {  
  2.     char msg[255];  
  3.     snprintf(msg, sizeof(msg), "Iface added %s", name);  
  4.   
  5.     mNm->getBroadcaster()->sendBroadcast(ResponseCode::InterfaceChange,  
  6.             msg, false);  
  7. }  

开始找Broadcaster了,就是前面的CommandListener

这样下去就把事件传给了Framework,整个过程都是内核事件触发的


那framework过来的command又是怎么处理的呢?

继续看main.cpp


cl->startListener()

这个其实和上面是一个道理了

只不过onDataAvailable变成了这个:

[cpp]  view plain copy
  1. bool FrameworkListener::onDataAvailable(SocketClient *c) {  
  2.     char buffer[255];  
  3.     int len;  
  4.   
  5.     len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));  
  6.     if (len < 0) {  
  7.         SLOGE("read() failed (%s)", strerror(errno));  
  8.         return false;  
  9.     } else if (!len)  
  10.         return false;  
  11.   
  12.     int offset = 0;  
  13.     int i;  
  14.   
  15.     for (i = 0; i < len; i++) {  
  16.         if (buffer[i] == '\0') {  
  17.             /* IMPORTANT: dispatchCommand() expects a zero-terminated string */  
  18.             dispatchCommand(c, buffer + offset);  
  19.             offset = i + 1;  
  20.         }  
  21.     }  
  22.     return true;  
  23. }  

一样的,读socket然后处理

dispatchCommand 也是巨复杂的函数,选几行吧:

[cpp]  view plain copy
  1. void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
  2.     ......  
  3.     for (i = mCommands->begin(); i != mCommands->end(); ++i) {  
  4.         FrameworkCommand *c = *i;  
  5.         if (!strcmp(argv[0], c->getCommand())) {  
  6.             if (c->runCommand(cli, argc, argv)) {  
  7.                 SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
  8.             }  
  9.             goto out;  
  10.         }  
  11.     }  


开始注册了很多cmd,每个cmd都有自己的名字,比如“interface”
这里就会找到那个interface cmd
又是一个巨长的函数,就选list这个参数的一段吧
这就正好和最前面的"interface" "list"对应

[cpp]  view plain copy
  1. int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,  
  2.                                                       int argc, char **argv) {  
  3.     if (argc < 2) {  
  4.         cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument"false);  
  5.         return 0;  
  6.     }  
  7.   
  8.     if (!strcmp(argv[1], "list")) {  
  9.         DIR *d;  
  10.         struct dirent *de;  
  11.   
  12.         if (!(d = opendir("/sys/class/net"))) {  
  13.             cli->sendMsg(ResponseCode::OperationFailed, "Failed to open sysfs dir"true);  
  14.             return 0;  
  15.         }  
  16.   
  17.         while((de = readdir(d))) {  
  18.             if (de->d_name[0] == '.')  
  19.                 continue;  
  20.             cli->sendMsg(ResponseCode::InterfaceListResult, de->d_name, false);  
  21.         }  
  22.         closedir(d);  
  23.         cli->sendMsg(ResponseCode::CommandOkay, "Interface list completed"false);  
  24.         return 0;  


很简单的处理,就是打开/sys/class/net ,即访问linux的sys文件系统
如果你的以太网的driver是好的的话,应该在/sys/class/net下面有个eth0(或类似的名字)
这里把/sys/class/net 下面所有接口名字都找出来,然后发给client


返回到EthernetDataTracker.startMonitoring()(跨度有点大啊)

[cpp]  view plain copy
  1. InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);  
  2. mLinkUp = config.isActive();  
  3. if (config != null && mHwAddr == null) {  
  4.     mHwAddr = config.getHardwareAddress();  
  5.     if (mHwAddr != null) {  
  6.         mNetworkInfo.setExtraInfo(mHwAddr);  
  7.     }  
  8. }  
  9. reconnect();  

这里先得到该接口(我这是eth0)的一些信息保存下来
怎么得到的话估计又要找NativeDaemonConnector发送cmd 给netd了
最后reconnect()

[cpp]  view plain copy
  1. public boolean reconnect() {  
  2.     if (mLinkUp) {  
  3.         mTeardownRequested.set(false);  
  4.         runDhcp();  
  5.     }  
  6.     return mLinkUp;  
  7. }  


要dhcp了!

[cpp]  view plain copy
  1. private void runDhcp() {  
  2.         Thread dhcpThread = new Thread(new Runnable() {  
  3.             public void run() {  
  4.                 DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();  
  5.                 if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) {  
  6.                     Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());  
  7.                     return;  
  8.                 }  
  9.                 mLinkProperties = dhcpInfoInternal.makeLinkProperties();  
  10.                 mLinkProperties.setInterfaceName(mIface);  
  11.   
  12.                 mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);  
  13.                 Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);  
  14.                 msg.sendToTarget();  
  15.             }  
  16.         });  
  17.         dhcpThread.start();  
  18.     }  

一直跟下去的话会到 native层


[cpp]  view plain copy
  1. int dhcp_do_request(const char *interface,  
  2.                     char *ipaddr,  
  3.                     char *gateway,  
  4.                     uint32_t *prefixLength,  
  5.                     char *dns1,  
  6.                     char *dns2,  
  7.                     char *server,  
  8.                     uint32_t *lease,  
  9.                     char *vendorInfo)  
  10. {  
  11.     char result_prop_name[PROPERTY_KEY_MAX];  
  12.     char daemon_prop_name[PROPERTY_KEY_MAX];  
  13.     char prop_value[PROPERTY_VALUE_MAX] = {'\0'};  
  14.     char daemon_cmd[PROPERTY_VALUE_MAX * 2];  
  15.     const char *ctrl_prop = "ctl.start";  
  16.     const char *desired_status = "running";  
  17.     /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */  
  18.     char p2p_interface[MAX_INTERFACE_LENGTH];  
  19.   
  20.     get_p2p_interface_replacement(interface, p2p_interface);  
  21.   
  22.     snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",  
  23.             DHCP_PROP_NAME_PREFIX,  
  24.             p2p_interface);  
  25.   
  26.     snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",  
  27.             DAEMON_PROP_NAME,  
  28.             p2p_interface);  
  29.   
  30.     /* Erase any previous setting of the dhcp result property */  
  31.     property_set(result_prop_name, "");  
  32.   
  33.     /* Start the daemon and wait until it's ready */  
  34.     if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0'))  
  35.         snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, p2p_interface,  
  36.                  prop_value, interface);  
  37.     else  
  38.         snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, p2p_interface, interface);  
  39.     memset(prop_value, '\0', PROPERTY_VALUE_MAX);  
  40.     property_set(ctrl_prop, daemon_cmd);  
  41.     if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {  
  42.         snprintf(errmsg, sizeof(errmsg), "%s""Timed out waiting for dhcpcd to start");  
  43.         return -1;  
  44.     }  
  45.   
  46.     /* Wait for the daemon to return a result */  
  47.     if (wait_for_property(result_prop_name, NULL, 30) < 0) {  
  48.         snprintf(errmsg, sizeof(errmsg), "%s""Timed out waiting for DHCP to finish");  
  49.         return -1;  
  50.     }  
  51.   
  52.     if (!property_get(result_prop_name, prop_value, NULL)) {  
  53.         /* shouldn't ever happen, given the success of wait_for_property() */  
  54.         snprintf(errmsg, sizeof(errmsg), "%s""DHCP result property was not set");  
  55.         return -1;  
  56.     }  
  57.     if (strcmp(prop_value, "ok") == 0) {  
  58.         char dns_prop_name[PROPERTY_KEY_MAX];  
  59.         if (fill_ip_info(interface, ipaddr, gateway, prefixLength,  
  60.                 dns1, dns2, server, lease, vendorInfo) == -1) {  
  61.             return -1;  
  62.         }  
  63.   
  64.         /* copy dns data to system properties - TODO - remove this after we have async 
  65.          * notification of renewal's */  
  66.         snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", interface);  
  67.         property_set(dns_prop_name, *dns1 ? ipaddr_to_string(*dns1) : "");  
  68.         snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", interface);  
  69.         property_set(dns_prop_name, *dns2 ? ipaddr_to_string(*dns2) : "");  
  70.         return 0;  
  71.     } else {  
  72.         snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);  
  73.         return -1;  
  74.     }  
  75. }  

[cpp]  view plain copy
  1. <pre></pre>  
  2. <pre></pre>  
  3. <pre></pre>  
  4. <pre></pre>  
  5. <pre></pre>  
  6. <pre></pre>  
  7. <pre></pre>  
  8. <pre></pre>  
  9. <pre></pre>  

你可能感兴趣的:(看看写写 之 Android上Ethernet和netd那点事)