Merge "Implementing bind() and getLocalAddress() for NIO2."
diff --git a/expectations/brokentests.txt b/expectations/brokentests.txt
index 93c9a5c..3412e1c 100644
--- a/expectations/brokentests.txt
+++ b/expectations/brokentests.txt
@@ -875,5 +875,13 @@
     "org.apache.harmony.tests.java.util.jar.ManifestTest#testRead",
     "org.apache.harmony.tests.java.util.jar.ManifestTest#testStreamConstructor"
   ]
+},
+{
+  description: "Potentially flakey because they rely on a specific local TCP port being free.",
+  result: EXEC_FAILED,
+  names: [
+      "org.apache.harmony.tests.java.nio.channels.ServerSocketChannelTest#test_bind_explicitPort",
+      "org.apache.harmony.tests.java.nio.channels.SocketChannelTest#testBind_ExplicitFreePort"
+  ]
 }
 ]
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java
index 0b8b26f..731e907 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/DatagramChannelTest.java
@@ -25,6 +25,7 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AsynchronousCloseException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.DatagramChannel;
@@ -71,11 +72,11 @@
         channel1 = DatagramChannel.open();
         channel2 = DatagramChannel.open();
 
-        channel1.socket().bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
-        channel2.socket().bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
+        channel1.bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
+        channel2.bind(new InetSocketAddress(Inet6Address.LOOPBACK, 0));
 
-        channel1Address = (InetSocketAddress) channel1.socket().getLocalSocketAddress();
-        channel2Address = (InetSocketAddress) channel2.socket().getLocalSocketAddress();
+        channel1Address = (InetSocketAddress) channel1.getLocalAddress();
+        channel2Address = (InetSocketAddress) channel2.getLocalAddress();
 
         this.datagramSocket1 = new DatagramSocket(0, Inet6Address.LOOPBACK);
         this.datagramSocket2 = new DatagramSocket(0, Inet6Address.LOOPBACK);
@@ -379,7 +380,8 @@
         try {
             s.connect(datagramSocket2Address);
             fail();
-        } catch (IllegalStateException expected) {}
+        } catch (IllegalStateException expected) {
+        }
 
         assertTrue(this.channel1.isConnected());
         assertTrue(s.isConnected());
@@ -1253,8 +1255,7 @@
             Thread.sleep(TIME_UNIT);
             channel2.send(ByteBuffer.wrap(str.getBytes()), datagramSocket1Address);
             fail("Should throw SocketException!");
-        } catch (SocketException e) {
-            //expected
+        } catch (SocketException expected) {
         }
     }
 
@@ -1942,7 +1943,7 @@
             sourceArray[i] = (byte) i;
         }
 
-        this.channel1.connect(channel1.socket().getLocalSocketAddress());
+        this.channel1.connect(channel1.getLocalAddress());
         this.channel2.connect(datagramSocket1Address); // the different addr
 
         // write
@@ -1972,7 +1973,7 @@
         assertEquals(CAPACITY_NORMAL, dc.write(sourceBuf));
 
         // Connect channel2 after data has been written.
-        channel2.connect(dc.socket().getLocalSocketAddress());
+        channel2.connect(dc.getLocalAddress());
 
         // read
         ByteBuffer targetBuf = ByteBuffer.wrap(targetArray);
@@ -2164,7 +2165,7 @@
         assertEquals(CAPACITY_NORMAL, dc.write(sourceBuf));
 
         // Connect channel2 after data has been written.
-        channel2.connect(dc.socket().getLocalSocketAddress());
+        channel2.connect(dc.getLocalAddress());
 
         // read
         ByteBuffer targetBuf = ByteBuffer.wrap(targetArray);
@@ -2382,8 +2383,8 @@
     public void test_bounded_harmony6493() throws IOException {
         DatagramChannel server = DatagramChannel.open();
         InetSocketAddress addr = new InetSocketAddress("localhost", 0);
-        server.socket().bind(addr);
-        SocketAddress boundedAddress = server.socket().getLocalSocketAddress();
+        server.bind(addr);
+        SocketAddress boundedAddress = server.getLocalAddress();
 
         DatagramChannel client = DatagramChannel.open();
         ByteBuffer sent = ByteBuffer.allocate(1024);
@@ -2395,4 +2396,146 @@
         server.close();
         client.close();
     }
+
+    public void test_bind_null() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        try {
+            assertNull(dc.getLocalAddress());
+
+            dc.bind(null);
+
+            InetSocketAddress localAddress = (InetSocketAddress) dc.getLocalAddress();
+            assertTrue(localAddress.getAddress().isAnyLocalAddress());
+            assertTrue(localAddress.getPort() > 0);
+        } finally {
+            dc.close();
+        }
+    }
+
+    public void test_bind_failure() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        try {
+            // Bind to a local address that is in use
+            dc.bind(channel1Address);
+            fail();
+        } catch (IOException expected) {
+        } finally {
+            dc.close();
+        }
+    }
+
+    public void test_bind_closed() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        dc.close();
+
+        try {
+            dc.bind(null);
+            fail();
+        } catch (ClosedChannelException expected) {
+        } finally {
+            dc.close();
+        }
+    }
+
+    public void test_bind_twice() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        dc.bind(null);
+
+        try {
+            dc.bind(null);
+            fail();
+        } catch (AlreadyBoundException expected) {
+        } finally {
+            dc.close();
+        }
+    }
+
+    public void test_bind_explicitPort() throws Exception {
+        InetSocketAddress address = (InetSocketAddress) channel1.getLocalAddress();
+        assertTrue(address.getPort() > 0);
+
+        DatagramChannel dc = DatagramChannel.open();
+        // Allow the socket to bind to a port we know is already in use.
+        dc.socket().setReuseAddress(true);
+        InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort());
+        dc.bind(bindAddress);
+
+        InetSocketAddress boundAddress = (InetSocketAddress) dc.getLocalAddress();
+        assertEquals(bindAddress.getHostName(), boundAddress.getHostName());
+        assertEquals(bindAddress.getPort(), boundAddress.getPort());
+
+        dc.close();
+        channel1.close();
+    }
+
+    /** Checks that the SocketChannel and associated Socket agree on the socket state. */
+    public void test_bind_socketSync() throws IOException {
+        DatagramChannel dc = DatagramChannel.open();
+        assertNull(dc.getLocalAddress());
+
+        DatagramSocket socket = dc.socket();
+        assertNull(socket.getLocalSocketAddress());
+        assertFalse(socket.isBound());
+
+        InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+        dc.bind(bindAddr);
+
+        InetSocketAddress actualAddr = (InetSocketAddress) dc.getLocalAddress();
+        assertEquals(actualAddr, socket.getLocalSocketAddress());
+        assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+        assertTrue(socket.isBound());
+        assertFalse(socket.isConnected());
+        assertFalse(socket.isClosed());
+
+        dc.close();
+
+        assertFalse(dc.isOpen());
+        assertTrue(socket.isClosed());
+    }
+
+    /**
+     * Checks that the SocketChannel and associated Socket agree on the socket state, even if
+     * the Socket object is requested/created after bind().
+     */
+    public void test_bind_socketSyncAfterBind() throws IOException {
+        DatagramChannel dc = DatagramChannel.open();
+        assertNull(dc.getLocalAddress());
+
+        InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+        dc.bind(bindAddr);
+
+        // Socket creation after bind().
+        DatagramSocket socket = dc.socket();
+        InetSocketAddress actualAddr = (InetSocketAddress) dc.getLocalAddress();
+        assertEquals(actualAddr, socket.getLocalSocketAddress());
+        assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+        assertTrue(socket.isBound());
+        assertFalse(socket.isConnected());
+        assertFalse(socket.isClosed());
+
+        dc.close();
+
+        assertFalse(dc.isOpen());
+        assertTrue(socket.isClosed());
+    }
+
+    public void test_getLocalSocketAddress_afterClose() throws IOException {
+        DatagramChannel dc = DatagramChannel.open();
+        assertNull(dc.getLocalAddress());
+
+        InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+        dc.bind(bindAddr);
+
+        assertNotNull(dc.getLocalAddress());
+
+        dc.close();
+
+        assertFalse(dc.isOpen());
+
+        try {
+            dc.getLocalAddress();
+            fail();
+        } catch (ClosedChannelException expected) {
+        }
+    }
 }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
index 828ab30..b417adc 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/ServerSocketChannelTest.java
@@ -24,6 +24,7 @@
 import java.net.ServerSocket;
 import java.net.Socket;
 import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AsynchronousCloseException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.IllegalBlockingModeException;
@@ -108,6 +109,157 @@
     }
 
     // -------------------------------------------------------------------
