Merge "Add support for Unix domain sockets."
diff --git a/luni/src/main/java/java/net/InetSocketAddress.java b/luni/src/main/java/java/net/InetSocketAddress.java
index 49dcfc4..4f4a348 100644
--- a/luni/src/main/java/java/net/InetSocketAddress.java
+++ b/luni/src/main/java/java/net/InetSocketAddress.java
@@ -131,18 +131,14 @@
     }
 
     /**
-     * Gets the port number of this socket.
-     *
-     * @return the socket endpoint port number.
+     * Returns this socket address' port.
      */
     public final int getPort() {
         return port;
     }
 
     /**
-     * Gets the address of this socket.
-     *
-     * @return the socket endpoint address.
+     * Returns this socket address' address.
      */
     public final InetAddress getAddress() {
         return addr;
diff --git a/luni/src/main/java/java/net/InetUnixAddress.java b/luni/src/main/java/java/net/InetUnixAddress.java
new file mode 100644
index 0000000..95bb097
--- /dev/null
+++ b/luni/src/main/java/java/net/InetUnixAddress.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.net;
+
+import java.nio.charset.Charsets;
+
+import static libcore.io.OsConstants.*;
+
+/**
+ * An AF_UNIX address. See {@link InetAddress}.
+ * @hide
+ */
+public final class InetUnixAddress extends InetAddress {
+  /**
+   * Constructs an AF_UNIX InetAddress for the given path.
+   */
+  public InetUnixAddress(String path) {
+    this(path.getBytes(Charsets.UTF_8));
+  }
+
+  /**
+   * Constructs an AF_UNIX InetAddress for the given path.
+   */
+  public InetUnixAddress(byte[] path) {
+    super(AF_UNIX, path, null);
+  }
+
+  /**
+   * Returns a string form of this InetAddress.
+   */
+  @Override public String toString() {
+    return "InetUnixAddress[" + new String(ipaddress, Charsets.UTF_8) + "]";
+  }
+}
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index fab3cc9..1c582ca 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -64,6 +64,7 @@
     public int getgid() { return os.getgid(); }
     public String getenv(String name) { return os.getenv(name); }
     public String getnameinfo(InetAddress address, int flags) throws GaiException { return os.getnameinfo(address, flags); }
+    public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException { return os.getpeername(fd); }
     public int getpid() { return os.getpid(); }
     public int getppid() { return os.getppid(); }
     public StructPasswd getpwnam(String name) throws ErrnoException { return os.getpwnam(name); }
@@ -74,6 +75,7 @@
     public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptInt(fd, level, option); }
     public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptLinger(fd, level, option); }
     public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptTimeval(fd, level, option); }
