看看写写 之 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

    public NetworkInfo getActiveNetworkInfo() {
        enforceAccessPermission();
        final int uid = Binder.getCallingUid();
        return getNetworkInfo(mActiveDefaultNetwork, uid);
    }


    private NetworkInfo getNetworkInfo(int networkType, int uid) {
        NetworkInfo info = null;
        if (isNetworkTypeValid(networkType)) {
            final NetworkStateTracker tracker = mNetTrackers[networkType];
            if (tracker != null) {
                info = getFilteredNetworkInfo(tracker, uid);
            }
        }
        return info;
    }
这里有一组NetworkStateTracker,想想肯定是一组可用的连接了

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

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

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

想法设法证实这一点吧

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

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

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


看看构造

ConnectivityService.java

	String[] raStrings = context.getResources().getStringArray(
                com.android.internal.R.array.radioAttributes);
        for (String raString : raStrings) {
            RadioAttributes r = new RadioAttributes(raString);
            ......
            mRadioAttributes[r.mType] = r;
        }

        String[] naStrings = context.getResources().getStringArray(
                com.android.internal.R.array.networkAttributes);
        for (String naString : naStrings) {
            try {
                NetworkConfig n = new NetworkConfig(naString);
                ......
                mNetConfigs[n.type] = n;
                mNetworksDefined++;
            } catch(Exception e) {
                // ignore it - leave the entry null
            }
        }
	for (int netType : mPriorityList) {
            switch (mNetConfigs[netType].radio) {
            case ConnectivityManager.TYPE_WIFI:
                mNetTrackers[netType] = new WifiStateTracker(netType,
                        mNetConfigs[netType].name);
                mNetTrackers[netType].startMonitoring(context, mHandler);
               break;
            case ConnectivityManager.TYPE_MOBILE:
                mNetTrackers[netType] = new MobileDataStateTracker(netType,
                        mNetConfigs[netType].name);
                mNetTrackers[netType].startMonitoring(context, mHandler);
                break;
            ......
            case ConnectivityManager.TYPE_ETHERNET:
                mNetTrackers[netType] = EthernetDataTracker.getInstance();
                mNetTrackers[netType].startMonitoring(context, mHandler);
                break;
            default:
                loge("Trying to create a DataStateTracker for an unknown radio type " +
                        mNetConfigs[netType].radio);
                continue;
            }
            mCurrentLinkProperties[netType] = null;
            if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {
                mNetTrackers[netType].reconnect();
            }
        }
这里先从xml读出来两个属性

com.android.internal.R.array.radioAttributes

com.android.internal.R.array.networkAttributes

分别保存到

mRadioAttributes和mNetConfigs两个数组里面

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

找到xml文件

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

    
        "wifi,1,1,1,-1,true"
        "mobile,0,0,0,-1,true"
        "mobile_mms,2,0,2,60000,true"
        "mobile_supl,3,0,2,60000,true"
        "mobile_hipri,5,0,3,60000,true"
        "mobile_fota,10,0,2,60000,true"
        "mobile_ims,11,0,2,60000,true"
        "mobile_cbs,12,0,2,60000,true"
        "wifi_p2p,13,1,0,-1,true"
    

    
        "1,1"
        "0,1"
    

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

赶紧补上,

networkAttributes:

	"eth,9,9,4,60000,true"
之所以这么补两个9是因为后面初始化的时候是这么弄的,后面的4是优先级,再后面两个参数就没这么管了,照着前面写

radioAttributes

	"9,1"

看这个分支:

            case ConnectivityManager.TYPE_ETHERNET:
                mNetTrackers[netType] = EthernetDataTracker.getInstance();
                mNetTrackers[netType].startMonitoring(context, mHandler);
                break;

继续追踪到startMonitoring

