Implementing bind() and getLocalAddress() for NIO2.
This change introduces NetworkChannel, but only bind() and
getLocalAddress() methods. To avoid breaking existing Android
applications that extend ServerSocketChannel, DatagramChannel and
SocketChannel the methods have been added with concrete
implementations that throw exceptions rather than leaving them
abstract.
In channel tests, usages of channel.socket().bind() and
channel.socket().getLocalSocketAddress() have been changed to
channel.bind() and channel.getLocalAddress(), since the behavior is
close enough and the tests should be written against the channel APIs
as much as possible. Tests have been added for new methods.
Removed further overriding in
DatagramChannelImpl.DatagramSocketAdapter and
SocketChannelImpl.SocketAdapter which revealed some bugs and lack of
clarity in the docs for the socket methods:
Improved the documentation for DatagramSocket.getLocalAddress(),
DatagramSocket.getLocalSocketAddress(),
ServerSocket.getInetAddress(), ServerSocket.getLocalPort(),
ServerSocket.getLocalSocketAddress(), Socket.getLocalAddress(),
Socket.getLocalPort(), Socket.getLocalSocketAddress(). These methods
treat special cases differently.
Fixed a bug in DatagramSocket.getLocalSocketAddress() where it would
incorrect throw an exception if the socket has been closed, which
contradicts the (updated) documentation and the RI. It now returns
null. Added tests.
Fixed a bug in Socket.close(): a closed socket would still report as
being connected. Added tests.
Fixed a bug in Socket.startupSocket() - the socket was recording it
was being bound, but was not updating the cached local address. This
method is called during ServerSocketChannel.accept() and would cause
the socket to report its local address as being the wildcard address,
when it is not. Added a test.
Change-Id: Ibec8527e1c72597e268d23e6c1f03eb16e46cdc4
Bug: 12464155
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.
*/