+    public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException { return os.getsockoptUcred(fd, level, option); }
     public int getuid() { return os.getuid(); }
     public String if_indextoname(int index) { return os.if_indextoname(index); }
     public InetAddress inet_pton(int family, String address) { return os.inet_pton(family, address); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 4aa839e..28bf9ea 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -56,6 +56,7 @@
     public String getenv(String name);
     /* TODO: break into getnameinfoHost and getnameinfoService? */
     public String getnameinfo(InetAddress address, int flags) throws GaiException;
+    public SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
     public int getpid();
     public int getppid();
     public StructPasswd getpwnam(String name) throws ErrnoException;
@@ -66,6 +67,7 @@
     public int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException;
     public StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException;
     public StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException;
+    public StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException;
     public int getuid();
     public String if_indextoname(int index);
     public InetAddress inet_pton(int family, String address);
diff --git a/luni/src/main/java/libcore/io/OsConstants.java b/luni/src/main/java/libcore/io/OsConstants.java
index 9f381f5..78df386 100644
--- a/luni/src/main/java/libcore/io/OsConstants.java
+++ b/luni/src/main/java/libcore/io/OsConstants.java
@@ -302,6 +302,8 @@
     public static final int SO_KEEPALIVE = placeholder();
     public static final int SO_LINGER = placeholder();
     public static final int SO_OOBINLINE = placeholder();
+    public static final int SO_PASSCRED = placeholder();
+    public static final int SO_PEERCRED = placeholder();
     public static final int SO_RCVBUF = placeholder();
     public static final int SO_RCVLOWAT = placeholder();
     public static final int SO_RCVTIMEO = placeholder();
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 27d3510..147750e 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -58,6 +58,7 @@
     public native int getgid();
     public native String getenv(String name);
     public native String getnameinfo(InetAddress address, int flags) throws GaiException;
+    public native SocketAddress getpeername(FileDescriptor fd) throws ErrnoException;
     public native int getpid();
     public native int getppid();
     public native StructPasswd getpwnam(String name) throws ErrnoException;
@@ -68,6 +69,7 @@
     public native int getsockoptInt(FileDescriptor fd, int level, int option) throws ErrnoException;
     public native StructLinger getsockoptLinger(FileDescriptor fd, int level, int option) throws ErrnoException;
     public native StructTimeval getsockoptTimeval(FileDescriptor fd, int level, int option) throws ErrnoException;
+    public native StructUcred getsockoptUcred(FileDescriptor fd, int level, int option) throws ErrnoException;
     public native int getuid();
     public native String if_indextoname(int index);
     public native InetAddress inet_pton(int family, String address);
diff --git a/luni/src/main/java/libcore/io/StructUcred.java b/luni/src/main/java/libcore/io/StructUcred.java
new file mode 100644
index 0000000..359995d
--- /dev/null
+++ b/luni/src/main/java/libcore/io/StructUcred.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.io;
+
+/**
+ * Corresponds to C's {@code struct ucred}.
+ */
+public final class StructUcred {
+  /** The peer's process id. */
+  public final int pid;
+
+  /** The peer process' uid. */
+  public final int uid;
+
+  /** The peer process' gid. */
+  public final int gid;
+
+  private StructUcred(int pid, int uid, int gid) {
+    this.pid = pid;
+    this.uid = uid;
+    this.gid = gid;
+  }
+
+  @Override public String toString() {
+    return "StructUcred[pid=" + pid + ",uid=" + uid + ",gid=" + gid + "]";
+  }
+}
diff --git a/luni/src/main/native/JniConstants.cpp b/luni/src/main/native/JniConstants.cpp
index 6b3c7bd..8693cf5 100644
--- a/luni/src/main/native/JniConstants.cpp
+++ b/luni/src/main/native/JniConstants.cpp
@@ -37,6 +37,7 @@
 jclass JniConstants::inet6AddressClass;
 jclass JniConstants::inetAddressClass;
 jclass JniConstants::inetSocketAddressClass;
+jclass JniConstants::inetUnixAddressClass;
 jclass JniConstants::inflaterClass;
 jclass JniConstants::inputStreamClass;
 jclass JniConstants::integerClass;
@@ -63,6 +64,7 @@
 jclass JniConstants::structStatClass;
 jclass JniConstants::structStatFsClass;
 jclass JniConstants::structTimevalClass;
+jclass JniConstants::structUcredClass;
 jclass JniConstants::structUtsnameClass;
 
 static jclass findClass(JNIEnv* env, const char* name) {
@@ -94,6 +96,7 @@
     inet6AddressClass = findClass(env, "java/net/Inet6Address");
     inetAddressClass = findClass(env, "java/net/InetAddress");
     inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
+    inetUnixAddressClass = findClass(env, "java/net/InetUnixAddress");
     inflaterClass = findClass(env, "java/util/zip/Inflater");
     inputStreamClass = findClass(env, "java/io/InputStream");
     integerClass = findClass(env, "java/lang/Integer");
@@ -120,5 +123,6 @@
     structStatClass = findClass(env, "libcore/io/StructStat");
     structStatFsClass = findClass(env, "libcore/io/StructStatFs");
     structTimevalClass = findClass(env, "libcore/io/StructTimeval");
+    structUcredClass = findClass(env, "libcore/io/StructUcred");
     structUtsnameClass = findClass(env, "libcore/io/StructUtsname");
 }
diff --git a/luni/src/main/native/JniConstants.h b/luni/src/main/native/JniConstants.h
index 414cef8..8fd7bc8 100644
--- a/luni/src/main/native/JniConstants.h
+++ b/luni/src/main/native/JniConstants.h
@@ -58,6 +58,7 @@
     static jclass inet6AddressClass;
     static jclass inetAddressClass;
     static jclass inetSocketAddressClass;
+    static jclass inetUnixAddressClass;
     static jclass inflaterClass;
     static jclass inputStreamClass;
     static jclass integerClass;
@@ -84,6 +85,7 @@
     static jclass structStatClass;
     static jclass structStatFsClass;
     static jclass structTimevalClass;
+    static jclass structUcredClass;
     static jclass structUtsnameClass;
 };
 
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
index 9f4d770..de07201 100644
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -26,45 +26,50 @@
 #include <stdio.h>
 #include <string.h>
 #include <sys/socket.h>
+#include <sys/un.h>
 
-jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, jint* port) {
+jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, jint* port) {
     // Convert IPv4-mapped IPv6 addresses to IPv4 addresses.
     // The RI states "Java will never return an IPv4-mapped address".
-    sockaddr_storage tmp;
-    memset(&tmp, 0, sizeof(tmp));
-    const sockaddr_in6* sin6 = reinterpret_cast<const sockaddr_in6*>(ss);
-    if (ss->ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
+    const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss);
+    if (ss.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&sin6.sin6_addr)) {
         // Copy the IPv6 address into the temporary sockaddr_storage.
-        memcpy(&tmp, ss, sizeof(sockaddr_in6));
+        sockaddr_storage tmp;
+        memset(&tmp, 0, sizeof(tmp));
+        memcpy(&tmp, &ss, sizeof(sockaddr_in6));
         // Unmap it into an IPv4 address.
-        sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(&tmp);
-        sin->sin_family = AF_INET;
-        sin->sin_port = sin6->sin6_port;
-        memcpy(&sin->sin_addr.s_addr, &sin6->sin6_addr.s6_addr[12], 4);
-        // Fall through into the regular conversion using the unmapped address.
-        ss = &tmp;
+        sockaddr_in& sin = reinterpret_cast<sockaddr_in&>(tmp);
+        sin.sin_family = AF_INET;
+        sin.sin_port = sin6.sin6_port;
+        memcpy(&sin.sin_addr.s_addr, &sin6.sin6_addr.s6_addr[12], 4);
+        // Do the regular conversion using the unmapped address.
+        return sockaddrToInetAddress(env, tmp, port);
     }
 
     const void* rawAddress;
     size_t addressLength;
