Android应用跨进程通信-Unix domain socket笔记

1. 背景

最近在做一个需求,需要native守护进程需要跟app进行通讯,App作为服务端,而native程序作为客户端,正常app与app之前通信都是通过binder方式通信,但基于此背景,肯定不合适,故首选应该通过socket方式

2. 编码

2.1 方案选型

采用Java LocalServerSocket/LocalSocket进行通讯,目前framework层APP仅支持LocalSocketAddress.Namespace.ABSTRACT,也是LocalServerSocket默认的类型,传入一个名称即可。LocalSocketAddress.Namespace.RESERVED这个只允许是init创建的类型,也可以选择LocalSocketAddress.Namespace.FILESYSTEM类型,但是没有公开,是不是也可以用呢,故想挑战下自己

2.2 编写代码

具体查看LocalSocketAddress类无法直接设置FILESYSTEM类型的LocalSocketAddress,只能传入name或者一个文件描述符,fd从哪里来,选择从jni直接创建socket,返回fd,然后反射设置fd初始化FileDescriptor

#define UDS_PATH "/data/system/rms_socket"

int native_get_sock_fd(JNIEnv *env) {
    struct sockaddr_un server_socket;
    int sock = 0;
    pthread_t thread;
    int opt = 1;

    //socket
    sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
        return -1;
    }

    //setopt reuse
    setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    //set address
    memset(&server_socket, 0, sizeof(server_socket));
    server_socket.sun_family = AF_LOCAL;
    strcpy(server_socket.sun_path, UDS_PATH);
    socklen_t
    socklen = strlen(UDS_PATH) + offsetof(
    struct sockaddr_un, sun_path);

    //bind
    if (bind(sock, (struct sockaddr *) &server_socket, socklen) < 0) {
        close(sock);
        return -1;
    }
    return sock;
}

java代码部分

        FileDescriptor fileDescriptor = new FileDescriptor();
        int native_fd = NativeSock.get_sock_fd();

        Utils.logDebug(TAG, "init native_fd : " + native_fd);
        if (native_fd == -1) {
            return;
        }
        
        try {
            @SuppressLint("DiscouragedPrivateApi")
            Method method = fileDescriptor.getClass().getDeclaredMethod("setInt$", int.class);
            method.setAccessible(true);
            method.invoke(fileDescriptor, native_fd);
        } catch (Exception e) {
            e.printStackTrace();
            Utils.logError(TAG, "init", e);
            return;
        }

        try {
            mServerSocket = new LocalServerSocket(fileDescriptor);
        } catch (IOException e) {
            return;
        }

2.3 填坑

2.3.1 坑一

刚刚c++代码server_socket.sun_family = AF_LOCAL; 写的是AF_UNIX类型,一直bind的时候报权限错误,就开始排查,最终定位/system/core/libcutils/socket_local_client_unix.c中的函数int socket_make_sockaddr_un(const char *name, int namespaceId, struct sockaddr_un *p_addr, socklen_t *alen),这里的默认sun_family为AF_LOCAL类型,修改之后bind正常

2.3.2 坑二

偶尔能bind成功,但是很多时候是地址已绑定错误,最终不创建UDSpath对应的文件修复

你可能感兴趣的:(Android应用跨进程通信-Unix domain socket笔记)