EthernetDataTracker.java

    public void startMonitoring(Context context, Handler target) {
        mContext = context;
        mCsHandler = target;

        // register for notifications from NetworkManagement Service
        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
        mNMService = INetworkManagementService.Stub.asInterface(b);

        mInterfaceObserver = new InterfaceObserver(this);
 
        sIfaceMatch = context.getResources().getString(
            com.android.internal.R.string.config_ethernet_iface_regex);
        try {
            final String[] ifaces = mNMService.listInterfaces();
            for (String iface : ifaces) {
                if (iface.matches(sIfaceMatch)) {
                    mIface = iface;
                    mNMService.setInterfaceUp(iface);
                    InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
                    mLinkUp = config.isActive();
                    if (config != null && mHwAddr == null) {
                        mHwAddr = config.getHardwareAddress();
                        if (mHwAddr != null) {
                            mNetworkInfo.setExtraInfo(mHwAddr);
                        }
                    }
                    reconnect();
                    break;
                }
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Could not get list of interfaces " + e);
        }
    }

这里先取出一个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

    public String[] listInterfaces() {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
        try {
            return NativeDaemonEvent.filterMessageList(
                    mConnector.executeForList("interface", "list"), InterfaceListResult);
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        }
    }

mConnector:NativeDaemonConnector

继续跟踪:

public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
            throws NativeDaemonConnectorException {
        final ArrayList events = Lists.newArrayList();

        final int sequenceNumber = mSequenceNumber.incrementAndGet();
        final StringBuilder cmdBuilder =
                new StringBuilder(Integer.toString(sequenceNumber)).append(' ');
        final long startTime = SystemClock.elapsedRealtime();

        makeCommand(cmdBuilder, cmd, args);

        final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */

        synchronized (mDaemonLock) {
            if (mOutputStream == null) {
                throw new NativeDaemonConnectorException("missing output stream");
            } else {
                try {
                    mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));
                } catch (IOException e) {
                    throw new NativeDaemonConnectorException("problem sending command", e);
                }
            }
        }
	......
}
参数cmd 就是"list"

参数args就是"interface"

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

最后发送出去:

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

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


只好跟踪mOutputStream

    private void listenToSocket() throws IOException {
        LocalSocket socket = null;

        try {
            socket = new LocalSocket();
            LocalSocketAddress address = new LocalSocketAddress(mSocket,
                    LocalSocketAddress.Namespace.RESERVED);

            socket.connect(address);
            InputStream inputStream = socket.getInputStream();
            synchronized (mDaemonLock) {
                mOutputStream = socket.getOutputStream();
            }

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

android_net_LocalSocketImpl.cpp

static jobject
socket_create (JNIEnv *env, jobject object, jboolean stream)
{
    int ret;

    ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0);

    if (ret < 0) {
        jniThrowIOException(env, errno);
        return NULL;
    }

    return jniCreateFileDescriptor(env,ret);
}
有了socket当然会去connect,这里有个address,它的初始化的第一个参数mSocket是一个字符串

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

NativeDaemonConnector是NetworkManagerService的小弟

    private NetworkManagementService(Context context) {
        mContext = context;
        mConnector = new NativeDaemonConnector(
                new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);
        mThread = new Thread(mConnector, NETD_TAG);
    }
那个"netd"就是mSocket了

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

static void
socket_connect_local(JNIEnv *env, jobject object,
                        jobject fileDescriptor, jstring name, jint namespaceId)
{
    int ret;
    const char *nameUtf8;
    int fd;

    nameUtf8 = env->GetStringUTFChars(name, NULL);

    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);

    ret = socket_local_client_connect(
                fd,
                nameUtf8,
                namespaceId,
                SOCK_STREAM);

    env->ReleaseStringUTFChars(name, nameUtf8);
}
int socket_local_client_connect(int fd, const char *name, int namespaceId, 
        int type)
{
    struct sockaddr_un addr;
    socklen_t alen;
    size_t namelen;
    int err;


    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);


    if (err < 0) {
        goto error;
    }


    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
        goto error;
    }


    return fd;


error:
    return -1;
}
int socket_make_sockaddr_un(const char *name, int namespaceId, 
        struct sockaddr_un *p_addr, socklen_t *alen)
{
    memset (p_addr, 0, sizeof (*p_addr));
    size_t namelen;
    switch (namespaceId) {
        case ANDROID_SOCKET_NAMESPACE_RESERVED:
            namelen = strlen(name) + strlen(ANDROID_RESERVED_SOCKET_PREFIX);
            /* unix_path_max appears to be missing on linux */
            if (namelen > sizeof(*p_addr) 
                    - offsetof(struct sockaddr_un, sun_path) - 1) {
                goto error;
            }
            strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
            strcat(p_addr->sun_path, name);
        break;

        default:
            // invalid namespace id
            return -1;
    }


