Netd和framework层的通信,主要解释socket

来源:互联网 发布:epoll 高性能网络库 编辑:程序博客网 时间:2024/06/10 09:29

Netd和framework层的通信

这两者的通信时通过unix domain socket来完成的。

系统初始化的时候会从init.rc里面读取并创建socket,名字叫netd

service netd /system/bin/netd

    class main

    socket netd stream 0660 root system

    socket dnsproxyd stream 0660 root inet

    socket mdns stream 0660 root system

 

1.      先来看一下socket的创建过程

手机开机的时候执行init.c的main函数

int main(int argc, char **argv)

{

………

restorecon("/dev");

restorecon("/dev/socket");//创建的socket文件在这个目录下

……...

init_parse_config_file("/init.rc");

………

}

init_parse_config_file调用init_parse.c的parse_config函数里处理init.rc的数据。

Socket的处理是在parse_line_service里面,保存socketinfo到一个链表里面

static void parse_line_service(struct parse_state *state, int nargs, char **args)

{

case K_socket: {/* name type perm [ uid gid ] */

        struct socketinfo *si;

        if (nargs < 4) {

parse_error(state, "socket option requires name, type, perm arguments\n");

            break;

        }

        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")

                && strcmp(args[2],"seqpacket")) {

parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");

            break;

        }

        si = calloc(1, sizeof(*si));

        if (!si) {

            parse_error(state, "out of memory\n");

            break;

        }

        si->name = args[1];

        si->type = args[2];

        si->perm = strtoul(args[3], 0, 8);

        if (nargs > 4)

            si->uid = decode_uid(args[4]);//userID

        if (nargs > 5)

            si->gid = decode_uid(args[5]);//groupID

        si->next = svc->sockets;

        svc->sockets = si;

        break;

    }

Socket真正的创建过程是在启动service的时候,即调用函数service_start的时候

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

{

……….

        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);

            }

        }

…………

}

这里做了两个操作,create_socket是创建socket,push_socket是把socket和socketname存储到一个数组里面。

Create_socket

/*

 * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR

 * ("/dev/socket") as dictated in init.rc. This socket is inherited by the

 * daemon. We communicate the file descriptor's value via the environment

 * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").

 */

int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid)

{

    struct sockaddr_un addr;

    int fd, ret;

#ifdef HAVE_SELINUX

    char *secon;

#endif

 

    fd = socket(PF_UNIX, type, 0);

    if (fd < 0) {

        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));

        return -1;

    }

 

    memset(&addr, 0 , sizeof(addr));

    addr.sun_family = AF_UNIX;

//这里的sun_path就是socket的文件路径

snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",name);

 

    ret = unlink(addr.sun_path);

    if (ret != 0 && errno != ENOENT) {

        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));

        goto out_close;

    }

 

#ifdef HAVE_SELINUX

    secon = NULL;

    if (sehandle) {

        ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK);

        if (ret == 0)

            setfscreatecon(secon);

    }

#endif

 

    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));

    if (ret) {

        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));

        goto out_unlink;

    }

 

#ifdef HAVE_SELINUX

    setfscreatecon(NULL);

    freecon(secon);

#endif

 

    chown(addr.sun_path, uid, gid);

    chmod(addr.sun_path, perm);

 

    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",

         addr.sun_path, perm, uid, gid);

 

    return fd;

 

out_unlink:

    unlink(addr.sun_path);

out_close:

    close(fd);

    return -1;

}

Publish_socket

static void publish_socket(const char *name, int fd)

{

    char key[64] = ANDROID_SOCKET_ENV_PREFIX;

    char val[64];

 

    strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,

            name,

            sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));

    snprintf(val, sizeof(val), "%d", fd);

    add_environment(key, val);

 

    /* make sure we don't close-on-exec */

    fcntl(fd, F_SETFD, 0);

}

这里的fd就是刚才新建socket的fd,name是在parse_line_service设置的name,即netd。最后调用add_environment(key, val)以key=value的字符串形式存储到数组ENV里面。

在service_start函数的最后

execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);

