AOSP Expert & Production Engineering
3 min read

Sockets

Unix Domain Sockets in Android

Unix domain sockets (UDS) are a standard POSIX mechanism for IPC on the same host system. Unlike network sockets that use IP addresses and ports, UDS use file paths in the local filesystem as addresses. They are highly efficient since they bypass the network stack entirely.

In Android, UDS are heavily used by core system daemons. When an app needs to talk to a low-level daemon (like vold or installd), Binder might be too heavyweight or the daemon might not support it. Instead, they communicate over Unix domain sockets.

#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>

int create_uds_server(const char* path) {
    int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);
    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);

    unlink(path); // Ensure path is clear
    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 5);
    return server_fd;
}

Socket Pairs

The socketpair() function creates a pair of connected, unnamed Unix domain sockets. It is similar to pipe(), but unlike pipes, socket pairs are bidirectional (full-duplex). This is extremely useful for parent-child communication where both sides need to send and receive data.

int sv[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {
    // Handle error
}
// sv[0] and sv[1] are now connected.

In Android, socketpair() is often used to send a file descriptor over Binder IPC, establishing a fast data channel between two processes after the initial Binder handshake.

Abstract Namespace Sockets (Android-specific)

Standard Unix domain sockets exist on the filesystem, which means they are subject to file permissions and leave a file behind if not cleaned up.

Linux introduced the abstract namespace, a feature heavily utilized in Android. Abstract sockets are not bound to the filesystem. They are identified by a string whose first byte is a null character (\0). When the process closes the socket, the namespace is automatically freed.

Android's init.rc configuration files frequently create abstract sockets for services using the socket keyword.

#include <sys/socket.h>
#include <sys/un.h>
#include <stddef.h>

int bind_abstract_socket(int fd, const char* name) {
    struct sockaddr_un addr;
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    
    // The first byte is null. The rest is the name.
    addr.sun_path[0] = '\0';
    strncpy(&addr.sun_path[1], name, sizeof(addr.sun_path) - 2);
    
    // The length must include the null byte and the string length.
    socklen_t len = offsetof(struct sockaddr_un, sun_path) + 1 + strlen(name);
    return bind(fd, (struct sockaddr*)&addr, len);
}

Netd and System Daemon Socket Communication

Android's netd (Network Daemon) manages network routing, iptables, and tethering. Applications and the system server communicate with netd via sockets.

While modern Android has moved many netd interfaces to Binder (via netd_aidl_interface), legacy and low-level communication still relies on sockets created in the init.rc file. For instance, the dnsproxyd socket is an abstract or filesystem UDS used by the C library (bionic) resolver functions to pass DNS queries to netd.

To view listening Unix domain sockets in Android, use the ss or netstat command:

adb shell su -c "ss -x -a"