    p_addr->sun_family = AF_LOCAL;
    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
    return 0;
error:
    return -1;
}

这里使用的是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有这么一段:

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

void service_start(struct service *svc, const char *dynamic_args)
{
    ... ...

    NOTICE("starting '%s'\n", svc->name);

    pid = fork();

    if (pid == 0) {
        struct socketinfo *si;
        struct svcenvinfo *ei;
        char tmp[32];
        int fd, sz;

        umask(077);

        for (si = svc->sockets; si; si = si->next) {
            int socket_type = (
                    !strcmp(si->type, "stream") ? SOCK_STREAM :
                        (!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
            int s = create_socket(si->name, socket_type,
                                  si->perm, si->uid, si->gid);
            if (s >= 0) {
                publish_socket(si->name, s);
            }
        }

        setpgid(0, getpid());

    /* as requested, set our gid, supplemental gids, and uid */

        if (!dynamic_args) {
            if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
                ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
            }
        } else {
            char *arg_ptrs[INIT_PARSER_MAXARGS+1];
            int arg_idx = svc->nargs;
            char *tmp = strdup(dynamic_args);
            char *next = tmp;
            char *bword;

            /* Copy the static arguments */
            memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));

            while((bword = strsep(&next, " "))) {
                arg_ptrs[arg_idx++] = bword;
                if (arg_idx == INIT_PARSER_MAXARGS)
                    break;
            }
            arg_ptrs[arg_idx] = '\0';
            execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
        }
        _exit(127);
    }

    ......
    if (properties_inited())
        notify_service_state(svc->name, "running");
}

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

socket netd stream 0660 root system

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

貌似有点眉目了

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


看看main函数吧

/system/netd/main.cpp

int main() {

    CommandListener *cl;
    NetlinkManager *nm;
    DnsProxyListener *dpl;
    MDnsSdListener *mdnsl;

    if (!(nm = NetlinkManager::Instance())) {
        ALOGE("Unable to create NetlinkManager");
        exit(1);
    };


    cl = new CommandListener();
    nm->setBroadcaster((SocketListener *) cl);

    if (nm->start()) {
        ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
        exit(1);
    }

    dpl = new DnsProxyListener();
    if (dpl->startListener()) {
        ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
        exit(1);
    }

    mdnsl = new MDnsSdListener();
    if (mdnsl->startListener()) {
        ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
        exit(1);
    }

    if (cl->startListener()) {
        ALOGE("Unable to start CommandListener (%s)", strerror(errno));
        exit(1);
    }

    // Eventually we'll become the monitoring thread
    while(1) {
        sleep(1000);
    }

    ALOGI("Netd exiting");
    exit(0);

先实例化一个NetlinkManager

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

看看定义

class NetlinkManager {
private:
    static NetlinkManager *sInstance;

private:
    SocketListener       *mBroadcaster;
    NetlinkHandler       *mUeventHandler;
    NetlinkHandler       *mRouteHandler;
    NetlinkHandler       *mQuotaHandler;
    NetlinkHandler       *mIfaceIdleTimerHandler;
    int                  mUeventSock;
    int                  mRouteSock;
    int                  mQuotaSock;
    int                  mIfaceIdleTimerSock;
}
单例模式

定义很明显了,有一个SocketListener

SocketListener,看来就是Socket的server端。

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

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

继续看Main吧

cl = new CommandListener();

class CommandListener : public FrameworkListener;

class FrameworkListener : public SocketListener;

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

CommandListener构造简直就是一对bull shit

CommandListener::CommandListener() :
                 FrameworkListener("netd", true) {
    registerCmd(new InterfaceCmd());
    registerCmd(new IpFwdCmd());
    ......
    if (!sSecondaryTableCtrl)
        sSecondaryTableCtrl = new SecondaryTableController();
    if (!sTetherCtrl)
        sTetherCtrl = new TetherController();
    ......
}

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

看看父类FrameworkListener:

FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
                            SocketListener(socketName, true, withSeq) {
    init(socketName, withSeq);
}

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

SocketListener::SocketListener(const char *socketName, bool listen) {
    init(socketName, -1, listen, false);
}
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();
}


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