-    int sin_port;
+    int sin_port = 0;
     int scope_id = 0;
-    if (ss->ss_family == AF_INET) {
-        const sockaddr_in* sin = reinterpret_cast<const sockaddr_in*>(ss);
-        rawAddress = &sin->sin_addr.s_addr;
+    if (ss.ss_family == AF_INET) {
+        const sockaddr_in& sin = reinterpret_cast<const sockaddr_in&>(ss);
+        rawAddress = &sin.sin_addr.s_addr;
         addressLength = 4;
-        sin_port = ntohs(sin->sin_port);
-    } else if (ss->ss_family == AF_INET6) {
-        const sockaddr_in6* sin6 = reinterpret_cast<const sockaddr_in6*>(ss);
-        rawAddress = &sin6->sin6_addr.s6_addr;
+        sin_port = ntohs(sin.sin_port);
+    } else if (ss.ss_family == AF_INET6) {
+        const sockaddr_in6& sin6 = reinterpret_cast<const sockaddr_in6&>(ss);
+        rawAddress = &sin6.sin6_addr.s6_addr;
         addressLength = 16;
-        sin_port = ntohs(sin6->sin6_port);
-        scope_id = sin6->sin6_scope_id;
+        sin_port = ntohs(sin6.sin6_port);
+        scope_id = sin6.sin6_scope_id;
+    } else if (ss.ss_family == AF_UNIX) {
+        const sockaddr_un& sun = reinterpret_cast<const sockaddr_un&>(ss);
+        rawAddress = &sun.sun_path;
+        addressLength = strlen(sun.sun_path);
     } else {
         // We can't throw SocketException. We aren't meant to see bad addresses, so seeing one
         // really does imply an internal error.
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "sockaddrToInetAddress bad ss_family: %i", ss->ss_family);
+                             "sockaddrToInetAddress unsupported ss_family: %i", ss.ss_family);
         return NULL;
     }
     if (port != NULL) {
@@ -78,6 +83,14 @@
     env->SetByteArrayRegion(byteArray.get(), 0, addressLength,
             reinterpret_cast<const jbyte*>(rawAddress));
 
+    if (ss.ss_family == AF_UNIX) {
+        // Note that we get here for AF_UNIX sockets on accept(2). The unix(7) man page claims
+        // that the peer's sun_path will contain the path, but in practice it doesn't, and the
+        // peer length is returned as 2 (meaning only the sun_family field was set).
+        static jmethodID ctor = env->GetMethodID(JniConstants::inetUnixAddressClass, "<init>", "([B)V");
+        return env->NewObject(JniConstants::inetUnixAddressClass, ctor, byteArray.get());
+    }
+
     static jmethodID getByAddressMethod = env->GetStaticMethodID(JniConstants::inetAddressClass,
             "getByAddress", "(Ljava/lang/String;[BI)Ljava/net/InetAddress;");
     if (getByAddressMethod == NULL) {
@@ -87,8 +100,9 @@
             NULL, byteArray.get(), scope_id);
 }
 
-static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss, bool map) {
-    memset(ss, 0, sizeof(*ss));
+static bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage& ss, socklen_t& sa_len, bool map) {
+    memset(&ss, 0, sizeof(ss));
+    sa_len = 0;
 
     if (inetAddress == NULL) {
         jniThrowNullPointerException(env, NULL);
@@ -97,15 +111,15 @@
 
     // Get the address family.
     static jfieldID familyFid = env->GetFieldID(JniConstants::inetAddressClass, "family", "I");
-    ss->ss_family = env->GetIntField(inetAddress, familyFid);
-    if (ss->ss_family == AF_UNSPEC) {
+    ss.ss_family = env->GetIntField(inetAddress, familyFid);
+    if (ss.ss_family == AF_UNSPEC) {
         return true; // Job done!
     }
 
     // Check this is an address family we support.
-    if (ss->ss_family != AF_INET && ss->ss_family != AF_INET6) {
+    if (ss.ss_family != AF_INET && ss.ss_family != AF_INET6 && ss.ss_family != AF_UNIX) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
-                "inetAddressToSockaddr bad family: %i", ss->ss_family);
+                "inetAddressToSockaddr bad family: %i", ss.ss_family);
         return false;
     }
 
@@ -117,16 +131,42 @@
         return false;
     }
 