+    // Tests for bind()
+    // -------------------------------------------------------------------
+
+    public void test_bind_null() throws Exception {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        try {
+            assertNull(ssc.getLocalAddress());
+
+            ssc.bind(null);
+
+            InetSocketAddress localAddress = (InetSocketAddress) ssc.getLocalAddress();
+            assertTrue(localAddress.getAddress().isAnyLocalAddress());
+            assertTrue(localAddress.getPort() > 0);
+        } finally {
+            ssc.close();
+        }
+    }
+
+    public void test_bind_failure() throws Exception {
+        ServerSocketChannel portHog = ServerSocketChannel.open();
+        portHog.bind(null);
+
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        try {
+            // Bind to a local address that is in use
+            ssc.bind(portHog.getLocalAddress());
+            fail();
+        } catch (IOException expected) {
+        } finally {
+            ssc.close();
+            portHog.close();
+        }
+    }
+
+    public void test_bind_closed() throws Exception {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.close();
+
+        try {
+            ssc.bind(null);
+            fail();
+        } catch (ClosedChannelException expected) {
+        } finally {
+            ssc.close();
+        }
+    }
+
+    public void test_bind_twice() throws Exception {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.bind(null);
+
+        try {
+            ssc.bind(null);
+            fail();
+        } catch (AlreadyBoundException expected) {
+        } finally {
+            ssc.close();
+        }
+    }
+
+    public void test_bind_explicitPort() throws Exception {
+        ServerSocketChannel portPickingChannel = ServerSocketChannel.open();
+        // Have the OS find a free port.
+        portPickingChannel.bind(null);
+
+        InetSocketAddress address = (InetSocketAddress) portPickingChannel.getLocalAddress();
+        assertTrue(address.getPort() > 0);
+        portPickingChannel.close();
+
+        // There is a risk of flakiness here if the port is allocated to something else between
+        // close() and bind().
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort());
+        ssc.bind(bindAddress);
+
+        InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress();
+        assertEquals(bindAddress.getHostName(), boundAddress.getHostName());
+        assertEquals(bindAddress.getPort(), boundAddress.getPort());
+
+        ssc.close();
+    }
+
+    public void test_bind_socketSync() throws IOException {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        assertNull(ssc.getLocalAddress());
+
+        ServerSocket socket = ssc.socket();
+        assertNull(socket.getLocalSocketAddress());
+        assertFalse(socket.isBound());
+
+        InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+        ssc.bind(bindAddr);
+
+        InetSocketAddress actualAddr = (InetSocketAddress) ssc.getLocalAddress();
+        assertEquals(actualAddr, socket.getLocalSocketAddress());
+        assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+        assertTrue(socket.isBound());
+        assertFalse(socket.isClosed());
+
+        ssc.close();
+
+        assertFalse(ssc.isOpen());
+        assertTrue(socket.isClosed());
+    }
+
+    public void test_bind_socketSyncAfterBind() throws IOException {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        assertNull(ssc.getLocalAddress());
+
+        InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+        ssc.bind(bindAddr);
+
+        // Socket creation after bind().
+        ServerSocket socket = ssc.socket();
+        InetSocketAddress actualAddr = (InetSocketAddress) ssc.getLocalAddress();
+        assertEquals(actualAddr, socket.getLocalSocketAddress());
+        assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+        assertTrue(socket.isBound());
+        assertFalse(socket.isClosed());
+
+        ssc.close();
+
+        assertFalse(ssc.isOpen());
+        assertTrue(socket.isClosed());
+    }
+
+    // -------------------------------------------------------------------
+    // Test for getLocalSocketAddress()
+    // -------------------------------------------------------------------
+
+    public void test_getLocalSocketAddress_afterClose() throws IOException {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        assertNull(ssc.getLocalAddress());
+
+        InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+        ssc.bind(bindAddr);
+
+        assertNotNull(ssc.getLocalAddress());
+
+        ssc.close();
+
+        assertFalse(ssc.isOpen());
+
+        try {
+            ssc.getLocalAddress();
+            fail();
+        } catch (ClosedChannelException expected) {
+        }
+    }
+
+    // -------------------------------------------------------------------
     // Test for socket()
     // -------------------------------------------------------------------
 
