Rearrange how AF_UNIX sockets are handled for accept()

Contains the following changes:
1) Introduction of UnixSocketAddress / removal of InetUnixAddress.
2) Os/Posix classes now take SocketAddress instead of InetSocketAddress.
3) Removal of native code that dealt with InetUnixAddress.
4) Introduction of OsConstants.UNIX_PATH_MAX to hold the maximum sun_path
size.
5) Update tests.

Bug: 3106438
Change-Id: I47a3618387f21599bfbfd0dc9a821839bf47b39b
diff --git a/luni/src/main/java/android/system/Os.java b/luni/src/main/java/android/system/Os.java
index a1e87c8..2e8d37f 100644
--- a/luni/src/main/java/android/system/Os.java
+++ b/luni/src/main/java/android/system/Os.java
@@ -43,6 +43,12 @@
   public static FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
 
   /**
+   * TODO Change the public API by removing the overload above and unhiding this version.
+   * @hide
+   */
+  public static FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { return Libcore.os.accept(fd, peerAddress); }
+
+  /**
    * See <a href="http://man7.org/linux/man-pages/man2/access.2.html">access(2)</a>.
    */
   public static boolean access(String path, int mode) throws ErrnoException { return Libcore.os.access(path, mode); }
diff --git a/luni/src/main/java/android/system/OsConstants.java b/luni/src/main/java/android/system/OsConstants.java
index 80ef00d..291ccdf 100644
--- a/luni/src/main/java/android/system/OsConstants.java
+++ b/luni/src/main/java/android/system/OsConstants.java
@@ -490,6 +490,7 @@
     public static final int S_IXUSR = placeholder();
     public static final int TCP_NODELAY = placeholder();
     /** @hide */ public static final int TIOCOUTQ = placeholder();
+    /** @hide */ public static final int UNIX_PATH_MAX = placeholder();
     public static final int WCONTINUED = placeholder();
     public static final int WEXITED = placeholder();
     public static final int WNOHANG = placeholder();
diff --git a/luni/src/main/java/android/system/UnixSocketAddress.java b/luni/src/main/java/android/system/UnixSocketAddress.java
new file mode 100644
index 0000000..5c9369d
--- /dev/null
+++ b/luni/src/main/java/android/system/UnixSocketAddress.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2007 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 android.system;
+
+import java.net.SocketAddress;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * A UNIX-domain (AF_UNIX / AF_LOCAL) socket address.
+ *
+ * @hide
+ */
+public final class UnixSocketAddress extends SocketAddress {
+
+    private static final int NAMED_PATH_LENGTH = OsConstants.UNIX_PATH_MAX;
+    private static final byte[] UNNAMED_PATH = new byte[0];
+
+    // See unix(7): Three types of UnixSocketAddress:
+    // 1) pathname: sun_path.length == NAMED_PATH_LENGTH, sun_path[0] != 0.
+    // 2) unnamed: sun_path = [].
+    // 3) abstract: sun_path.length == NAMED_PATH_LENGTH, sun_path[0] == 0.
+    private byte[] sun_path;
+
+    /** This constructor is also used from JNI. */
+    private UnixSocketAddress(byte[] sun_path) {
+        if (sun_path == null) {
+            throw new IllegalArgumentException("sun_path must not be null");
+        }
+        if (sun_path.length > NAMED_PATH_LENGTH) {
+            throw new IllegalArgumentException("sun_path exceeds the maximum length");
+        }
+
+        if (sun_path.length == 0) {
+            this.sun_path = UNNAMED_PATH;
+        } else {
+            this.sun_path = new byte[NAMED_PATH_LENGTH];
+            System.arraycopy(sun_path, 0, this.sun_path, 0, sun_path.length);
+        }
+    }
+
+    /**
+     * Creates a named, abstract AF_UNIX socket address.
+     */
+    public static UnixSocketAddress createAbstract(String name) {
+        byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
+        // Abstract sockets have a path that starts with (byte) 0.
+        byte[] path = new byte[nameBytes.length + 1];
+        System.arraycopy(nameBytes, 0, path, 1, nameBytes.length);
+        return new UnixSocketAddress(path);
+    }
+
+    /**
+     * Creates a named, filesystem AF_UNIX socket address.
+     */
+    public static UnixSocketAddress createFileSystem(String path) {
+        return new UnixSocketAddress(path.getBytes(StandardCharsets.UTF_8));
+    }
+
+    /**
+     * Creates an unnamed, filesystem AF_UNIX socket address.
+     */
+    public static UnixSocketAddress createUnnamed() {
+        return new UnixSocketAddress(UNNAMED_PATH);
+    }
+
+    /** Used for testing. */
+    public byte[] getSunPath() {
+        if (sun_path.length == 0) {
+            return sun_path;
+        }
+        byte[] sunPathCopy = new byte[sun_path.length];
+        System.arraycopy(sun_path, 0, sunPathCopy, 0, sun_path.length);
+        return sunPathCopy;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        UnixSocketAddress that = (UnixSocketAddress) o;
+        return Arrays.equals(sun_path, that.sun_path);
+    }
+
+    @Override
+    public int hashCode() {
+        return Arrays.hashCode(sun_path);
+    }
+
+    @Override
+    public String toString() {
+        return "UnixSocketAddress{" +
+                "sun_path=" + Arrays.toString(sun_path) +
+                '}';
+    }
+}
diff --git a/luni/src/main/java/java/net/InetUnixAddress.java b/luni/src/main/java/java/net/InetUnixAddress.java
deleted file mode 100644
index 51236e2..0000000
--- a/luni/src/main/java/java/net/InetUnixAddress.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.StandardCharsets;
-
-import static android.system.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(StandardCharsets.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, StandardCharsets.UTF_8) + "]";
-  }
-}
diff --git a/luni/src/main/java/libcore/io/BlockGuardOs.java b/luni/src/main/java/libcore/io/BlockGuardOs.java
index 532493a..0923c85 100644
--- a/luni/src/main/java/libcore/io/BlockGuardOs.java
+++ b/luni/src/main/java/libcore/io/BlockGuardOs.java
@@ -28,6 +28,7 @@
 import java.io.InterruptedIOException;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