+    // Handle the AF_UNIX special case.
+    if (ss.ss_family == AF_UNIX) {
+        sockaddr_un& sun = reinterpret_cast<sockaddr_un&>(ss);
+
+        size_t path_length = env->GetArrayLength(addressBytes.get());
+        if (path_length >= sizeof(sun.sun_path)) {
+            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                                 "inetAddressToSockaddr path too long for AF_UNIX: %i", path_length);
+            return false;
+        }
+
+        // Copy the bytes...
+        jbyte* dst = reinterpret_cast<jbyte*>(&sun.sun_path);
+        memset(dst, 0, sizeof(sun.sun_path));
+        env->GetByteArrayRegion(addressBytes.get(), 0, path_length, dst);
+        sa_len = sizeof(sun.sun_path);
+        return true;
+    }
+
+    // TODO: bionic's getnameinfo(3) seems to want its length parameter to be exactly
+    // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
+    // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
+    // then unconditionally set sa_len to sizeof(sockaddr_storage) instead of having
+    // to deal with this case by case.
+
     // We use AF_INET6 sockets, so we want an IPv6 address (which may be a IPv4-mapped address).
-    sockaddr_in6* sin6 = reinterpret_cast<sockaddr_in6*>(ss);
-    sin6->sin6_port = htons(port);
-    if (ss->ss_family == AF_INET6) {
+    sockaddr_in6& sin6 = reinterpret_cast<sockaddr_in6&>(ss);
+    sin6.sin6_port = htons(port);
+    if (ss.ss_family == AF_INET6) {
         // IPv6 address. Copy the bytes...
-        jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr);
+        jbyte* dst = reinterpret_cast<jbyte*>(&sin6.sin6_addr.s6_addr);
         env->GetByteArrayRegion(addressBytes.get(), 0, 16, dst);
         // ...and set the scope id...
         static jfieldID scopeFid = env->GetFieldID(JniConstants::inet6AddressClass, "scope_id", "I");
-        sin6->sin6_scope_id = env->GetIntField(inetAddress, scopeFid);
+        sin6.sin6_scope_id = env->GetIntField(inetAddress, scopeFid);
+        sa_len = sizeof(sockaddr_in6);
         return true;
     }
 
@@ -134,31 +174,33 @@
     if (map) {
         // We should represent this Inet4Address as an IPv4-mapped IPv6 sockaddr_in6.
         // Change the family...
-        sin6->sin6_family = AF_INET6;
+        sin6.sin6_family = AF_INET6;
         // Copy the bytes...
-        jbyte* dst = reinterpret_cast<jbyte*>(&sin6->sin6_addr.s6_addr[12]);
+        jbyte* dst = reinterpret_cast<jbyte*>(&sin6.sin6_addr.s6_addr[12]);
         env->GetByteArrayRegion(addressBytes.get(), 0, 4, dst);
         // INADDR_ANY and in6addr_any are both all-zeros...
-        if (!IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+        if (!IN6_IS_ADDR_UNSPECIFIED(&sin6.sin6_addr)) {
             // ...but all other IPv4-mapped addresses are ::ffff:a.b.c.d, so insert the ffff...
-            memset(&(sin6->sin6_addr.s6_addr[10]), 0xff, 2);
+            memset(&(sin6.sin6_addr.s6_addr[10]), 0xff, 2);
         }
+        sa_len = sizeof(sockaddr_in6);
     } else {
         // We should represent this Inet4Address as an IPv4 sockaddr_in.
-        sockaddr_in* sin = reinterpret_cast<sockaddr_in*>(ss);
-        sin->sin_port = htons(port);
-        jbyte* dst = reinterpret_cast<jbyte*>(&sin->sin_addr.s_addr);
+        sockaddr_in& sin = reinterpret_cast<sockaddr_in&>(ss);
+        sin.sin_port = htons(port);
+        jbyte* dst = reinterpret_cast<jbyte*>(&sin.sin_addr.s_addr);
         env->GetByteArrayRegion(addressBytes.get(), 0, 4, dst);
+        sa_len = sizeof(sockaddr_in);
     }
     return true;
 }
 
-bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss) {
-    return inetAddressToSockaddr(env, inetAddress, port, ss, false);
+bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage& ss, socklen_t& sa_len) {
+    return inetAddressToSockaddr(env, inetAddress, port, ss, sa_len, false);
 }
 
-bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss) {
-    return inetAddressToSockaddr(env, inetAddress, port, ss, true);
+bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage& ss, socklen_t& sa_len) {
+    return inetAddressToSockaddr(env, inetAddress, port, ss, sa_len, true);
 }
 
 bool setBlocking(int fd, bool blocking) {
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
index 1ae6c53..28e9fa5 100644
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -17,20 +17,27 @@
 #include "jni.h"
 #include <sys/socket.h>
 
-// Convert from sockaddr_storage to InetAddress and an optional int port.
-jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage* ss, int* port);
+// Convert from sockaddr_storage to Inet4Address (AF_INET), Inet6Address (AF_INET6), or
+// InetUnixAddress (AF_UNIX). If 'port' is non-NULL and the address family includes a notion
+// of port number, *port will be set to the port number.
+jobject sockaddrToInetAddress(JNIEnv* env, const sockaddr_storage& ss, int* port);
 