@@ -235,8 +387,7 @@
 
     public void testAccept_Block_NoConnect() throws IOException {
         assertTrue(this.serverChannel.isBlocking());
-        ServerSocket gotSocket = this.serverChannel.socket();
-        gotSocket.bind(null);
+        serverChannel.bind(null);
         // blocking mode , will block and wait for ever...
         // so must close the server channel with another thread.
         new Thread() {
@@ -259,8 +410,7 @@
     }
 
     public void testAccept_NonBlock_NoConnect() throws IOException {
-        ServerSocket gotSocket = this.serverChannel.socket();
-        gotSocket.bind(null);
+        this.serverChannel.bind(null);
         this.serverChannel.configureBlocking(false);
         // non-blocking mode , will immediately return
         assertNull(this.serverChannel.accept());
@@ -270,13 +420,13 @@
      * @tests ServerSocketChannel#accept().socket()
      */
     public void test_read_Blocking_RealData() throws IOException {
-        serverChannel.socket().bind(null);
+        serverChannel.bind(null);
         ByteBuffer buf = ByteBuffer.allocate(CAPACITY_NORMAL);
 
         for (int i = 0; i < CAPACITY_NORMAL; i++) {
             buf.put((byte) i);
         }
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         Socket serverSocket = serverChannel.accept().socket();
         InputStream in = serverSocket.getInputStream();
         buf.flip();
@@ -309,13 +459,13 @@
      */
     public void test_read_NonBlocking_RealData() throws Exception {
         serverChannel.configureBlocking(false);
-        serverChannel.socket().bind(null);
+        serverChannel.bind(null);
         ByteBuffer buf = ByteBuffer.allocate(CAPACITY_NORMAL);
         for (int i = 0; i < CAPACITY_NORMAL; i++) {
             buf.put((byte) i);
         }
         buf.flip();
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         Socket serverSocket = serverChannel.accept().socket();
         InputStream in = serverSocket.getInputStream();
         clientChannel.write(buf);
@@ -328,14 +478,13 @@
      */
     public void test_write_Blocking_RealData() throws IOException {
         assertTrue(serverChannel.isBlocking());
-        ServerSocket serverSocket = serverChannel.socket();
-        serverSocket.bind(null);
+        serverChannel.bind(null);
 
         byte[] writeContent = new byte[CAPACITY_NORMAL];
         for (int i = 0; i < writeContent.length; i++) {
             writeContent[i] = (byte) i;
         }
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         Socket socket = serverChannel.accept().socket();
         OutputStream out = socket.getOutputStream();
         out.write(writeContent);
@@ -350,14 +499,13 @@
      */
     public void test_write_NonBlocking_RealData() throws Exception {
         serverChannel.configureBlocking(false);
-        ServerSocket serverSocket = serverChannel.socket();
-        serverSocket.bind(null);
+        serverChannel.bind(null);
 
         byte[] writeContent = new byte[CAPACITY_NORMAL];
         for (int i = 0; i < CAPACITY_NORMAL; i++) {
             writeContent[i] = (byte) i;
         }
-        clientChannel.connect(serverSocket.getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         Socket clientSocket = serverChannel.accept().socket();
         OutputStream out = clientSocket.getOutputStream();
         out.write(writeContent);
@@ -371,13 +519,13 @@
      */
     public void test_read_LByteBuffer_Blocking_ReadWriteRealLargeData()
             throws IOException, InterruptedException {
-        serverChannel.socket().bind(null);
+        serverChannel.bind(null);
         ByteBuffer buf = ByteBuffer.allocate(CAPACITY_64KB);
         for (int i = 0; i < CAPACITY_64KB; i++) {
             buf.put((byte) i);
         }
         buf.flip();
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         WriteChannelThread writeThread = new WriteChannelThread(clientChannel, buf);
         writeThread.start();
         Socket socket = serverChannel.accept().socket();
@@ -416,13 +564,13 @@
     public void test_read_LByteBuffer_NonBlocking_ReadWriteRealLargeData()
             throws Exception {
         serverChannel.configureBlocking(false);
-        serverChannel.socket().bind(null);
+        serverChannel.bind(null);
         ByteBuffer buf = ByteBuffer.allocate(CAPACITY_64KB);
         for (int i = 0; i < CAPACITY_64KB; i++) {
             buf.put((byte) i);
         }
         buf.flip();
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         WriteChannelThread writeThread = new WriteChannelThread(clientChannel, buf);
         writeThread.start();
         Socket socket = serverChannel.accept().socket();
@@ -441,12 +589,12 @@
     public void test_write_LByteBuffer_NonBlocking_ReadWriteRealLargeData()
             throws Exception {
         serverChannel.configureBlocking(false);
-        serverChannel.socket().bind(null);
+        serverChannel.bind(null);
         byte[] writeContent = new byte[CAPACITY_64KB];
         for (int i = 0; i < writeContent.length; i++) {
             writeContent[i] = (byte) i;
         }
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         Socket socket = serverChannel.accept().socket();
         WriteSocketThread writeThread = new WriteSocketThread(socket, writeContent);
         writeThread.start();
@@ -484,12 +632,12 @@
      */
     public void test_write_LByteBuffer_Blocking_ReadWriteRealLargeData()
             throws Exception {
-        serverChannel.socket().bind(null);
+        serverChannel.bind(null);
         byte[] writeContent = new byte[CAPACITY_64KB];
         for (int i = 0; i < writeContent.length; i++) {
             writeContent[i] = (byte) i;
         }
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        clientChannel.connect(serverChannel.getLocalAddress());
         Socket socket = serverChannel.accept().socket();
         WriteSocketThread writeThread = new WriteSocketThread(socket, writeContent);
         writeThread.start();
@@ -531,9 +679,9 @@
         final int SO_TIMEOUT = 10;
         ServerSocketChannel sc = ServerSocketChannel.open();
         try {
-            ServerSocket ss = sc.socket();
-            ss.bind(null);
+            sc.bind(null);
             sc.configureBlocking(false);
+            ServerSocket ss = sc.socket();
             ss.setSoTimeout(SO_TIMEOUT);
             SocketChannel client = sc.accept();
             // non blocking mode, returns null since there are no pending connections.
@@ -556,15 +704,13 @@
         try {
             gotSocket.accept();
             fail("Should throw an IllegalBlockingModeException");
-        } catch (IllegalBlockingModeException e) {
-            // expected
+        } catch (IllegalBlockingModeException expected) {
         }
         serverChannel.close();
         try {
             gotSocket.accept();
             fail("Should throw an IllegalBlockingModeException");
-        } catch (IllegalBlockingModeException e) {
-            // expected
+        } catch (IllegalBlockingModeException expected) {
         }
     }
 
@@ -578,15 +724,13 @@
         try {
             gotSocket.accept();
             fail("Should throw an IllegalBlockingModeException");
-        } catch (IllegalBlockingModeException e) {
-            // expected
+        } catch (IllegalBlockingModeException expected) {
         }
         serverChannel.close();
         try {
             gotSocket.accept();
             fail("Should throw an IllegalBlockingModeException");
-        } catch (IllegalBlockingModeException e) {
-            // expected
+        } catch (IllegalBlockingModeException expected) {
         }
     }
 
@@ -596,20 +740,18 @@
     public void test_socket_accept_Nonblocking_Bound() throws IOException {
         // regression test for Harmony-748
         serverChannel.configureBlocking(false);
+        serverChannel.bind(null);
         ServerSocket gotSocket = serverChannel.socket();
-        gotSocket.bind(null);
         try {
             gotSocket.accept();
             fail("Should throw an IllegalBlockingModeException");
-        } catch (IllegalBlockingModeException e) {
-            // expected
+        } catch (IllegalBlockingModeException expected) {
         }
         serverChannel.close();
         try {
             gotSocket.accept();
             fail("Should throw a ClosedChannelException");
-        } catch (ClosedChannelException e) {
-            // expected
+        } catch (ClosedChannelException expected) {
         }
     }
 
@@ -619,22 +761,20 @@
     public void test_socket_accept_Blocking_Bound() throws IOException {
         // regression test for Harmony-748
         serverChannel.configureBlocking(true);
-        ServerSocket gotSocket = serverChannel.socket();
-        gotSocket.bind(null);
+        serverChannel.bind(null);
         serverChannel.close();
         try {
-            gotSocket.accept();
+            serverChannel.socket().accept();
             fail("Should throw a ClosedChannelException");
-        } catch (ClosedChannelException e) {
-            // expected
+        } catch (ClosedChannelException expected) {
         }
     }
     /**
      * Regression test for HARMONY-4961
      */
     public void test_socket_getLocalPort() throws IOException {
-        serverChannel.socket().bind(null);
-        clientChannel.connect(serverChannel.socket().getLocalSocketAddress());
+        serverChannel.bind(null);
+        clientChannel.connect(serverChannel.getLocalAddress());
         SocketChannel myChannel = serverChannel.accept();
         int port = myChannel.socket().getLocalPort();
         assertEquals(serverChannel.socket().getLocalPort(), port);
@@ -648,7 +788,7 @@
      */
     public void test_accept_configureBlocking() throws Exception {
         InetSocketAddress localAddr = new InetSocketAddress("localhost", 0);
-        serverChannel.socket().bind(localAddr);
+        serverChannel.bind(localAddr);
 
         // configure the channel non-blocking
         // when it is accepting in main thread
@@ -667,8 +807,7 @@
         try {
             serverChannel.accept();
             fail("should throw AsynchronousCloseException");
-        } catch (AsynchronousCloseException e) {
-            // expected
+        } catch (AsynchronousCloseException expected) {
         }
         serverChannel.close();
     }
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java
index 2d52bc5..52dff79 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/nio/channels/SocketChannelTest.java
@@ -30,6 +30,7 @@
 import java.net.SocketException;
 import java.nio.Buffer;
 import java.nio.ByteBuffer;
+import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AlreadyConnectedException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.ConnectionPendingException;
@@ -153,6 +154,87 @@
         }
     }
 
+    public void testBind_Null() throws Exception {
+        assertNull(channel1.getLocalAddress());
+
+        channel1.bind(null);
+
+        InetSocketAddress localAddress = (InetSocketAddress) channel1.getLocalAddress();
+        assertTrue(localAddress.getAddress().isAnyLocalAddress());
+        assertTrue(localAddress.getPort() > 0);
+    }
+
+    public void testBind_Failure() throws Exception {
+        assertNull(channel1.getLocalAddress());
+
+        try {
+            // Bind to a local address that is in use
+            channel1.bind(localAddr1);
+            fail();
+        } catch (IOException expected) {
+        }
+    }
+
+    public void testBind_Closed() throws Exception {
+        channel1.close();
+
+        try {
+            channel1.bind(null);
+            fail();
+        } catch (ClosedChannelException expected) {
+        }
+    }
+
+    public void testBind_Twice() throws Exception {
+        channel1.bind(null);
+
+        try {
+            channel1.bind(null);
+            fail();
+        } catch (AlreadyBoundException expected) {
+        }
+    }
+
+    public void testBind_explicitPort() throws Exception {
+        ServerSocketChannel portPickingChannel = ServerSocketChannel.open();
+        // Have the OS find a free port.
+        portPickingChannel.bind(null);
+        InetSocketAddress address = (InetSocketAddress) portPickingChannel.getLocalAddress();
+        assertTrue(address.getPort() > 0);
+        portPickingChannel.close();
+
+        // There is a risk of flakiness here if the port is allocated to something else between
+        // close() and bind().
+        InetSocketAddress bindAddress = new InetSocketAddress("localhost", address.getPort());
+        // Allow the socket to bind to a port we know is already in use.
+        channel1.socket().setReuseAddress(true);
+        channel1.bind(bindAddress);
+
+        InetSocketAddress boundAddress = (InetSocketAddress) channel1.getLocalAddress();
+        assertEquals(bindAddress.getHostName(), boundAddress.getHostName());
+        assertEquals(bindAddress.getPort(), boundAddress.getPort());
+    }
+
+    public void test_getLocalSocketAddress_afterClose() throws IOException {
+        SocketChannel sc = SocketChannel.open();
+        assertNull(sc.getLocalAddress());
+
+        InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+        sc.bind(bindAddr);
+
+        assertNotNull(sc.getLocalAddress());
+
+        sc.close();
+
+        assertFalse(sc.isOpen());
+
+        try {
+            sc.getLocalAddress();
+            fail();
+        } catch (ClosedChannelException expected) {
+        }
+    }
+
     /*
      * Test method for 'java.nio.channels.SocketChannel.read(ByteBuffer[])'
      */
@@ -1800,7 +1882,7 @@
         ServerSocket serversocket = theServerChannel.socket();
         serversocket.setReuseAddress(true);
         // Bind the socket
-        serversocket.bind(address);
+        theServerChannel.bind(address);
 
         boolean doneNonBlockingConnect = false;
         // Loop so that we make sure we're definitely testing finishConnect()
@@ -2121,8 +2203,7 @@
         ByteBuffer buffer = ByteBuffer.allocateDirect(128);
 
         ServerSocketChannel server = ServerSocketChannel.open();
-        server.socket().bind(
-                new InetSocketAddress(InetAddress.getLocalHost(), 0), 5);
+        server.bind(new InetSocketAddress(InetAddress.getLocalHost(), 0), 5);
         Socket client = new Socket(InetAddress.getLocalHost(), server.socket()
                 .getLocalPort());
         client.setTcpNoDelay(false);
@@ -2740,9 +2821,9 @@
      */
     public void test_writev() throws Exception {
         ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
-        sc.connect(ssc.socket().getLocalSocketAddress());
+        sc.connect(ssc.getLocalAddress());
         SocketChannel sock = ssc.accept();
         ByteBuffer[] buf = { ByteBuffer.allocate(10), ByteBuffer.allocateDirect(20) };
 
@@ -2767,10 +2848,10 @@
     public void test_writev2() throws Exception {
         ServerSocketChannel ssc = ServerSocketChannel.open();
         ssc.configureBlocking(false);
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
         sc.configureBlocking(false);
-        boolean connected = sc.connect(ssc.socket().getLocalSocketAddress());
+        boolean connected = sc.connect(ssc.getLocalAddress());
         SocketChannel sock = ssc.accept();
         if (!connected) {
             sc.finishConnect();
@@ -2805,10 +2886,10 @@
     public void test_write$NonBlockingException() throws Exception {
         ServerSocketChannel ssc = ServerSocketChannel.open();
         ssc.configureBlocking(false);
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
         sc.configureBlocking(false);
-        boolean connected = sc.connect(ssc.socket().getLocalSocketAddress());
+        boolean connected = sc.connect(ssc.getLocalAddress());
         SocketChannel sock = ssc.accept();
         if (!connected) {
             sc.finishConnect();
@@ -2841,9 +2922,9 @@
     public void test_write$LByteBuffer2() throws IOException {
         // Set-up
         ServerSocketChannel server = ServerSocketChannel.open();
-        server.socket().bind(null);
+        server.bind(null);
         SocketChannel client = SocketChannel.open();
-        client.connect(server.socket().getLocalSocketAddress());
+        client.connect(server.getLocalAddress());
         SocketChannel worker = server.accept();
 
         // Test overlapping buffers
@@ -2873,9 +2954,9 @@
     public void test_write$LByteBuffer_buffers() throws IOException {
         // Set-up
         ServerSocketChannel server = ServerSocketChannel.open();
-        server.socket().bind(null);
+        server.bind(null);
         SocketChannel client = SocketChannel.open();
-        client.connect(server.socket().getLocalSocketAddress());
+        client.connect(server.getLocalAddress());
         SocketChannel worker = server.accept();
 
         // A variety of buffer types to write
@@ -2915,9 +2996,9 @@
     public void test_write$LByteBuffer_writes() throws IOException {
         // Set-up
         ServerSocketChannel server = ServerSocketChannel.open();
-        server.socket().bind(null);
+        server.bind(null);
         SocketChannel client = SocketChannel.open();
-        client.connect(server.socket().getLocalSocketAddress());
+        client.connect(server.getLocalAddress());
         SocketChannel worker = server.accept();
 
         // Data to write
@@ -2942,7 +3023,7 @@
 
         // Read what we wrote and check it
         ByteBuffer readBuffer = ByteBuffer.allocate(1024);
-        while (EOF != worker.read(readBuffer)) {};
+        while (EOF != worker.read(readBuffer)) {}
         readBuffer.flip();
         assertEquals(ByteBuffer.wrap(data), readBuffer);
 
@@ -2957,10 +3038,10 @@
     public void test_write$LByteBuffer_invalid() throws IOException {
         // Set-up
         ServerSocketChannel server = ServerSocketChannel.open();
-        server.socket().bind(null);
+        server.bind(null);
 
         SocketChannel client = SocketChannel.open();
-        client.connect(server.socket().getLocalSocketAddress());
+        client.connect(server.getLocalAddress());
 
         SocketChannel worker = server.accept();
 
@@ -2968,32 +3049,27 @@
         try {
             client.write((ByteBuffer[]) null);
             fail("Should throw a NPE");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
         try {
             client.write((ByteBuffer[]) null, 0, 0);
             fail("Should throw a NPE");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
         try {
             client.write((ByteBuffer[]) null, 1, 0);
             fail("Should throw a NPE");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
         try {
             client.write((ByteBuffer[]) null, 0, 1);
             fail("Should throw a NPE");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
         try {
             client.write((ByteBuffer[]) null, 1, 1);
             fail("Should throw a NPE");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
 
         ByteBuffer[] buffers = new ByteBuffer[1];
@@ -3065,9 +3141,9 @@
             throws Exception {
         // regression 1 for HARMONY-549
         ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
-        sc.connect(ssc.socket().getLocalSocketAddress());
+        sc.connect(ssc.getLocalAddress());
         ssc.accept().close();
         ByteBuffer[] buf = { ByteBuffer.allocate(10) };
         assertEquals(-1, sc.read(buf, 0, 1));
@@ -3081,16 +3157,15 @@
     public void test_socketChannel_write_ByteBufferII() throws Exception {
         // regression 2 for HARMONY-549
         ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
-        sc.connect(ssc.socket().getLocalSocketAddress());
+        sc.connect(ssc.getLocalAddress());
         SocketChannel sock = ssc.accept();
         ByteBuffer[] buf = { ByteBuffer.allocate(10), null };
         try {
             sc.write(buf, 0, 2);
             fail("should throw NPE");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
         ssc.close();
         sc.close();
@@ -3104,9 +3179,9 @@
     public void test_socketChannel_read_ByteBufferII_bufNULL() throws Exception {
         // regression 3 for HARMONY-549
         ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
-        sc.connect(ssc.socket().getLocalSocketAddress());
+        sc.connect(ssc.getLocalAddress());
         ssc.accept();
         ByteBuffer[] buf = new ByteBuffer[2];
         buf[0] = ByteBuffer.allocate(1);
@@ -3114,8 +3189,7 @@
         try {
             sc.read(buf, 0, 2);
             fail("should throw NullPointerException");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
         ssc.close();
         sc.close();
@@ -3127,9 +3201,9 @@
     public void test_socketChannel_write_close() throws Exception {
         // regression 4 for HARMONY-549
         ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
-        sc.connect(ssc.socket().getLocalSocketAddress());
+        sc.connect(ssc.getLocalAddress());
         SocketChannel sock = ssc.accept();
         ByteBuffer buf = null;
         ssc.close();
@@ -3137,8 +3211,7 @@
         try {
             sc.write(buf);
             fail("should throw NPE");
-        } catch (NullPointerException e) {
-            // expected
+        } catch (NullPointerException expected) {
         }
         sock.close();
     }
@@ -3153,9 +3226,9 @@
         ByteBuffer readBuf = ByteBuffer.allocate(11);
         ByteBuffer buf = ByteBuffer.wrap(testStr.getBytes());
         ServerSocketChannel ssc = ServerSocketChannel.open();
-        ssc.socket().bind(null);
+        ssc.bind(null);
         SocketChannel sc = SocketChannel.open();
-        sc.connect(ssc.socket().getLocalSocketAddress());
+        sc.connect(ssc.getLocalAddress());
         buf.position(2);
         ssc.accept().write(buf);
         assertEquals(9, sc.read(readBuf));
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index e2acac7..26825d2 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -176,8 +176,8 @@
     }
 
     /**
-     * Returns the local address to which this socket is bound,
-     * or {@code null} if this socket is closed.
+     * Returns the local address to which this socket is bound, a wildcard address if this
+     * socket is not yet bound, or {@code null} if this socket is closed.
      */
     public InetAddress getLocalAddress() {
         try {
@@ -588,10 +588,11 @@
     }
 
     /**
-     * Returns the {@code SocketAddress} this socket is bound to, or null for an unbound socket.
+     * Returns the {@code SocketAddress} this socket is bound to, or {@code null} for an unbound or
+     * closed socket.
      */
     public SocketAddress getLocalSocketAddress() {
-        if (!isBound()) {
+        if (isClosed() || !isBound()) {
             return null;
         }
         return new InetSocketAddress(getLocalAddress(), getLocalPort());
diff --git a/luni/src/main/java/java/net/ServerSocket.java b/luni/src/main/java/java/net/ServerSocket.java
index 8b56ca1..72b197f 100644
--- a/luni/src/main/java/java/net/ServerSocket.java
+++ b/luni/src/main/java/java/net/ServerSocket.java
@@ -164,8 +164,8 @@
     }
 
     /**
-     * Gets the local IP address of this server socket or {@code null} if the
-     * socket is unbound. This is useful for multihomed hosts.
+     * Gets the local IP address of this server socket if this socket has ever been bound,
+     * {@code null} otherwise. This is useful for multihomed hosts.
      *
      * @return the local address of this server socket.
      */
@@ -177,8 +177,9 @@
     }
 
     /**
-     * Gets the local port of this server socket or {@code -1} if the socket is
-     * unbound.
+     * Gets the local port of this server socket or {@code -1} if the socket is not bound.
+     * If the socket has ever been bound this method will return the local port it was bound to,
+     * even after it has been closed.
      *
      * @return the local port this server is listening on.
      */
@@ -342,8 +343,9 @@
     }
 
     /**
-     * Gets the local socket address of this server socket or {@code null} if
-     * the socket is unbound. This is useful on multihomed hosts.
+     * Gets the local socket address of this server socket or {@code null} if the socket is unbound.
+     * This is useful on multihomed hosts. If the socket has ever been bound this method will return
+     * the local address it was bound to, even after it has been closed.
      *
      * @return the local socket address and port this socket is bound to.
      */
diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java
index 517571d..5dd350a 100644
--- a/luni/src/main/java/java/net/Socket.java
+++ b/luni/src/main/java/java/net/Socket.java
@@ -312,6 +312,7 @@
      */
     public synchronized void close() throws IOException {
         isClosed = true;
+        isConnected = false;
         // RI compatibility: the RI returns the any address (but the original local port) after
         // close.
         localAddress = Inet4Address.ANY;
@@ -326,6 +327,7 @@
      */
     public void onClose() {
         isClosed = true;
+        isConnected = false;
         // RI compatibility: the RI returns the any address (but the original local port) after
         // close.
         localAddress = Inet4Address.ANY;
@@ -371,14 +373,15 @@
 
     /**
      * Returns the local IP address this socket is bound to, or an address for which
-     * {@link InetAddress#isAnyLocalAddress()} returns true if the socket is unbound.
+     * {@link InetAddress#isAnyLocalAddress()} returns true if the socket is closed or unbound.
      */
     public InetAddress getLocalAddress() {
         return localAddress;
     }
 
     /**
-     * Returns the local port this socket is bound to, or -1 if the socket is unbound.
+     * Returns the local port this socket is bound to, or -1 if the socket is unbound. If the socket
+     * has been closed this method will still return the local port the socket was bound to.
      */
     public int getLocalPort() {
         if (!isBound()) {
@@ -583,6 +586,7 @@
                     impl.bind(addr, localPort);
                 }
                 isBound = true;
+                cacheLocalAddress();
                 impl.connect(dstAddress, dstPort);
                 isConnected = true;
                 cacheLocalAddress();
@@ -691,9 +695,10 @@
     }
 
     /**
-     * Returns the local address and port of this socket as a SocketAddress or
-     * null if the socket is unbound. This is useful on multihomed
-     * hosts.
+     * Returns the local address and port of this socket as a SocketAddress or null if the socket
+     * has never been bound. If the socket is closed but has previously been bound then an address
+     * for which {@link InetAddress#isAnyLocalAddress()} returns true will be returned with the
+     * previously-bound port. This is useful on multihomed hosts.
      */
     public SocketAddress getLocalSocketAddress() {
         if (!isBound()) {
@@ -800,6 +805,7 @@
      */
     public void onBind(InetAddress localAddress, int localPort) {
         isBound = true;
+        this.localAddress = localAddress;
         impl.onBind(localAddress, localPort);
     }
 
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 5f74bdc..3c4a980 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -24,16 +24,19 @@
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.DatagramSocketImpl;
+import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.PlainDatagramSocketImpl;
 import java.net.SocketAddress;
 import java.net.SocketException;
+import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AlreadyConnectedException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.DatagramChannel;
 import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.NotYetConnectedException;
+import java.nio.channels.UnsupportedAddressTypeException;
 import java.nio.channels.spi.SelectorProvider;
 import java.util.Arrays;
 import libcore.io.ErrnoException;
@@ -100,6 +103,26 @@
         return socket;
     }
 
+    /** @hide Until ready for a public API change */
+    @Override
+    synchronized public DatagramChannel bind(SocketAddress local) throws IOException {
+        checkOpen();
+        if (isBound) {
+            throw new AlreadyBoundException();
+        }
+
+        if (local == null) {
+            local = new InetSocketAddress(Inet4Address.ANY, 0);
+        } else if (!(local instanceof InetSocketAddress)) {
+            throw new UnsupportedAddressTypeException();
+        }
+
+        InetSocketAddress localAddress = (InetSocketAddress) local;
+        IoBridge.bind(fd, localAddress.getAddress(), localAddress.getPort());
+        onBind(true /* updateSocketState */);
+        return this;
+    }
+
     /**
      * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when
      * some or all of the bound state has been left to the OS to decide, or when the Socket handled
@@ -125,6 +148,13 @@
         }
     }
 
+    /** @hide Until ready for a public API change */
+    @Override
+    synchronized public SocketAddress getLocalAddress() throws IOException {
+        checkOpen();
+        return isBound ? new InetSocketAddress(localAddress, localPort) : null;
+    }
+
     @Override
     synchronized public boolean isConnected() {
         return connected;
@@ -547,40 +577,6 @@
         }
 
         @Override
-        public boolean isBound() {
-            return channelImpl.isBound;
-        }
-
-        @Override
-        public boolean isConnected() {
-            return channelImpl.isConnected();
-        }
-
-        @Override
-        public InetAddress getInetAddress() {
-            if (channelImpl.connectAddress == null) {
-                return null;
-            }
-            return channelImpl.connectAddress.getAddress();
-        }
-
-        @Override public InetAddress getLocalAddress() {
-            try {
-                return IoBridge.getSocketLocalAddress(channelImpl.fd);
-            } catch (SocketException ex) {
-                return null;
-            }
-        }
-
-        @Override
-        public int getPort() {
-            if (channelImpl.connectAddress == null) {
-                return -1;
-            }
-            return channelImpl.connectAddress.getPort();
-        }
-
-        @Override
         public void bind(SocketAddress localAddr) throws SocketException {
             if (channelImpl.isConnected()) {
                 throw new AlreadyConnectedException();
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index 92bc419..9ae282e 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -22,12 +22,15 @@
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketAddress;
 import java.net.SocketTimeoutException;
+import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.IllegalBlockingModeException;
 import java.nio.channels.NotYetBoundException;
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
+import java.nio.channels.UnsupportedAddressTypeException;
 import java.nio.channels.spi.SelectorProvider;
 import libcore.io.ErrnoException;
 import libcore.io.IoUtils;
@@ -51,6 +54,32 @@
         return socket;
     }
 
+    /** @hide Until ready for a public API change */
+    @Override
+    public final ServerSocketChannel bind(SocketAddress localAddr, int backlog) throws IOException {
+        if (!isOpen()) {
+            throw new ClosedChannelException();
+        }
+        if (socket.isBound()) {
+            throw new AlreadyBoundException();
+        }
+        if (localAddr != null && !(localAddr instanceof InetSocketAddress)) {
+            throw new UnsupportedAddressTypeException();
+        }
+
+        socket.bind(localAddr, backlog);
+        return this;
+    }
+
+    /** @hide Until ready for a public API change */
+    @Override
+    public SocketAddress getLocalAddress() throws IOException {
+        if (!isOpen()) {
+            throw new ClosedChannelException();
+        }
+        return socket.getLocalSocketAddress();
+    }
+
     @Override
     public SocketChannel accept() throws IOException {
         if (!isOpen()) {
@@ -60,7 +89,7 @@
             throw new NotYetBoundException();
         }
 
-        // Create an empty socket channel. This will be populated by ServerSocketAdapter.accept.
+        // Create an empty socket channel. This will be populated by ServerSocketAdapter.implAccept.
         SocketChannelImpl result = new SocketChannelImpl(provider(), false);
         try {
             begin();
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index 6af9f9f..977c433 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -32,6 +32,7 @@
 import java.net.SocketAddress;
 import java.net.SocketException;
 import java.net.SocketUtils;
+import java.nio.channels.AlreadyBoundException;
 import java.nio.channels.AlreadyConnectedException;
 import java.nio.channels.ClosedChannelException;
 import java.nio.channels.ConnectionPendingException;
@@ -135,6 +136,28 @@
         return socket;
     }
 
+    /** @hide Until ready for a public API change */
+    @Override
+    synchronized public final SocketChannel bind(SocketAddress local) throws IOException {
+        if (!isOpen()) {
+            throw new ClosedChannelException();
+        }
+        if (isBound) {
+            throw new AlreadyBoundException();
+        }
+
+        if (local == null) {
+            local = new InetSocketAddress(Inet4Address.ANY, 0);
+        } else if (!(local instanceof InetSocketAddress)) {
+            throw new UnsupportedAddressTypeException();
+        }
+
+        InetSocketAddress localAddress = (InetSocketAddress) local;
+        IoBridge.bind(fd, localAddress.getAddress(), localAddress.getPort());
+        onBind(true /* updateSocketState */);
+        return this;
+    }
+
     /**
      * Initialise the isBound, localAddress and localPort state from the file descriptor. Used when
      * some or all of the bound state has been left to the OS to decide, or when the Socket handled
@@ -160,6 +183,15 @@
         }
     }
 
+    /** @hide Until ready for a public API change */
+    @Override
+    synchronized public SocketAddress getLocalAddress() throws IOException {
+        if (!isOpen()) {
+            throw new ClosedChannelException();
+        }
+        return isBound ? new InetSocketAddress(localAddress, localPort) : null;
+    }
+
     @Override
     synchronized public boolean isConnected() {
         return status == SOCKET_STATUS_CONNECTED;
@@ -526,21 +558,6 @@
         }
 
         @Override
-        public boolean isBound() {
-            return channel.isBound;
-        }
-
-        @Override
-        public boolean isConnected() {
-            return channel.isConnected();
-        }
-
-        @Override
-        public InetAddress getLocalAddress() {
-            return channel.localAddress != null ? channel.localAddress : Inet4Address.ANY;
-        }
-
-        @Override
         public void connect(SocketAddress remoteAddr, int timeout) throws IOException {
             if (!channel.isBlocking()) {
                 throw new IllegalBlockingModeException();
diff --git a/luni/src/main/java/java/nio/channels/AlreadyBoundException.java b/luni/src/main/java/java/nio/channels/AlreadyBoundException.java
new file mode 100644
index 0000000..0a35fc3
--- /dev/null
+++ b/luni/src/main/java/java/nio/channels/AlreadyBoundException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 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.nio.channels;
+
+/**
+ * An {@code AlreadyBoundException} is thrown when an attempt is made to bind a NetworkChannel that
+ * is already bound.
+ *
+ * @hide Until ready for a public API change
+ */
+public class AlreadyBoundException extends IllegalStateException {
+
+  private static final long serialVersionUID = 6796072983322737592L;
+
+  public AlreadyBoundException() {
+  }
+}
diff --git a/luni/src/main/java/java/nio/channels/DatagramChannel.java b/luni/src/main/java/java/nio/channels/DatagramChannel.java
index 486b168..2040b8e 100644
--- a/luni/src/main/java/java/nio/channels/DatagramChannel.java
+++ b/luni/src/main/java/java/nio/channels/DatagramChannel.java
@@ -40,7 +40,7 @@
  * same time.
  */
 public abstract class DatagramChannel extends AbstractSelectableChannel
-        implements ByteChannel, ScatteringByteChannel, GatheringByteChannel {
+        implements ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel {
 
     /**
      * Constructs a new {@code DatagramChannel}.
@@ -88,6 +88,22 @@
      */
     public abstract DatagramSocket socket();
 
+    /** @hide Until ready for a public API change */
+    @Override
+    public DatagramChannel bind(SocketAddress local) throws IOException {
+        // This method was added for interoperability with Java 7, where it is abstract. It is
+        // concrete here to avoid breaking existing Android applications that extend this class.
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /** @hide Until ready for a public API change */
+    @Override
+    public SocketAddress getLocalAddress() throws IOException {
+        // This method was added for interoperability with Java 7, where it is abstract. It is
+        // concrete here to avoid breaking existing Android applications that extend this class.
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
     /**
      * Returns whether this channel's socket is connected or not.
      *
diff --git a/luni/src/main/java/java/nio/channels/NetworkChannel.java b/luni/src/main/java/java/nio/channels/NetworkChannel.java
new file mode 100644
index 0000000..b3505b9
--- /dev/null
+++ b/luni/src/main/java/java/nio/channels/NetworkChannel.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 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.nio.channels;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.net.SocketAddress;
+
+/**
+ * A common interface for channels that are backed by network sockets.
+ *
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public interface NetworkChannel extends AutoCloseable, Channel, Closeable {
+
+  /**
+   * Binds this channel to the given local socket address. If the {@code localAddr} is set
+   * to {@code null} the socket will be bound to an available local address on any free port of
+   * the system.
+   *
+   * @param local
+   *     the local machine address and port to bind on.
+   * @return this channel.
+   * @throws UnsupportedAddressTypeException
+   *     if the {@code SocketAddress} is not supported.
+   * @throws ClosedChannelException
+   *     if the channel is closed.
+   * @throws AlreadyBoundException
+   *     if the channel is already bound.
+   * @throws IOException
+   *     if another I/O error occurs.
+   * @hide Until ready for a public API change
+   */
+  NetworkChannel bind(SocketAddress local) throws IOException;
+
+  /**
+   * Returns the local socket address the channel is bound to. The socket may be bound explicitly
+   * via {@link #bind(java.net.SocketAddress)} or similar methods, or as a side-effect when other
+   * methods are called, depending on the implementation. If the channel is not bound {@code null}
+   * is returned.
+   *
+   * <p>If IP is being used, the returned object will be a subclass of
+   * {@link java.net.InetSocketAddress}
+   *
+   * @return the local socket address, or {@code null} if the socket is not bound
+   * @throws ClosedChannelException
+   *     if the channel is closed.
+   * @throws IOException
+   *     if another I/O error occurs.
+   * @hide Until ready for a public API change
+   */
+  SocketAddress getLocalAddress() throws IOException;
+}
diff --git a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
index f3c3390..aeb6d8f 100644
--- a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
+++ b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
@@ -19,6 +19,7 @@
 
 import java.io.IOException;
 import java.net.ServerSocket;
+import java.net.SocketAddress;
 import java.nio.channels.spi.AbstractSelectableChannel;
 import java.nio.channels.spi.SelectorProvider;
 
@@ -34,7 +35,8 @@
  * {@link NotYetBoundException}. It can be bound by calling the bind method of a
  * related {@code ServerSocket} instance.
  */
-public abstract class ServerSocketChannel extends AbstractSelectableChannel {
+public abstract class ServerSocketChannel extends AbstractSelectableChannel
+        implements NetworkChannel {
 
     /**
      * Constructs a new {@link ServerSocketChannel}.
@@ -82,6 +84,52 @@
     public abstract ServerSocket socket();
 
     /**
+     * {@inheritDoc}
+     *
+     * <p>This is equivalent to {@code bind(local, 0)}.
+     * @hide Until ready for a public API change
+     */
+    @Override
+    public final ServerSocketChannel bind(SocketAddress local) throws IOException {
+        return bind(local, 0);
+    }
+
+    /**
+     * Binds this server channel to the given local socket address. If the {@code localAddr} is set
+     * to {@code null} the socket will be bound to an available local address on any free port of
+     * the system.
+     *
+     * @param localAddr
+     *             the local machine address and port to bind on.
+     * @param backlog the maximum number of unaccepted connections. Passing 0 or
+     *             a negative value yields the default backlog of 50.
+     * @return this {@code ServerSocketChannel}.
+     * @throws UnsupportedAddressTypeException
+     *             if the {@code SocketAddress} is not supported.
+     * @throws ClosedChannelException
+     *             if the channel is closed.
+     * @throws AlreadyBoundException
+     *             if the channel is already bound.
+     * @throws IOException
+     *             if another I/O error occurs.
+     * @since 1.7
+     * @hide Until ready for a public API change
+     */
+    public ServerSocketChannel bind(SocketAddress localAddr, int backlog) throws IOException {
+        // This method was added for interoperability with Java 7, where it is abstract. It is
+        // concrete here to avoid breaking existing Android applications that extend this class.
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /** @hide Until ready for a public API change */
+     @Override
+    public SocketAddress getLocalAddress() throws IOException {
+        // This method was added for interoperability with Java 7, where it is abstract. It is
+        // concrete here to avoid breaking existing Android applications that extend this class.
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /**
      * Accepts a connection to this server-socket channel.
      * <p>
      * This method returns {@code null} when this channel is non-blocking and no
diff --git a/luni/src/main/java/java/nio/channels/SocketChannel.java b/luni/src/main/java/java/nio/channels/SocketChannel.java
index da5b7ea..12dfe38 100644
--- a/luni/src/main/java/java/nio/channels/SocketChannel.java
+++ b/luni/src/main/java/java/nio/channels/SocketChannel.java
@@ -62,7 +62,7 @@
  * processing, calls to {@link #read} and {@link #write} will block.
  */
 public abstract class SocketChannel extends AbstractSelectableChannel implements
-        ByteChannel, ScatteringByteChannel, GatheringByteChannel {
+        ByteChannel, ScatteringByteChannel, GatheringByteChannel, NetworkChannel {
 
     /**
      * Constructs a new {@code SocketChannel}.
@@ -140,6 +140,22 @@
      */
     public abstract Socket socket();
 
+    /** @hide Until ready for a public API change */
+    @Override
+    public SocketChannel bind(SocketAddress local) throws IOException {
+        // This method was added for interoperability with Java 7, where it is abstract. It is
+        // concrete here to avoid breaking existing Android applications that extend this class.
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
+    /** @hide Until ready for a public API change */
+    @Override
+    public SocketAddress getLocalAddress() throws IOException {
+        // This method was added for interoperability with Java 7, where it is abstract. It is
+        // concrete here to avoid breaking existing Android applications that extend this class.
+        throw new UnsupportedOperationException("Subclasses must override this method");
+    }
+
     /**
      * Indicates whether this channel's socket is connected.
      *
diff --git a/luni/src/test/java/libcore/java/net/DatagramSocketTest.java b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java
new file mode 100644
index 0000000..86e47ec
--- /dev/null
+++ b/luni/src/test/java/libcore/java/net/DatagramSocketTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 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.java.net;
+
+import junit.framework.TestCase;
+
+import java.net.DatagramSocket;
+import java.net.InetSocketAddress;
+
+public class DatagramSocketTest extends TestCase {
+
+  public void testInitialState() throws Exception {
+    DatagramSocket ds = new DatagramSocket();
+    try {
+      assertTrue(ds.isBound());
+      assertTrue(ds.getBroadcast()); // The RI starts DatagramSocket in broadcast mode.
+      assertFalse(ds.isClosed());
+      assertFalse(ds.isConnected());
+      assertTrue(ds.getLocalPort() > 0);
+      assertTrue(ds.getLocalAddress().isAnyLocalAddress());
+      InetSocketAddress socketAddress = (InetSocketAddress) ds.getLocalSocketAddress();
+      assertEquals(ds.getLocalPort(), socketAddress.getPort());
+      assertEquals(ds.getLocalAddress(), socketAddress.getAddress());
+      assertNull(ds.getInetAddress());
+      assertEquals(-1, ds.getPort());
+      assertNull(ds.getRemoteSocketAddress());
+      assertFalse(ds.getReuseAddress());
+      assertNull(ds.getChannel());
+    } finally {
+      ds.close();
+    }
+  }
+
+  public void testStateAfterClose() throws Exception {
+    DatagramSocket ds = new DatagramSocket();
+    ds.close();
+    assertTrue(ds.isBound());
+    assertTrue(ds.isClosed());
+    assertFalse(ds.isConnected());
+    assertNull(ds.getLocalAddress());
+    assertEquals(-1, ds.getLocalPort());
+    assertNull(ds.getLocalSocketAddress());
+  }
+}
diff --git a/luni/src/test/java/libcore/java/net/ServerSocketTest.java b/luni/src/test/java/libcore/java/net/ServerSocketTest.java
index fe9d423..d82e934 100644
--- a/luni/src/test/java/libcore/java/net/ServerSocketTest.java
+++ b/luni/src/test/java/libcore/java/net/ServerSocketTest.java
@@ -17,6 +17,8 @@
 package libcore.java.net;
 
 import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
 
@@ -43,4 +45,34 @@
         t.join();
         assertEquals(0, result[0].getSoTimeout());
     }
+
+    public void testInitialState() throws Exception {
+        ServerSocket ss = new ServerSocket();
+        try {
+            assertFalse(ss.isBound());
+            assertFalse(ss.isClosed());
+            assertEquals(-1, ss.getLocalPort());
+            assertNull(ss.getLocalSocketAddress());
+            assertNull(ss.getInetAddress());
+            assertTrue(ss.getReuseAddress());
+            assertNull(ss.getChannel());
+        } finally {
+            ss.close();
+        }
+    }
+
+    public void testStateAfterClose() throws Exception {
+        ServerSocket ss = new ServerSocket();
+        ss.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 0));
+        InetSocketAddress boundAddress = (InetSocketAddress) ss.getLocalSocketAddress();
+        ss.close();
+
+        assertTrue(ss.isBound());
+        assertTrue(ss.isClosed());
+        assertEquals(boundAddress.getAddress(), ss.getInetAddress());
+        assertEquals(boundAddress.getPort(), ss.getLocalPort());
+
+        InetSocketAddress localAddressAfterClose = (InetSocketAddress) ss.getLocalSocketAddress();
+        assertEquals(boundAddress, localAddressAfterClose);
+    }
 }
diff --git a/luni/src/test/java/libcore/java/net/SocketTest.java b/luni/src/test/java/libcore/java/net/SocketTest.java
index 42b7250..b9ed99c 100644
--- a/luni/src/test/java/libcore/java/net/SocketTest.java
+++ b/luni/src/test/java/libcore/java/net/SocketTest.java
@@ -20,6 +20,7 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ConnectException;
+import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
@@ -89,7 +90,7 @@
         // Open a local server port.
         ServerSocketChannel ssc = ServerSocketChannel.open();
         InetSocketAddress listenAddr = new InetSocketAddress(host, 0);
-        ssc.socket().bind(listenAddr, 0);
+        ssc.bind(listenAddr, 0);
         ServerSocket ss = ssc.socket();
 
         // Open a socket to the local port.
@@ -109,10 +110,12 @@
             in.socket().setTcpNoDelay(false);
         }
 
+        InetSocketAddress listenAddress = (InetSocketAddress) in.getLocalAddress();
         InetSocketAddress outRemoteAddress = (InetSocketAddress) out.socket().getRemoteSocketAddress();
         InetSocketAddress outLocalAddress = (InetSocketAddress) out.socket().getLocalSocketAddress();
         InetSocketAddress inLocalAddress = (InetSocketAddress) in.socket().getLocalSocketAddress();
         InetSocketAddress inRemoteAddress = (InetSocketAddress) in.socket().getRemoteSocketAddress();
+        System.err.println("listenAddress: " + listenAddr);
         System.err.println("inLocalAddress: " + inLocalAddress);
         System.err.println("inRemoteAddress: " + inRemoteAddress);
         System.err.println("outLocalAddress: " + outLocalAddress);
@@ -127,14 +130,42 @@
         assertEquals(outLocalAddress.getAddress(), ss.getInetAddress());
         assertEquals(outRemoteAddress.getAddress(), ss.getInetAddress());
 
+        assertFalse(ssc.socket().isClosed());
+        assertTrue(ssc.socket().isBound());
+        assertTrue(in.isConnected());
+        assertTrue(in.socket().isConnected());
+        assertTrue(out.socket().isConnected());
+        assertTrue(out.isConnected());
+
         in.close();
         out.close();
         ssc.close();
 
+        assertTrue(ssc.socket().isClosed());
+        assertTrue(ssc.socket().isBound());
+        assertFalse(in.isConnected());
+        assertFalse(in.socket().isConnected());
+        assertFalse(out.socket().isConnected());
+        assertFalse(out.isConnected());
+
         assertNull(in.socket().getRemoteSocketAddress());
         assertNull(out.socket().getRemoteSocketAddress());
 
-        assertEquals(in.socket().getLocalSocketAddress(), ss.getLocalSocketAddress());
+        // As per docs and RI - server socket local address methods continue to return the bind()
+        // addresses even after close().
+        assertEquals(listenAddress, ssc.socket().getLocalSocketAddress());
+
+        // As per docs and RI - socket local address methods return the wildcard address before
+        // bind() and after close(), but the port will be the same as it was before close().
+        InetSocketAddress inLocalAddressAfterClose =
+                (InetSocketAddress) in.socket().getLocalSocketAddress();
+        assertTrue(inLocalAddressAfterClose.getAddress().isAnyLocalAddress());
+        assertEquals(inLocalAddress.getPort(), inLocalAddressAfterClose.getPort());
+
+        InetSocketAddress outLocalAddressAfterClose =
+                (InetSocketAddress) out.socket().getLocalSocketAddress();
+        assertTrue(outLocalAddressAfterClose.getAddress().isAnyLocalAddress());
+        assertEquals(outLocalAddress.getPort(), outLocalAddressAfterClose.getPort());
     }
 
     // SocketOptions.setOption has weird behavior for setSoLinger/SO_LINGER.
@@ -286,6 +317,42 @@
         serverSocket.close();
     }
 
+    public void testInitialState() throws Exception {
+        Socket s = new Socket();
+        try {
+            assertFalse(s.isBound());
+            assertFalse(s.isClosed());
+            assertFalse(s.isConnected());
+            assertEquals(-1, s.getLocalPort());
+            assertTrue(s.getLocalAddress().isAnyLocalAddress());
+            assertNull(s.getLocalSocketAddress());
+            assertNull(s.getInetAddress());
+            assertEquals(0, s.getPort());
+            assertNull(s.getRemoteSocketAddress());
+            assertFalse(s.getReuseAddress());
+            assertNull(s.getChannel());
+        } finally {
+            s.close();
+        }
+    }
+
+    public void testStateAfterClose() throws Exception {
+        Socket s = new Socket();
+        s.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 0));
+        InetSocketAddress boundAddress = (InetSocketAddress) s.getLocalSocketAddress();
+        s.close();
+
+        assertTrue(s.isBound());
+        assertTrue(s.isClosed());
+        assertFalse(s.isConnected());
+        assertTrue(s.getLocalAddress().isAnyLocalAddress());
+        assertEquals(boundAddress.getPort(), s.getLocalPort());
+
+        InetSocketAddress localAddressAfterClose = (InetSocketAddress) s.getLocalSocketAddress();
+        assertTrue(localAddressAfterClose.getAddress().isAnyLocalAddress());
+        assertEquals(boundAddress.getPort(), localAddressAfterClose.getPort());
+    }
+
     static class MockServer {
         private ExecutorService executor;
         private ServerSocket serverSocket;
diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
index 13757d2..a0092d0 100644
--- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
@@ -48,11 +48,35 @@
         DatagramChannel dc = DatagramChannel.open();
         try {
             dc.configureBlocking(false);
-            dc.socket().bind(null);
+            dc.bind(null);
             // Should return immediately, since we're non-blocking.
             assertNull(dc.receive(ByteBuffer.allocate(2048)));
         } finally {
             dc.close();
         }
     }
+
+    public void testInitialState() throws Exception {
+        DatagramChannel dc = DatagramChannel.open();
+        try {
+            assertNull(dc.getLocalAddress());
+
+            DatagramSocket socket = dc.socket();
+            assertFalse(socket.isBound());
+            assertFalse(socket.getBroadcast());
+            assertFalse(socket.isClosed());
+            assertFalse(socket.isConnected());
+            assertEquals(0, socket.getLocalPort());
+            assertTrue(socket.getLocalAddress().isAnyLocalAddress());
+            assertNull(socket.getLocalSocketAddress());
+            assertNull(socket.getInetAddress());
+            assertEquals(-1, socket.getPort());
+            assertNull(socket.getRemoteSocketAddress());
+            assertFalse(socket.getReuseAddress());
+
+            assertSame(dc, socket.getChannel());
+        } finally {
+            dc.close();
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
index e66096c..bceb759 100644
--- a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
@@ -16,7 +16,14 @@
 
 package libcore.java.nio.channels;
 
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.ServerSocket;
 import java.nio.channels.ServerSocketChannel;
+import java.nio.channels.SocketChannel;
+import java.util.Enumeration;
 
 public class ServerSocketChannelTest extends junit.framework.TestCase {
     // http://code.google.com/p/android/issues/detail?id=16579
@@ -24,11 +31,100 @@
         ServerSocketChannel ssc = ServerSocketChannel.open();
         try {
             ssc.configureBlocking(false);
-            ssc.socket().bind(null);
+            ssc.bind(null);
             // Should return immediately, since we're non-blocking.
             assertNull(ssc.accept());
         } finally {
             ssc.close();
         }
     }
+
+    /** Checks the state of the ServerSocketChannel and associated ServerSocket after open() */
+    public void test_open_initialState() throws Exception {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        try {
+            assertNull(ssc.getLocalAddress());
+
+            ServerSocket socket = ssc.socket();
+            assertFalse(socket.isBound());
+            assertFalse(socket.isClosed());
+            assertEquals(-1, socket.getLocalPort());
+            assertNull(socket.getLocalSocketAddress());
+            assertNull(socket.getInetAddress());
+            assertTrue(socket.getReuseAddress());
+
+            assertSame(ssc, socket.getChannel());
+        } finally {
+            ssc.close();
+        }
+    }
+
+    public void test_bind_nullBindsToAll() throws Exception {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.bind(null);
+        InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress();
+        assertTrue(boundAddress.getAddress().isAnyLocalAddress());
+        assertFalse(boundAddress.getAddress().isLinkLocalAddress());
+        assertFalse(boundAddress.getAddress().isLoopbackAddress());
+
+        // Attempt to connect to the "any" address.
+        assertTrue(canConnect(boundAddress));
+
+        // Go through all local IPs and try to connect to each in turn - all should succeed.
+        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+        while (interfaces.hasMoreElements()) {
+            NetworkInterface nic = interfaces.nextElement();
+            Enumeration<InetAddress> inetAddresses = nic.getInetAddresses();
+            while (inetAddresses.hasMoreElements()) {
+                InetSocketAddress address =
+                        new InetSocketAddress(inetAddresses.nextElement(), boundAddress.getPort());
+                assertTrue(canConnect(address));
+            }
+        }
+
+        ssc.close();
+    }
+
+    public void test_bind_loopback() throws Exception {
+        ServerSocketChannel ssc = ServerSocketChannel.open();
+        ssc.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
+        InetSocketAddress boundAddress = (InetSocketAddress) ssc.getLocalAddress();
+        assertFalse(boundAddress.getAddress().isAnyLocalAddress());
+        assertFalse(boundAddress.getAddress().isLinkLocalAddress());
+        assertTrue(boundAddress.getAddress().isLoopbackAddress());
+
+        // Attempt to connect to the "loopback" address. Note: There can be several loopback
+        // addresses, such as 127.0.0.1 (IPv4) and 0:0:0:0:0:0:0:1 (IPv6) and only one will be
+        // bound.
+        InetSocketAddress loopbackAddress =
+                new InetSocketAddress(InetAddress.getLoopbackAddress(), boundAddress.getPort());
+        assertTrue(canConnect(loopbackAddress));
+
+        // Go through all local IPs and try to connect to each in turn - all should fail except
+        // for the loopback.
+        Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+        while (interfaces.hasMoreElements()) {
+            NetworkInterface nic = interfaces.nextElement();
+            Enumeration<InetAddress> inetAddresses = nic.getInetAddresses();
+            while (inetAddresses.hasMoreElements()) {
+                InetSocketAddress address =
+                        new InetSocketAddress(inetAddresses.nextElement(), boundAddress.getPort());
+                if (!address.equals(loopbackAddress)) {
+                    assertFalse(canConnect(address));
+                }
+            }
+        }
+
+        ssc.close();
+    }
+
+    private static boolean canConnect(InetSocketAddress address) {
+        try {
+            SocketChannel socketChannel = SocketChannel.open(address);
+            socketChannel.close();
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
 }
diff --git a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
index fbb2d8d..553e20c 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
@@ -16,29 +16,34 @@
 
 package libcore.java.nio.channels;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.ConnectException;
+import java.net.Socket;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
-import java.net.Socket;
 import java.nio.ByteBuffer;
 import java.nio.channels.ClosedChannelException;
+import java.nio.channels.SocketChannel;
 import java.nio.channels.Selector;
 import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
 import tests.io.MockOs;
+
 import static libcore.io.OsConstants.*;
 
 public class SocketChannelTest extends junit.framework.TestCase {
+
   private final MockOs mockOs = new MockOs();
 
-  @Override public void setUp() throws Exception {
+  @Override
+  public void setUp() throws Exception {
     mockOs.install();
   }
 
-  @Override protected void tearDown() throws Exception {
+  @Override
+  protected void tearDown() throws Exception {
     mockOs.uninstall();
   }
 
@@ -129,6 +134,81 @@
     ss.close();
   }
 
+  /** Checks the state of the SocketChannel and associated Socket after open() */
+  public void test_open_initialState() throws Exception {
+    SocketChannel sc = SocketChannel.open();
+    try {
+      assertNull(sc.getLocalAddress());
+
+      Socket socket = sc.socket();
+      assertFalse(socket.isBound());
+      assertFalse(socket.isClosed());
+      assertFalse(socket.isConnected());
+      assertEquals(-1, socket.getLocalPort());
+      assertTrue(socket.getLocalAddress().isAnyLocalAddress());
+      assertNull(socket.getLocalSocketAddress());
+      assertNull(socket.getInetAddress());
+      assertEquals(0, socket.getPort());
+      assertNull(socket.getRemoteSocketAddress());
+      assertFalse(socket.getReuseAddress());
+
+      assertSame(sc, socket.getChannel());
+    } finally {
+      sc.close();
+    }
+  }
+
+  /** Checks that the SocketChannel and associated Socket agree on the socket state. */
+  public void test_bind_socketStateSync() throws IOException {
+    SocketChannel sc = SocketChannel.open();
+    assertNull(sc.getLocalAddress());
+
+    Socket socket = sc.socket();
+    assertNull(socket.getLocalSocketAddress());
+    assertFalse(socket.isBound());
+
+    InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+    sc.bind(bindAddr);
+
+    InetSocketAddress actualAddr = (InetSocketAddress) sc.getLocalAddress();
+    assertEquals(actualAddr, socket.getLocalSocketAddress());
+    assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+    assertTrue(socket.isBound());
+    assertFalse(socket.isConnected());
+    assertFalse(socket.isClosed());
+
+    sc.close();
+
+    assertFalse(sc.isOpen());
+    assertTrue(socket.isClosed());
+  }
+
+  /**
+   * Checks that the SocketChannel and associated Socket agree on the socket state, even if
+   * the Socket object is requested/created after bind().
+   */
+  public void test_bind_socketObjectCreationAfterBind() throws IOException {
+    SocketChannel sc = SocketChannel.open();
+    assertNull(sc.getLocalAddress());
+
+    InetSocketAddress bindAddr = new InetSocketAddress("localhost", 0);
+    sc.bind(bindAddr);
+
+    // Socket object creation after bind().
+    Socket socket = sc.socket();
+    InetSocketAddress actualAddr = (InetSocketAddress) sc.getLocalAddress();
+    assertEquals(actualAddr, socket.getLocalSocketAddress());
+    assertEquals(bindAddr.getHostName(), actualAddr.getHostName());
+    assertTrue(socket.isBound());
+    assertFalse(socket.isConnected());
+    assertFalse(socket.isClosed());
+
+    sc.close();
+
+    assertFalse(sc.isOpen());
+    assertTrue(socket.isClosed());
+  }
+
   /**
    * Tests connect() and object state for a blocking SocketChannel. Blocking mode is the default.
    */