这里把env当做环境变量集传了进去

 

2.      Netd里面获取socket

先看Netd里面CommandListener的构造函数,这个类是继承自FrameworkListener,从名字就可以看出来是跟framework打交道的

CommandListener::CommandListener() :

                 FrameworkListener("netd", true) {

………………

}

           再看FrmameworkListener对应的构造函数

FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :

                            SocketListener(socketName, true, withSeq) {

    init(socketName, withSeq);

}

          SocketListener的构造函数

SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {

    init(socketName, -1, listen, useCmdNum);

}

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();

}

         下面在startListener里面获取socket的fd

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) {

            return -1;

        }

        SLOGV("got mSock = %d for %s", mSock, mSocketName);

    }

 

    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;

}

android_get_control_socket去找到name对应的fd
static inline int android_get_control_socket(const char *name)
{
         char key[64] = ANDROID_SOCKET_ENV_PREFIX;
         const char *val;
         int fd;
 
         /* build our environment variable, counting cycles like a wolf ... */
#if HAVE_STRLCPY
         strlcpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
                 name,
                 sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
#else
//包装成init里面设置的key,价格前缀,变成ANDROID_SOCKET_netd
        strncpy(key + sizeof(ANDROID_SOCKET_ENV_PREFIX) - 1,
               name,
               sizeof(key) - sizeof(ANDROID_SOCKET_ENV_PREFIX));
         key[sizeof(key)-1] = '\0';
#endif
 
         val = getenv(key);//从环境变量里面获取key对应的value
         if (!val)
                 return -1;
 
         errno = 0;
         fd = strtol(val, NULL, 10);
         if (errno)
                 return -1;
 
         return fd;
}
 
现在netd作为一个socket的server以及开始listen了。
 
3.  Framework部分
从NetworkManagermentService开始
    private NetworkManagementService(Context context) {
        mContext = context;
 
        mConnector = new NativeDaemonConnector(
                new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);
        mThread = new Thread(mConnector, NETD_TAG);
 
        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
    }
这里初始化一个NativeDaemonConnector来和netd通信,NativeDaemonConnector在listenToSocket函数里面会connect到netd的socket
private void listenToSocket() throws IOException {
socket = new LocalSocket();
            LocalSocketAddress address = new LocalSocketAddress(mSocket,
                    LocalSocketAddress.Namespace.RESERVED);
 
            socket.connect(address);
……..
}
重点是connect函数
LocalSocket.java
    public void connect(LocalSocketAddress endpoint) throws IOException {
        synchronized (this) {
            if (isConnected) {
                throw new IOException("already connected");
            }
 
            implCreateIfNeeded();//新建一个local socket if needed
            impl.connect(endpoint, 0);
            isConnected = true;
            isBound = true;
        }
    }
调用的是LocalSocketImpl的connect函数
    protected void connect(LocalSocketAddress address, int timeout)
                        throws IOException
    {        
        connectLocal(fd, address.getName(), address.getNamespace().getId());
    }
connectLocal是个JNI方法,在android_net_LocalSocketImpl.cpp里面定义,对应socket_connect_local
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);
 
    if (ret < 0) {
        jniThrowIOException(env, errno);
        return;
    }
}
socket_local_client_connect在system/core/libcutils/socket_local_client.c
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;
//Namespace=RESERVED=1
    err = socket_make_sockaddr_un(name, namespaceId, &addr, &alen);
 
    if(connect(fd, (struct sockaddr *) &addr, alen) < 0) {
        goto error;
    }
 
    return fd;
 
error:
    return -1;
}
socket_make_sockaddr_un函数
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;
            }
//结果是dev/socket/netd即socket文件的路径代替网络socket的地址
            strcpy(p_addr->sun_path, ANDROID_RESERVED_SOCKET_PREFIX);
            strcat(p_addr->sun_path, name);
        break;
p_addr->sun_family = AF_LOCAL;
    *alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
    return 0;
现在Framework这边已经连上了netd的socket,两者就可以开始通信了。
Framework层还有一个LocalSocket作为server的接口。
原创粉丝点击