这个就是创建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();

int NetlinkManager::start() {
    if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
         0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
        return -1;
    }

    if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE, RTMGRP_LINK,
         NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
        return -1;
    }

    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;
}
要start了,一副关键代码的样子

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

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;

    if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
        ALOGE("Unable to create netlink socket: %s", strerror(errno));
        return NULL;
    }


    if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        ALOGE("Unable to bind netlink socket: %s", strerror(errno));
        close(*sock);
        return NULL;
    }

    NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
    if (handler->start()) {
        ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));
        close(*sock);
        return NULL;
    }

    return handler;
}
终于看到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方法:

int SocketListener::startListener() {
    if (mListen && listen(mSock, 4) < 0) {
        SLOGE("Unable to listen on socket (%s)", strerror(errno));
        return -1;
    } else if (!mListen)
        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;
}


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

void SocketListener::runListener() {

    SocketClientCollection *pendingList = new SocketClientCollection();

    while(1) {
        SocketClientCollection::iterator it;
        fd_set read_fds;
        int rc = 0;
        int max = -1;

        FD_ZERO(&read_fds);

        if (mListen) {
            max = mSock;
            FD_SET(mSock, &read_fds);
        }

        FD_SET(mCtrlPipe[0], &read_fds);
        if (mCtrlPipe[0] > max)
            max = mCtrlPipe[0];

        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);
        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
            if (errno == EINTR)
                continue;
            SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);
            sleep(1);
            continue;
        } else if (!rc)
            continue;

        if (FD_ISSET(mCtrlPipe[0], &read_fds))
            break;
        if (mListen && FD_ISSET(mSock, &read_fds)) {
            struct sockaddr addr;
            socklen_t alen;
            int c;

            do {
                alen = sizeof(addr);
                c = accept(mSock, &addr, &alen);
                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;
            }
            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 */
        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) {
                /* 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);
                        break;
                    }
                }
                pthread_mutex_unlock(&mClientsLock);
                /* Remove our reference to the client */
                c->decRef();
            }
        }
    }
    delete pendingList;
}
这个函数真实复杂无比。。。我的内心实在承受不了

就看了几个关键:

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

跟进去!

bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
    int socket = cli->getSocket();
    ssize_t count;
    uid_t uid = -1;

    count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(
                                       socket, mBuffer, sizeof(mBuffer), &uid));
    if (count < 0) {
        if (uid > 0)
            LOG_EVENT_INT(65537, uid);
        SLOGE("recvmsg failed (%s)", strerror(errno));
        return false;
    }

    NetlinkEvent *evt = new NetlinkEvent();
    if (!evt->decode(mBuffer, count, mFormat)) {
        SLOGE("Error decoding NetlinkEvent");
    } else {
        onEvent(evt);
    }

    delete evt;
    return true;
}

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

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

最后onEvent

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