-// Convert from InetAddress to sockaddr_storage. An Inet4Address will be converted to
-// an IPv4-mapped AF_INET6 sockaddr_in6. This is what you want if you're about to perform an
-// operation on a socket, since all our sockets are AF_INET6.
-bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss);
+// Convert from InetAddress to sockaddr_storage. An InetUnixAddress will be converted to
+// an AF_UNIX sockaddr_un. An Inet6Address will be converted to an AF_INET6 sockaddr_in6.
+// An Inet4Address will be converted to an IPv4-mapped AF_INET6 sockaddr_in6. This is what
+// you want if you're about to perform an operation on a socket, since all our sockets
+// are AF_INET6.
+bool inetAddressToSockaddr(JNIEnv* env, jobject inetAddress, int port,
+                           sockaddr_storage& ss, socklen_t& sa_len);
 
-// Convert from InetAddress to sockaddr_storage. An Inet6Address will be converted to
-// a sockaddr_in6 while an Inet4Address will be converted to a sockaddr_in. This is
-// probably only useful for getnameinfo(2), where we'll be presenting the result to
-// the user and the user may actually care whether the original address was pure IPv4
-// or an IPv4-mapped IPv6 address, and for the MCAST_JOIN_GROUP socket option.
-bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port, sockaddr_storage* ss);
+// Convert from InetAddress to sockaddr_storage. An InetUnixAddress will be converted to
+// an AF_UNIX sockaddr_un. An Inet6Address will be converted to an AF_INET6 sockaddr_in6.
+// An Inet4Address will be converted to a sockaddr_in. This is probably only useful for
+// getnameinfo(2), where we'll be presenting the result to the user and the user may actually
+// care whether the original address was pure IPv4 or an IPv4-mapped IPv6 address, and
+// for the MCAST_JOIN_GROUP socket option.
+bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port,
+                                   sockaddr_storage& ss, socklen_t& sa_len);
 
 
 
diff --git a/luni/src/main/native/libcore_io_OsConstants.cpp b/luni/src/main/native/libcore_io_OsConstants.cpp
index 7247a38..e23fc8b 100644
--- a/luni/src/main/native/libcore_io_OsConstants.cpp
+++ b/luni/src/main/native/libcore_io_OsConstants.cpp
@@ -357,6 +357,8 @@
     initConstant(env, c, "SO_KEEPALIVE", SO_KEEPALIVE);
     initConstant(env, c, "SO_LINGER", SO_LINGER);
     initConstant(env, c, "SO_OOBINLINE", SO_OOBINLINE);
+    initConstant(env, c, "SO_PASSCRED", SO_PASSCRED);
+    initConstant(env, c, "SO_PEERCRED", SO_PEERCRED);
     initConstant(env, c, "SO_RCVBUF", SO_RCVBUF);
     initConstant(env, c, "SO_RCVLOWAT", SO_RCVLOWAT);
     initConstant(env, c, "SO_RCVTIMEO", SO_RCVTIMEO);
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 336c982..e73b990 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -211,8 +211,7 @@
     std::vector<ScopedT*> mScopedBuffers;
 };
 
-static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage* ss) {
-    // TODO: support AF_UNIX and AF_UNSPEC (and other families?)
+static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss) {
     jint port;
     jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
     if (inetAddress == NULL) {
@@ -279,6 +278,11 @@
             static_cast<jlong>(tv.tv_sec), static_cast<jlong>(tv.tv_usec));
 }
 
+static jobject makeStructUcred(JNIEnv* env, const struct ucred& u) {
+  static jmethodID ctor = env->GetMethodID(JniConstants::structUcredClass, "<init>", "(III)V");
+  return env->NewObject(JniConstants::structUcredClass, ctor, u.pid, u.uid, u.gid);
+}
+
 static jobject makeStructUtsname(JNIEnv* env, const struct utsname& buf) {
     TO_JAVA_STRING(sysname, buf.sysname);
     TO_JAVA_STRING(nodename, buf.nodename);
@@ -302,7 +306,7 @@
     return true;
 }
 
-static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage* ss) {
+static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage& ss) {
     if (rc == -1 || javaInetSocketAddress == NULL) {
         return true;
     }
@@ -334,6 +338,21 @@
     return makeStructStat(env, sb);
 }
 