+import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
 import static android.system.OsConstants.*;
@@ -58,7 +59,7 @@
         }
     }
 
-    @Override public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException {
+    @Override public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException {
         BlockGuard.getThreadPolicy().onNetwork();
         return tagSocket(os.accept(fd, peerAddress));
     }
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index cb77573..758b4bd 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -50,7 +50,7 @@
         this.os = os;
     }
 
-    public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
+    public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException { return os.accept(fd, peerAddress); }
     public boolean access(String path, int mode) throws ErrnoException { return os.access(path, mode); }
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException { return os.android_getaddrinfo(node, hints, netId); }
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException { os.bind(fd, address, port); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 6d28b95..36ff6ce 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -41,7 +41,7 @@
 import java.nio.ByteBuffer;
 
 public interface Os {
-    public FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
+    public FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException;
     public boolean access(String path, int mode) throws ErrnoException;
     public InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index 151809d..09ee313 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -44,7 +44,7 @@
 public final class Posix implements Os {
     Posix() { }
 
-    public native FileDescriptor accept(FileDescriptor fd, InetSocketAddress peerAddress) throws ErrnoException, SocketException;
+    public native FileDescriptor accept(FileDescriptor fd, SocketAddress peerAddress) throws ErrnoException, SocketException;
     public native boolean access(String path, int mode) throws ErrnoException;
     public native InetAddress[] android_getaddrinfo(String node, StructAddrinfo hints, int netId) throws GaiException;
     public native void bind(FileDescriptor fd, InetAddress address, int port) throws ErrnoException, SocketException;
diff --git a/luni/src/main/native/NetworkUtilities.cpp b/luni/src/main/native/NetworkUtilities.cpp
index 7215306..f2d0f8a 100644
--- a/luni/src/main/native/NetworkUtilities.cpp
+++ b/luni/src/main/native/NetworkUtilities.cpp
@@ -61,10 +61,6 @@
         addressLength = 16;
         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.
@@ -83,14 +79,6 @@
     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) {
@@ -118,7 +106,7 @@
     }
 
     // Check this is an address family we support.
-    if (ss.ss_family != AF_INET && ss.ss_family != AF_INET6 && ss.ss_family != AF_UNIX) {
+    if (ss.ss_family != AF_INET && ss.ss_family != AF_INET6) {
         jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
                 "inetAddressToSockaddr bad family: %i", ss.ss_family);
         return false;
@@ -132,25 +120,6 @@
         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
diff --git a/luni/src/main/native/NetworkUtilities.h b/luni/src/main/native/NetworkUtilities.h
index 6b720d4..db1ad1f 100644
--- a/luni/src/main/native/NetworkUtilities.h
+++ b/luni/src/main/native/NetworkUtilities.h
@@ -17,25 +17,24 @@
 #include "jni.h"
 #include <sys/socket.h>
 
-// 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
+// Convert from sockaddr_storage to Inet4Address (AF_INET) or Inet6Address (AF_INET6).
+// 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 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.
+// Convert from InetAddress to sockaddr_storage. 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 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, MCAST_LEAVE_GROUP, and other multicast socket options.
+// Convert from InetAddress to sockaddr_storage. 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, MCAST_LEAVE_GROUP, and other multicast socket
+// options.
 bool inetAddressToSockaddrVerbatim(JNIEnv* env, jobject inetAddress, int port,
                                    sockaddr_storage& ss, socklen_t& sa_len);
 
diff --git a/luni/src/main/native/android_system_OsConstants.cpp b/luni/src/main/native/android_system_OsConstants.cpp
index 38d9129..b84cf5a 100644
--- a/luni/src/main/native/android_system_OsConstants.cpp
+++ b/luni/src/main/native/android_system_OsConstants.cpp
@@ -35,6 +35,7 @@
 #endif
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <sys/un.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
 #include <unistd.h>
@@ -551,6 +552,8 @@
     initConstant(env, c, "S_IXUSR", S_IXUSR);
     initConstant(env, c, "TCP_NODELAY", TCP_NODELAY);
     initConstant(env, c, "TIOCOUTQ", TIOCOUTQ);
+    // UNIX_PATH_MAX is mentioned in some versions of unix(7), but not actually declared.
+    initConstant(env, c, "UNIX_PATH_MAX", sizeof(sockaddr_un::sun_path));
     initConstant(env, c, "WCONTINUED", WCONTINUED);
     initConstant(env, c, "WEXITED", WEXITED);
     initConstant(env, c, "WNOHANG", WNOHANG);
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 99b76f9..ff5b106 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -57,6 +57,7 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/uio.h>
+#include <sys/un.h>
 #include <sys/utsname.h>
 #include <sys/wait.h>
 #include <sys/xattr.h>
@@ -317,8 +318,35 @@
     std::vector<ScopedT*> mScopedBuffers;
 };
 
-static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss) {
-    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6 || ss.ss_family == AF_UNIX) {
+/**
+ * Returns the address and length of sockaddr_un.sun_path in ss. As per unix(7) sa_len should be
+ * the length of ss as returned by getsockname(2), getpeername(2), or accept(2). After the call path
+ * will point to sun_path and pathLength will contain the sun_path length. If pathLength is 0 then
+ * the sockaddr_un refers to is an unnamed socket. See unix(7) for more information.
+ */
+static bool getUnixSocketPath(JNIEnv* env, const sockaddr_storage& ss, const socklen_t& sa_len,
+        const void*& path, size_t& pathLength) {
+
+    if (ss.ss_family != AF_UNIX) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "getUnixSocketPath unsupported ss_family: %i", ss.ss_family);
+        return false;
+    }
+    const struct sockaddr_un* un_addr = reinterpret_cast<const struct sockaddr_un*>(&ss);
+    path = &un_addr->sun_path;
+    // See unix(7) for details.
+    if (sa_len == sizeof(sa_family_t)) {
+      // unnamed
+      pathLength = 0;
+    } else {
+      // pathname or abstract. We just return the fixed array length here.
+      pathLength = sizeof(sockaddr_un::sun_path);
+    }
+    return true;
+}
+
+static jobject makeSocketAddress(JNIEnv* env, const sockaddr_storage& ss, const socklen_t sa_len) {
+    if (ss.ss_family == AF_INET || ss.ss_family == AF_INET6) {
         jint port;
         jobject inetAddress = sockaddrToInetAddress(env, ss, &port);
         if (inetAddress == NULL) {
@@ -327,6 +355,23 @@
         static jmethodID ctor = env->GetMethodID(JniConstants::inetSocketAddressClass,
                 "<init>", "(Ljava/net/InetAddress;I)V");
         return env->NewObject(JniConstants::inetSocketAddressClass, ctor, inetAddress, port);
+    } else if (ss.ss_family == AF_UNIX) {
+        static jmethodID ctor = env->GetMethodID(JniConstants::unixSocketAddressClass,
+                "<init>", "([B)V");
+
+        const void* pathAddress;
+        size_t pathLength;
+        if (!getUnixSocketPath(env, ss, sa_len, pathAddress, pathLength)) {
+            return NULL;
+        }
+
+        ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(pathLength));
+        if (byteArray.get() == NULL) {
+            return NULL;
+        }
+        env->SetByteArrayRegion(byteArray.get(), 0, pathLength,
+                reinterpret_cast<const jbyte*>(pathAddress));
+        return env->NewObject(JniConstants::unixSocketAddressClass, ctor, byteArray.get());
     } else if (ss.ss_family == AF_NETLINK) {
         const struct sockaddr_nl* nl_addr = reinterpret_cast<const struct sockaddr_nl*>(&ss);
         static jmethodID ctor = env->GetMethodID(JniConstants::netlinkSocketAddressClass,
@@ -448,8 +493,34 @@
     return true;
 }
 
-static bool fillInetSocketAddress(JNIEnv* env, jint rc, jobject javaInetSocketAddress, const sockaddr_storage& ss) {
-    if (rc == -1 || javaInetSocketAddress == NULL) {
+static bool fillUnixSocketAddress(JNIEnv* env, jobject javaUnixSocketAddress,
+        const sockaddr_storage& ss, const socklen_t& sa_len) {
+    if (javaUnixSocketAddress == NULL) {
+        return true;
+    }
+    const void* pathAddress;
+    size_t pathLength;
+    if (!getUnixSocketPath(env, ss, sa_len, pathAddress, pathLength)) {
+        return false;
+    }
+
+    static jfieldID sunPathFid =
+            env->GetFieldID(JniConstants::unixSocketAddressClass, "sun_path", "[B");
+    ScopedLocalRef<jbyteArray> byteArray(env, env->NewByteArray(pathLength));
+    if (byteArray.get() == NULL) {
+        return false;
+    }
+    if (pathLength > 0) {
+        env->SetByteArrayRegion(byteArray.get(), 0, pathLength,
+                reinterpret_cast<const jbyte*>(pathAddress));
+    }
+    env->SetObjectField(javaUnixSocketAddress, sunPathFid, byteArray.get());
+    return true;
+}
+
+static bool fillInetSocketAddress(JNIEnv* env, jobject javaInetSocketAddress,
+        const sockaddr_storage& ss) {
+    if (javaInetSocketAddress == NULL) {
         return true;
     }
     // Fill out the passed-in InetSocketAddress with the sender's IP address and port number.
@@ -465,6 +536,23 @@
     return true;
 }
 
+static bool fillSocketAddress(JNIEnv* env, jobject javaSocketAddress, const sockaddr_storage& ss,
+        const socklen_t& sa_len) {
+    if (javaSocketAddress == NULL) {
+        return true;
+    }
+
+    if (env->IsInstanceOf(javaSocketAddress, JniConstants::inetSocketAddressClass)) {
+        return fillInetSocketAddress(env, javaSocketAddress, ss);
+    } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) {
+        return fillUnixSocketAddress(env, javaSocketAddress, ss, sa_len);
+    }
+    jniThrowException(env, "java/lang/UnsupportedOperationException",
+            "unsupported SocketAddress subclass");
+    return false;
+
+}
+
 static void javaInetSocketAddressToInetAddressAndPort(
         JNIEnv* env, jobject javaInetSocketAddress, jobject& javaInetAddress, jint& port) {
     static jfieldID addressFid = env->GetFieldID(
@@ -497,6 +585,33 @@
     return true;
 }
 
+static bool javaUnixSocketAddressToSockaddr(
+        JNIEnv* env, jobject javaUnixSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
+    static jfieldID sunPathFid = env->GetFieldID(
+            JniConstants::unixSocketAddressClass, "sun_path", "[B");
+
+    struct sockaddr_un* un_addr = reinterpret_cast<struct sockaddr_un*>(&ss);
+    un_addr->sun_family = AF_UNIX;
+
+    jbyteArray javaSunPath = (jbyteArray) env->GetObjectField(javaUnixSocketAddress, sunPathFid);
+    jsize pathLength = env->GetArrayLength(javaSunPath);
+    if (pathLength == 0) {
+        sa_len = sizeof(sa_family_t);
+    } else {
+        const size_t sun_path_length = sizeof(sockaddr_un::sun_path);
+        if ((size_t) pathLength != sun_path_length) {
+            jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                    "sun_path incorrect size: expected=%i, was=%i",
+                    sun_path_length, pathLength);
+            return false;
+        }
+        memset(&un_addr->sun_path, 0, sun_path_length);
+        env->GetByteArrayRegion(javaSunPath, 0, pathLength, (jbyte*) un_addr->sun_path);
+        sa_len = sizeof(sockaddr_un);
+    }
+    return true;
+}
+
 static bool javaPacketSocketAddressToSockaddr(
         JNIEnv* env, jobject javaSocketAddress, sockaddr_storage& ss, socklen_t& sa_len) {
     static jfieldID protocolFid = env->GetFieldID(
@@ -546,6 +661,8 @@
         return javaInetSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
     } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::packetSocketAddressClass)) {
         return javaPacketSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
+    } else if (env->IsInstanceOf(javaSocketAddress, JniConstants::unixSocketAddressClass)) {
+        return javaUnixSocketAddressToSockaddr(env, javaSocketAddress, ss, sa_len);
     }
     jniThrowException(env, "java/lang/UnsupportedOperationException",
             "unsupported SocketAddress subclass");
@@ -579,7 +696,7 @@
     throwErrnoException(env, is_sockname ? "getsockname" : "getpeername");
     return NULL;
   }
-  return makeSocketAddress(env, ss);
+  return makeSocketAddress(env, ss, byteCount);
 }
 
 class Passwd {
@@ -618,14 +735,14 @@
     struct passwd* mResult;
 };
 