void NetlinkHandler::onEvent(NetlinkEvent *evt) {
    const char *subsys = evt->getSubsystem();
    if (!subsys) {
        ALOGW("No subsystem found in netlink event");
        return;
    }

    if (!strcmp(subsys, "net")) {
        int action = evt->getAction();
        const char *iface = evt->findParam("INTERFACE");

        if (action == evt->NlActionAdd) {
            notifyInterfaceAdded(iface);

void NetlinkHandler::notifyInterfaceAdded(const char *name) {
    char msg[255];
    snprintf(msg, sizeof(msg), "Iface added %s", name);

    mNm->getBroadcaster()->sendBroadcast(ResponseCode::InterfaceChange,
            msg, false);
}

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

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


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

继续看main.cpp


cl->startListener()

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

只不过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;
}

一样的,读socket然后处理

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

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
    ......
    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
        FrameworkCommand *c = *i;
        if (!strcmp(argv[0], c->getCommand())) {
            if (c->runCommand(cli, argc, argv)) {
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
            }
            goto out;
        }
    }


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

int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
                                                      int argc, char **argv) {
    if (argc < 2) {
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
        return 0;
    }

    if (!strcmp(argv[1], "list")) {
        DIR *d;
        struct dirent *de;

        if (!(d = opendir("/sys/class/net"))) {
            cli->sendMsg(ResponseCode::OperationFailed, "Failed to open sysfs dir", true);
            return 0;
        }

        while((de = readdir(d))) {
            if (de->d_name[0] == '.')
                continue;
            cli->sendMsg(ResponseCode::InterfaceListResult, de->d_name, false);
        }
        closedir(d);
        cli->sendMsg(ResponseCode::CommandOkay, "Interface list completed", false);
        return 0;


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


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

                    InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
                    mLinkUp = config.isActive();
                    if (config != null && mHwAddr == null) {
                        mHwAddr = config.getHardwareAddress();
                        if (mHwAddr != null) {
                            mNetworkInfo.setExtraInfo(mHwAddr);
                        }
                    }
                    reconnect();

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

    public boolean reconnect() {
        if (mLinkUp) {
            mTeardownRequested.set(false);
            runDhcp();
        }
        return mLinkUp;
    }


要dhcp了!

private void runDhcp() {
        Thread dhcpThread = new Thread(new Runnable() {
            public void run() {
                DhcpInfoInternal dhcpInfoInternal = new DhcpInfoInternal();
                if (!NetworkUtils.runDhcp(mIface, dhcpInfoInternal)) {
                    Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
                    return;
                }
                mLinkProperties = dhcpInfoInternal.makeLinkProperties();
                mLinkProperties.setInterfaceName(mIface);

                mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
                Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
                msg.sendToTarget();
            }
        });
        dhcpThread.start();
    }

一直跟下去的话会到 native层


int dhcp_do_request(const char *interface,
                    char *ipaddr,
                    char *gateway,
                    uint32_t *prefixLength,
                    char *dns1,
                    char *dns2,
                    char *server,
                    uint32_t *lease,
                    char *vendorInfo)
{
    char result_prop_name[PROPERTY_KEY_MAX];
    char daemon_prop_name[PROPERTY_KEY_MAX];
    char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
    char daemon_cmd[PROPERTY_VALUE_MAX * 2];
    const char *ctrl_prop = "ctl.start";
    const char *desired_status = "running";
    /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
    char p2p_interface[MAX_INTERFACE_LENGTH];

    get_p2p_interface_replacement(interface, p2p_interface);

    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
            DHCP_PROP_NAME_PREFIX,
            p2p_interface);

    snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
            DAEMON_PROP_NAME,
            p2p_interface);

    /* Erase any previous setting of the dhcp result property */
    property_set(result_prop_name, "");

    /* Start the daemon and wait until it's ready */
    if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0'))
        snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-h %s %s", DAEMON_NAME, p2p_interface,
                 prop_value, interface);
    else
        snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME, p2p_interface, interface);
    memset(prop_value, '\0', PROPERTY_VALUE_MAX);
    property_set(ctrl_prop, daemon_cmd);
    if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
        return -1;
    }

    /* Wait for the daemon to return a result */
    if (wait_for_property(result_prop_name, NULL, 30) < 0) {
        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
        return -1;
    }

    if (!property_get(result_prop_name, prop_value, NULL)) {
        /* shouldn't ever happen, given the success of wait_for_property() */
        snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
        return -1;
    }
    if (strcmp(prop_value, "ok") == 0) {
        char dns_prop_name[PROPERTY_KEY_MAX];
        if (fill_ip_info(interface, ipaddr, gateway, prefixLength,
                dns1, dns2, server, lease, vendorInfo) == -1) {
            return -1;
        }

        /* copy dns data to system properties - TODO - remove this after we have async
         * notification of renewal's */
        snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns1", interface);
        property_set(dns_prop_name, *dns1 ? ipaddr_to_string(*dns1) : "");
        snprintf(dns_prop_name, sizeof(dns_prop_name), "net.%s.dns2", interface);
        property_set(dns_prop_name, *dns2 ? ipaddr_to_string(*dns2) : "");
        return 0;
    } else {
        snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
        return -1;
    }
}










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