+static jobject doGetSockName(JNIEnv* env, jobject javaFd, bool is_sockname) {
+  int fd = jniGetFDFromFileDescriptor(env, javaFd);
+  sockaddr_storage ss;
+  sockaddr* sa = reinterpret_cast<sockaddr*>(&ss);
+  socklen_t byteCount = sizeof(ss);
+  memset(&ss, 0, byteCount);
+  int rc = is_sockname ? TEMP_FAILURE_RETRY(getsockname(fd, sa, &byteCount))
+      : TEMP_FAILURE_RETRY(getpeername(fd, sa, &byteCount));
+  if (rc == -1) {
+    throwErrnoException(env, is_sockname ? "getsockname" : "getpeername");
+    return NULL;
+  }
+  return makeSocketAddress(env, ss);
+}
+
 class Passwd {
 public:
     Passwd(JNIEnv* env) : mEnv(env), mResult(NULL) {
@@ -382,7 +401,7 @@
     sockaddr* peer = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
     socklen_t* peerLength = (javaInetSocketAddress != NULL) ? &sl : 0;
     jint clientFd = NET_FAILURE_RETRY(env, int, accept, javaFd, peer, peerLength);
-    if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, &ss)) {
+    if (clientFd == -1 || !fillInetSocketAddress(env, clientFd, javaInetSocketAddress, ss)) {
         close(clientFd);
         return NULL;
     }
@@ -403,12 +422,13 @@
 
 static void Posix_bind(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
     sockaddr_storage ss;
-    if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
+    socklen_t sa_len;
+    if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
         return;
     }
     const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
     // We don't need the return value because we'll already have thrown.
-    (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sizeof(sockaddr_storage));
+    (void) NET_FAILURE_RETRY(env, int, bind, javaFd, sa, sa_len);
 }
 
 static void Posix_chmod(JNIEnv* env, jobject, jstring javaPath, jint mode) {
@@ -441,12 +461,13 @@
 
 static void Posix_connect(JNIEnv* env, jobject, jobject javaFd, jobject javaAddress, jint port) {
     sockaddr_storage ss;
-    if (!inetAddressToSockaddr(env, javaAddress, port, &ss)) {
+    socklen_t sa_len;
+    if (!inetAddressToSockaddr(env, javaAddress, port, ss, sa_len)) {
         return;
     }
     const sockaddr* sa = reinterpret_cast<const sockaddr*>(&ss);
     // We don't need the return value because we'll already have thrown.
-    (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sizeof(sockaddr_storage));
+    (void) NET_FAILURE_RETRY(env, int, connect, javaFd, sa, sa_len);
 }
 
 static jobject Posix_dup(JNIEnv* env, jobject, jobject javaOldFd) {
@@ -638,7 +659,7 @@
         }
 
         // Convert each IP address into a Java byte array.
-        sockaddr_storage* address = reinterpret_cast<sockaddr_storage*>(ai->ai_addr);
+        sockaddr_storage& address = *reinterpret_cast<sockaddr_storage*>(ai->ai_addr);
         ScopedLocalRef<jobject> inetAddress(env, sockaddrToInetAddress(env, address, NULL));
         if (inetAddress.get() == NULL) {
             return NULL;
@@ -671,17 +692,13 @@
 
 static jstring Posix_getnameinfo(JNIEnv* env, jobject, jobject javaAddress, jint flags) {
     sockaddr_storage ss;
-    if (!inetAddressToSockaddrVerbatim(env, javaAddress, 0, &ss)) {
+    socklen_t sa_len;
+    if (!inetAddressToSockaddrVerbatim(env, javaAddress, 0, ss, sa_len)) {
         return NULL;
     }
-    // TODO: bionic's getnameinfo(3) seems to want its length parameter to be exactly
-    // sizeof(sockaddr_in) for an IPv4 address and sizeof (sockaddr_in6) for an
-    // IPv6 address. Fix getnameinfo so it accepts sizeof(sockaddr_storage), and
-    // then remove this hack.
-    socklen_t size = (ss.ss_family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6);
     char buf[NI_MAXHOST]; // NI_MAXHOST is longer than INET6_ADDRSTRLEN.
     errno = 0;
-    int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), size, buf, sizeof(buf), NULL, 0, flags);
+    int rc = getnameinfo(reinterpret_cast<sockaddr*>(&ss), sa_len, buf, sizeof(buf), NULL, 0, flags);
     if (rc != 0) {
         throwGaiException(env, "getnameinfo", rc);
         return NULL;
@@ -689,6 +706,10 @@
     return env->NewStringUTF(buf);
 }
 
+static jobject Posix_getpeername(JNIEnv* env, jobject, jobject javaFd) {
+  return doGetSockName(env, javaFd, false);
+}
+
 static jint Posix_getpid(JNIEnv*, jobject) {
     return getpid();
 }
@@ -710,17 +731,7 @@
 }
 
 static jobject Posix_getsockname(JNIEnv* env, jobject, jobject javaFd) {
-    int fd = jniGetFDFromFileDescriptor(env, javaFd);
-    sockaddr_storage ss;
-    sockaddr* sa = reinterpret_cast<sockaddr*>(&ss);
-    socklen_t byteCount = sizeof(ss);
-    memset(&ss, 0, byteCount);
-    int rc = TEMP_FAILURE_RETRY(getsockname(fd, sa, &byteCount));
-    if (rc == -1) {
-        throwErrnoException(env, "getsockname");
-        return NULL;
-    }
-    return makeSocketAddress(env, &ss);
+  return doGetSockName(env, javaFd, true);
 }
 
 static jint Posix_getsockoptByte(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) {
@@ -743,7 +754,7 @@
         throwErrnoException(env, "getsockopt");
         return NULL;
     }
-    return sockaddrToInetAddress(env, &ss, NULL);
+    return sockaddrToInetAddress(env, ss, NULL);
 }
 
 static jint Posix_getsockoptInt(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) {
@@ -780,6 +791,19 @@
     return makeStructTimeval(env, tv);
 }
 
+static jobject Posix_getsockoptUcred(JNIEnv* env, jobject, jobject javaFd, jint level, jint option) {
+  int fd = jniGetFDFromFileDescriptor(env, javaFd);
+  struct ucred u;
+  socklen_t size = sizeof(u);
+  memset(&u, 0, size);
+  int rc = TEMP_FAILURE_RETRY(getsockopt(fd, level, option, &u, &size));
+  if (rc == -1) {
+    throwErrnoException(env, "getsockopt");
+    return NULL;
+  }
+  return makeStructUcred(env, u);
+}
+
 static jint Posix_getuid(JNIEnv*, jobject) {
     return getuid();
 }
@@ -805,7 +829,7 @@
         return NULL;
     }
     ss.ss_family = family;