-static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaInetSocketAddress) {
+static jobject Posix_accept(JNIEnv* env, jobject, jobject javaFd, jobject javaSocketAddress) {
     sockaddr_storage ss;
     socklen_t sl = sizeof(ss);
     memset(&ss, 0, sizeof(ss));
-    sockaddr* peer = (javaInetSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
-    socklen_t* peerLength = (javaInetSocketAddress != NULL) ? &sl : 0;
+    sockaddr* peer = (javaSocketAddress != NULL) ? reinterpret_cast<sockaddr*>(&ss) : NULL;
+    socklen_t* peerLength = (javaSocketAddress != 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 || !fillSocketAddress(env, javaSocketAddress, ss, *peerLength)) {
         close(clientFd);
         return NULL;
     }
@@ -1435,7 +1552,10 @@
     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);
+    if (recvCount == -1) {
+        return recvCount;
+    }
+    fillInetSocketAddress(env, javaInetSocketAddress, ss);
     return recvCount;
 }
 
@@ -1856,7 +1976,7 @@
     { #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName ## variant) }
 
 static JNINativeMethod gMethods[] = {
-    NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/InetSocketAddress;)Ljava/io/FileDescriptor;"),
+    NATIVE_METHOD(Posix, accept, "(Ljava/io/FileDescriptor;Ljava/net/SocketAddress;)Ljava/io/FileDescriptor;"),
     NATIVE_METHOD(Posix, access, "(Ljava/lang/String;I)Z"),
     NATIVE_METHOD(Posix, android_getaddrinfo, "(Ljava/lang/String;Landroid/system/StructAddrinfo;I)[Ljava/net/InetAddress;"),
     NATIVE_METHOD(Posix, bind, "(Ljava/io/FileDescriptor;Ljava/net/InetAddress;I)V"),
diff --git a/luni/src/test/java/libcore/io/OsTest.java b/luni/src/test/java/libcore/io/OsTest.java
index 9b38ee9..b116276 100644
--- a/luni/src/test/java/libcore/io/OsTest.java
+++ b/luni/src/test/java/libcore/io/OsTest.java
@@ -22,6 +22,8 @@
 import android.system.PacketSocketAddress;
 import android.system.StructTimeval;
 import android.system.StructUcred;
+import android.system.UnixSocketAddress;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -30,10 +32,8 @@
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
-import java.net.InetUnixAddress;
 import java.net.NetworkInterface;
 import java.net.ServerSocket;
-import java.net.SocketAddress;
 import java.nio.ByteBuffer;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
@@ -70,19 +70,25 @@
   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);
+    checkUnixDomainSocket(UnixSocketAddress.createFileSystem(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);
+    checkUnixDomainSocket(UnixSocketAddress.createAbstract("/abstract_name_unix_socket"), true);
   }
 
-  private void checkUnixDomainSocket(final InetUnixAddress address, final boolean isAbstract) throws Exception {
+  public void testUnixDomainSocket_unnamed() throws Exception {
+    final FileDescriptor fd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+    // unix(7) says an unbound socket is unnamed.
+    checkNoSockName(fd);
+    Libcore.os.close(fd);
+  }
+
+  private void checkUnixDomainSocket(final UnixSocketAddress 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.bind(serverFd, address);
     Libcore.os.listen(serverFd, 5);
 
     checkSockName(serverFd, isAbstract, address);
@@ -90,7 +96,7 @@
     Thread server = new Thread(new Runnable() {
       public void run() {
         try {
-          InetSocketAddress peerAddress = new InetSocketAddress();
+          UnixSocketAddress peerAddress = UnixSocketAddress.createUnnamed();
           FileDescriptor clientFd = Libcore.os.accept(serverFd, peerAddress);
           checkSockName(clientFd, isAbstract, address);
           checkNoName(peerAddress);
@@ -119,7 +125,7 @@
 
     FileDescriptor clientFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
 
-    Libcore.os.connect(clientFd, address, 0);
+    Libcore.os.connect(clientFd, address);
     checkNoSockName(clientFd);
 
     String string = "hello, world!";
@@ -135,26 +141,24 @@
     Libcore.os.close(clientFd);
   }
 
-  private void checkSockName(FileDescriptor fd, boolean isAbstract, InetAddress address) throws Exception {
-    InetSocketAddress isa = (InetSocketAddress) Libcore.os.getsockname(fd);
+  private static void checkSockName(FileDescriptor fd, boolean isAbstract, UnixSocketAddress address) throws Exception {
+    UnixSocketAddress isa = (UnixSocketAddress) Libcore.os.getsockname(fd);
+    assertEquals(address, isa);
     if (isAbstract) {
-      checkNoName(isa);
-    } else {
-      assertEquals(address, isa.getAddress());
+      assertEquals(0, isa.getSunPath()[0]);
     }
   }
 
-  private void checkNoName(SocketAddress sa) {
-    InetSocketAddress isa = (InetSocketAddress) sa;
-    assertEquals(0, isa.getAddress().getAddress().length);
+  private void checkNoName(UnixSocketAddress usa) {
+      assertEquals(0, usa.getSunPath().length);
   }
 
   private void checkNoPeerName(FileDescriptor fd) throws Exception {
-    checkNoName(Libcore.os.getpeername(fd));
+    checkNoName((UnixSocketAddress) Libcore.os.getpeername(fd));
   }
 
   private void checkNoSockName(FileDescriptor fd) throws Exception {
-    checkNoName(Libcore.os.getsockname(fd));
+    checkNoName((UnixSocketAddress) Libcore.os.getsockname(fd));
   }
 
   public void test_strsignal() throws Exception {