-    return sockaddrToInetAddress(env, &ss, NULL);
+    return sockaddrToInetAddress(env, ss, NULL);
 }
 
 static jobject Posix_ioctlInetAddress(JNIEnv* env, jobject, jobject javaFd, jint cmd, jstring javaInterfaceName) {
@@ -818,7 +842,7 @@
     if (rc == -1) {
         return NULL;
     }
-    return sockaddrToInetAddress(env, reinterpret_cast<sockaddr_storage*>(&req.ifr_addr), NULL);
+    return sockaddrToInetAddress(env, reinterpret_cast<sockaddr_storage&>(req.ifr_addr), NULL);
 }
 
 static jint Posix_ioctlInt(JNIEnv* env, jobject, jobject javaFd, jint cmd, jobject javaArg) {
@@ -1039,7 +1063,7 @@
     sockaddr* from = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
     socklen_t* fromLength = (javaInetSocketAddress != NULL) ? &sl : 0;
     jint recvCount = NET_FAILURE_RETRY(env, ssize_t, recvfrom, javaFd, bytes.get() + byteOffset, byteCount, flags, from, fromLength);
-    fillInetSocketAddress(env, recvCount, javaInetSocketAddress, &ss);
+    fillInetSocketAddress(env, recvCount, javaInetSocketAddress, ss);
     return recvCount;
 }
 
@@ -1087,12 +1111,12 @@
         return -1;
     }
     sockaddr_storage ss;
-    if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, &ss)) {
+    socklen_t sa_len = 0;
+    if (javaInetAddress != NULL && !inetAddressToSockaddr(env, javaInetAddress, port, ss, sa_len)) {
         return -1;
     }
     const sockaddr* to = (javaInetAddress != NULL) ? reinterpret_cast<const sockaddr*>(&ss) : NULL;
-    socklen_t toLength = (javaInetAddress != NULL) ? sizeof(ss) : 0;
-    return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, toLength);
+    return NET_FAILURE_RETRY(env, ssize_t, sendto, javaFd, bytes.get() + byteOffset, byteCount, flags, to, sa_len);
 }
 
 static void Posix_setegid(JNIEnv* env, jobject, jint egid) {
@@ -1165,7 +1189,8 @@
     // Get the IPv4 or IPv6 multicast address to join or leave.
     static jfieldID grGroupFid = env->GetFieldID(JniConstants::structGroupReqClass, "gr_group", "Ljava/net/InetAddress;");
     ScopedLocalRef<jobject> javaGroup(env, env->GetObjectField(javaGroupReq, grGroupFid));
-    if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, &req.gr_group)) {
+    socklen_t sa_len;
+    if (!inetAddressToSockaddrVerbatim(env, javaGroup.get(), 0, req.gr_group, sa_len)) {
         return;
     }
 
@@ -1365,6 +1390,7 @@
     NATIVE_METHOD(Posix, getgid, "()I"),
     NATIVE_METHOD(Posix, getenv, "(Ljava/lang/String;)Ljava/lang/String;"),
     NATIVE_METHOD(Posix, getnameinfo, "(Ljava/net/InetAddress;I)Ljava/lang/String;"),
+    NATIVE_METHOD(Posix, getpeername, "(Ljava/io/FileDescriptor;)Ljava/net/SocketAddress;"),
     NATIVE_METHOD(Posix, getpid, "()I"),
     NATIVE_METHOD(Posix, getppid, "()I"),
     NATIVE_METHOD(Posix, getpwnam, "(Ljava/lang/String;)Llibcore/io/StructPasswd;"),
@@ -1375,6 +1401,7 @@
     NATIVE_METHOD(Posix, getsockoptInt, "(Ljava/io/FileDescriptor;II)I"),
     NATIVE_METHOD(Posix, getsockoptLinger, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructLinger;"),
     NATIVE_METHOD(Posix, getsockoptTimeval, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructTimeval;"),
+    NATIVE_METHOD(Posix, getsockoptUcred, "(Ljava/io/FileDescriptor;II)Llibcore/io/StructUcred;"),
     NATIVE_METHOD(Posix, getuid, "()I"),
     NATIVE_METHOD(Posix, if_indextoname, "(I)Ljava/lang/String;"),
     NATIVE_METHOD(Posix, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"),
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index e6446ae..a15bbdd 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -17,21 +17,116 @@
 package libcore.io;
 
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.InetUnixAddress;
 import java.net.ServerSocket;
+import java.net.SocketAddress;
 import junit.framework.TestCase;
 
 import static libcore.io.OsConstants.*;
 
 public class OsTest extends TestCase {
-    public void testIsSocket() throws Exception {
-        File f = new File("/dev/null");
-        FileInputStream fis = new FileInputStream(f);
-        assertFalse(S_ISSOCK(Libcore.os.fstat(fis.getFD()).st_mode));
-        fis.close();
+  public void testIsSocket() throws Exception {
+    File f = new File("/dev/null");
+    FileInputStream fis = new FileInputStream(f);
+    assertFalse(S_ISSOCK(Libcore.os.fstat(fis.getFD()).st_mode));
+    fis.close();
 
-        ServerSocket s = new ServerSocket();
-        assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl$().getFD$()).st_mode));
-        s.close();
+    ServerSocket s = new ServerSocket();
+    assertTrue(S_ISSOCK(Libcore.os.fstat(s.getImpl$().getFD$()).st_mode));
+    s.close();
+  }
+
+  public void testUnixDomainSockets_in_file_system() throws Exception {
+    String path = System.getProperty("java.io.tmpdir") + "/test_unix_socket";
+    new File(path).delete();
+    checkUnixDomainSocket(new InetUnixAddress(path), false);
+  }
+
+  public void testUnixDomainSocket_abstract_name() throws Exception {
+    // Linux treats a sun_path starting with a NUL byte as an abstract name. See unix(7).
+    byte[] path = "/abstract_name_unix_socket".getBytes("UTF-8");
+    path[0] = 0;
+    checkUnixDomainSocket(new InetUnixAddress(path), true);
+  }
+
+  private void checkUnixDomainSocket(final InetUnixAddress address, final boolean isAbstract) throws Exception {
+    final FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+    Libcore.os.bind(serverFd, address, 0);
+    Libcore.os.listen(serverFd, 5);
+
+    checkSockName(serverFd, isAbstract, address);
+
+    Thread server = new Thread(new Runnable() {
+      public void run() {
+        try {
+          InetSocketAddress peerAddress = new InetSocketAddress();
+          FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
+          checkSockName(clientFd, isAbstract, address);
+          checkNoName(peerAddress);
+
+          checkNoPeerName(clientFd);
+
+          StructUcred credentials = Libcore.os.getsockoptUcred(clientFd, SOL_SOCKET, SO_PEERCRED);
+          assertEquals(Libcore.os.getpid(), credentials.pid);
+          assertEquals(Libcore.os.getuid(), credentials.uid);
+          assertEquals(Libcore.os.getgid(), credentials.gid);
+
+          byte[] request = new byte[256];
+          int requestLength = Libcore.os.read(clientFd, request, 0, request.length);
+
+          String s = new String(request, "UTF-8");
+          byte[] response = s.toUpperCase().getBytes("UTF-8");
+          Libcore.os.write(clientFd, response, 0, response.length);
+
+          Libcore.os.close(clientFd);
+        } catch (Exception ex) {
+          throw new RuntimeException(ex);
+        }
+      }
+    });
+    server.start();
+
+    FileDescriptor clientFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+
+    Libcore.os.connect(clientFd, address, 0);
+    checkNoSockName(clientFd);
+
+    String string = "hello, world!";
+
+    byte[] request = string.getBytes("UTF-8");
+    assertEquals(request.length, Libcore.os.write(clientFd, request, 0, request.length));
+
+    byte[] response = new byte[request.length];
+    assertEquals(response.length, Libcore.os.read(clientFd, response, 0, response.length));
+
+    assertEquals(string.toUpperCase(), new String(response, "UTF-8"));
+
+    Libcore.os.close(clientFd);
+  }
+
+  private void checkSockName(FileDescriptor fd, boolean isAbstract, InetAddress address) throws Exception {
+    InetSocketAddress isa = (InetSocketAddress) Libcore.os.getsockname(fd);
+    if (isAbstract) {
+      checkNoName(isa);
+    } else {
+      assertEquals(address, isa.getAddress());
     }
+  }
+
+  private void checkNoName(SocketAddress sa) {
+    InetSocketAddress isa = (InetSocketAddress) sa;
+    assertEquals(0, isa.getAddress().getAddress().length);
+  }
+
+  private void checkNoPeerName(FileDescriptor fd) throws Exception {
+    checkNoName(Libcore.os.getpeername(fd));
+  }
+
+  private void checkNoSockName(FileDescriptor fd) throws Exception {
+    checkNoName(Libcore.os.getsockname(fd));
+  }
 }