summaryrefslogtreecommitdiffstats
path: root/luni/src
diff options
context:
space:
mode:
authorNeil Fuller <nfuller@google.com>2014-01-14 09:46:54 +0000
committerNeil Fuller <nfuller@google.com>2014-01-29 16:58:52 +0000
commit933fbbf606268eec9fc430632b8bca7002a833b3 (patch)
tree572d963c8b6f0712491ebd652989686be4015a58 /luni/src
parentfff149a4eaa55e9eacddd22a386569b863092973 (diff)
downloadlibcore-933fbbf606268eec9fc430632b8bca7002a833b3.zip
libcore-933fbbf606268eec9fc430632b8bca7002a833b3.tar.gz
libcore-933fbbf606268eec9fc430632b8bca7002a833b3.tar.bz2
Refactoring in preparation for NIO2 changes.
Making the various Socket/Channel classes more consistent with each other for synchronizing state between the Channel, Socket and the OS. This is in preparation for NetworkChannel.bind(). Synchronizing state revealed a test that relied upon a Socket from a channel not reporting isConnected() properly after a connect(). Tests have been updated. Reading the local address back from the OS revealed that Android is using IPv6 and reports the string equivalent of IPv4's 0.0.0.0 as "::". Updated a test that was relying on the string form. Calling connect() twice on a DatagramSocket appears inconsistent with itself and Channel and other Sockets. A pure DatagramSocket switches over to the new remote address. I have changed the Channel-backed DatagramSocket to throw an IllegalStateException exception, and have created a bug to discuss making the calls more consistent. Socket has been modified to avoid using the address field to store the local address after a bind(). This field is documented as holding the remote address, not the local. Changed implementation of SocketChannelImpl.socket().getInputStream() and SocketChannelImpl.socket().getOutputStream() to use the one that is implemented by Socket and not a custom NIO-based implementation. The use of NIO provided two parallel implementations for the same thing. This change alters behavior when attempting to read zero bytes and when a Channel is in non-blocking mode: now it throws IllegalBlockingModeException rather than ArrayIndexOutOfBoundsException. The tests have been updated. Various tidying up changes to introduce @Override, remove unnecessarily initialized local variables, fields, semi-colons, javadocs and imports. Added close() calls for objects during tests that were cluttering the logs. Modified IoBridge.connect() to be void. Adjusted SocketChannelImpl accordingly and tidied up impossible cases. Modified SocketChannel.connect() so that it always returns false in non-blocking mode. This looks like an existing bug: it would previously have returned true, even though the connection was potentially still pending. Tests have been added. Also tidied up SocketChannelImpl.finishConnect() - it was potentially resetting the isBound state. Change-Id: Ic7943615b4b763f77e74397e0e91a62edc7d7017 bug: 12464155
Diffstat (limited to 'luni/src')
-rw-r--r--luni/src/main/java/java/net/DatagramSocket.java57
-rw-r--r--luni/src/main/java/java/net/DatagramSocketImpl.java36
-rw-r--r--luni/src/main/java/java/net/PlainDatagramSocketImpl.java35
-rw-r--r--luni/src/main/java/java/net/PlainSocketImpl.java39
-rw-r--r--luni/src/main/java/java/net/ServerSocket.java29
-rw-r--r--luni/src/main/java/java/net/Socket.java60
-rw-r--r--luni/src/main/java/java/net/SocketImpl.java27
-rw-r--r--luni/src/main/java/java/nio/DatagramChannelImpl.java211
-rw-r--r--luni/src/main/java/java/nio/ServerSocketChannelImpl.java55
-rw-r--r--luni/src/main/java/java/nio/SocketChannelImpl.java280
-rw-r--r--luni/src/main/java/libcore/io/IoBridge.java15
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java16
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java95
13 files changed, 660 insertions, 295 deletions
diff --git a/luni/src/main/java/java/net/DatagramSocket.java b/luni/src/main/java/java/net/DatagramSocket.java
index 49f141a..e2acac7 100644
--- a/luni/src/main/java/java/net/DatagramSocket.java
+++ b/luni/src/main/java/java/net/DatagramSocket.java
@@ -114,6 +114,17 @@ public class DatagramSocket implements Closeable {
}
/**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful close()
+ * took place, without actually performing an OS close().
+ *
+ * @hide used in java.nio
+ */
+ public void onClose() {
+ isClosed = true;
+ impl.onClose();
+ }
+
+ /**
* Disconnects this UDP datagram socket from the remote host. This method
* called on an unconnected socket does nothing.
*/
@@ -127,6 +138,19 @@ public class DatagramSocket implements Closeable {
isConnected = false;
}
+ /**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful
+ * disconnect() took place, without actually performing a disconnect().
+ *
+ * @hide used in java.nio
+ */
+ public void onDisconnect() {
+ address = null;
+ port = -1;
+ isConnected = false;
+ impl.onDisconnect();
+ }
+
synchronized void createSocket(int aPort, InetAddress addr) throws SocketException {
impl = factory != null ? factory.createDatagramSocketImpl()
: new PlainDatagramSocketImpl();
@@ -439,9 +463,12 @@ public class DatagramSocket implements Closeable {
*/
public void bind(SocketAddress localAddr) throws SocketException {
checkOpen();
- int localPort = 0;
- InetAddress addr = Inet4Address.ANY;
- if (localAddr != null) {
+ int localPort;
+ InetAddress addr;
+ if (localAddr == null) {
+ localPort = 0;
+ addr = Inet4Address.ANY;
+ } else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
localAddr.getClass());
@@ -459,6 +486,17 @@ public class DatagramSocket implements Closeable {
}
/**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful bind()
+ * took place, without actually performing an OS bind().
+ *
+ * @hide used in java.nio
+ */
+ public void onBind(InetAddress localAddress, int localPort) {
+ isBound = true;
+ impl.onBind(localAddress, localPort);
+ }
+
+ /**
* Connects this datagram socket to the address and port specified by {@code peer}.
* Future calls to {@link #send} will use this as the default target, and {@link #receive}
* will only accept packets from this source.
@@ -492,6 +530,19 @@ public class DatagramSocket implements Closeable {
}
/**
+ * Sets the DatagramSocket and its related DatagramSocketImpl state as if a successful connect()
+ * took place, without actually performing an OS connect().
+ *
+ * @hide used in java.nio
+ */
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ isConnected = true;
+ this.address = remoteAddress;
+ this.port = remotePort;
+ impl.onConnect(remoteAddress, remotePort);
+ }
+
+ /**
* Connects this datagram socket to the specific {@code address} and {@code port}.
* Future calls to {@link #send} will use this as the default target, and {@link #receive}
* will only accept packets from this source.
diff --git a/luni/src/main/java/java/net/DatagramSocketImpl.java b/luni/src/main/java/java/net/DatagramSocketImpl.java
index 097eb17..1a39987 100644
--- a/luni/src/main/java/java/net/DatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/DatagramSocketImpl.java
@@ -268,4 +268,40 @@ public abstract class DatagramSocketImpl implements SocketOptions {
* if an error occurs while peeking at the data.
*/
protected abstract int peekData(DatagramPacket pack) throws IOException;
+
+ /**
+ * Initialize the bind() state.
+ * @hide used in java.nio.
+ */
+ protected void onBind(InetAddress localAddress, int localPort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the connect() state.
+ * @hide used in java.nio.
+ */
+ protected void onConnect(InetAddress remoteAddress, int remotePort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the disconnected state.
+ * @hide used in java.nio.
+ */
+ protected void onDisconnect() {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the closed state.
+ * @hide used in java.nio.
+ */
+ protected void onClose() {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
}
diff --git a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
index 3226527..edf7024 100644
--- a/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainDatagramSocketImpl.java
@@ -20,14 +20,6 @@ package java.net;
import dalvik.system.CloseGuard;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocketImpl;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.NetworkInterface;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.UnknownHostException;
import libcore.io.ErrnoException;
import libcore.io.IoBridge;
import libcore.io.Libcore;
@@ -78,6 +70,11 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
}
@Override
+ protected void onBind(InetAddress localAddress, int localPort) {
+ this.localPort = localPort;
+ }
+
+ @Override
public synchronized void close() {
guard.close();
try {
@@ -87,6 +84,11 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
}
@Override
+ protected void onClose() {
+ guard.close();
+ }
+
+ @Override
public void create() throws SocketException {
this.fd = IoBridge.socket(false);
}
@@ -179,7 +181,8 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
public void send(DatagramPacket packet) throws IOException {
int port = isNativeConnected ? 0 : packet.getPort();
InetAddress address = isNativeConnected ? null : packet.getAddress();
- IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address, port);
+ IoBridge.sendto(fd, packet.getData(), packet.getOffset(), packet.getLength(), 0, address,
+ port);
}
public void setOption(int option, Object value) throws SocketException {
@@ -211,6 +214,13 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
}
@Override
+ protected void onConnect(InetAddress remoteAddress, int remotePort) {
+ isNativeConnected = true;
+ connectedAddress = remoteAddress;
+ connectedPort = remotePort;
+ }
+
+ @Override
public void disconnect() {
try {
Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
@@ -224,6 +234,13 @@ public class PlainDatagramSocketImpl extends DatagramSocketImpl {
isNativeConnected = false;
}
+ @Override
+ protected void onDisconnect() {
+ connectedPort = -1;
+ connectedAddress = null;
+ isNativeConnected = false;
+ }
+
/**
* Set the received address and port in the packet. We do this when the
* Datagram socket is connected at the native level and the
diff --git a/luni/src/main/java/java/net/PlainSocketImpl.java b/luni/src/main/java/java/net/PlainSocketImpl.java
index 18942d6..d81303f 100644
--- a/luni/src/main/java/java/net/PlainSocketImpl.java
+++ b/luni/src/main/java/java/net/PlainSocketImpl.java
@@ -22,15 +22,6 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.net.ConnectException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.net.SocketImpl;
-import java.net.SocketTimeoutException;
-import java.net.UnknownHostException;
import java.nio.ByteOrder;
import java.util.Arrays;
import libcore.io.ErrnoException;
@@ -120,15 +111,6 @@ public class PlainSocketImpl extends SocketImpl {
return proxy != null && proxy.type() == Proxy.Type.SOCKS;
}
- public void initLocalPort(int localPort) {
- this.localport = localPort;
- }
-
- public void initRemoteAddressAndPort(InetAddress remoteAddress, int remotePort) {
- this.address = remoteAddress;
- this.port = remotePort;
- }
-
private void checkNotClosed() throws IOException {
if (!fd.valid()) {
throw new SocketException("Socket is closed");
@@ -148,7 +130,6 @@ public class PlainSocketImpl extends SocketImpl {
@Override protected void bind(InetAddress address, int port) throws IOException {
IoBridge.bind(fd, address, port);
- this.address = address;
if (port != 0) {
this.localport = port;
} else {
@@ -157,12 +138,22 @@ public class PlainSocketImpl extends SocketImpl {
}
@Override
+ public void onBind(InetAddress localAddress, int localPort) {
+ localport = localPort;
+ }
+
+ @Override
protected synchronized void close() throws IOException {
guard.close();
IoBridge.closeSocket(fd);
}
@Override
+ public void onClose() {
+ guard.close();
+ }
+
+ @Override
protected void connect(String aHost, int aPort) throws IOException {
connect(InetAddress.getByName(aHost), aPort);
}
@@ -191,8 +182,14 @@ public class PlainSocketImpl extends SocketImpl {
} else {
IoBridge.connect(fd, normalAddr, aPort, timeout);
}
- super.address = normalAddr;
- super.port = aPort;
+ address = normalAddr;
+ port = aPort;
+ }
+
+ @Override
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ address = remoteAddress;
+ port = remotePort;
}
@Override
diff --git a/luni/src/main/java/java/net/ServerSocket.java b/luni/src/main/java/java/net/ServerSocket.java
index 399511f..8b56ca1 100644
--- a/luni/src/main/java/java/net/ServerSocket.java
+++ b/luni/src/main/java/java/net/ServerSocket.java
@@ -21,6 +21,8 @@ import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.ServerSocketChannel;
+import libcore.io.IoBridge;
+
/**
* This class represents a server-side socket that waits for incoming client
* connections. A {@code ServerSocket} handles the requests and sends back an
@@ -49,6 +51,8 @@ public class ServerSocket implements Closeable {
private boolean isClosed;
+ private InetAddress localAddress;
+
/**
* Constructs a new unbound {@code ServerSocket}.
*
@@ -99,7 +103,7 @@ public class ServerSocket implements Closeable {
impl.create(true);
try {
impl.bind(addr, port);
- isBound = true;
+ readBackBindState();
impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
} catch (IOException e) {
close();
@@ -109,6 +113,14 @@ public class ServerSocket implements Closeable {
}
/**
+ * Read the cached isBound and localAddress state from the underlying OS socket.
+ */
+ private void readBackBindState() throws SocketException {
+ localAddress = IoBridge.getSocketLocalAddress(impl.fd);
+ isBound = true;
+ }
+
+ /**
* Waits for an incoming request and blocks until the connection is opened.
* This method returns a socket object representing the just opened
* connection.
@@ -161,7 +173,7 @@ public class ServerSocket implements Closeable {
if (!isBound()) {
return null;
}
- return impl.getInetAddress();
+ return localAddress;
}
/**
@@ -300,9 +312,12 @@ public class ServerSocket implements Closeable {
if (isBound()) {
throw new BindException("Socket is already bound");
}
- int port = 0;
- InetAddress addr = Inet4Address.ANY;
- if (localAddr != null) {
+ InetAddress addr;
+ int port;
+ if (localAddr == null) {
+ addr = Inet4Address.ANY;
+ port = 0;
+ } else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
localAddr.getClass());
@@ -317,7 +332,7 @@ public class ServerSocket implements Closeable {
synchronized (this) {
try {
impl.bind(addr, port);
- isBound = true;
+ readBackBindState();
impl.listen(backlog > 0 ? backlog : DEFAULT_BACKLOG);
} catch (IOException e) {
close();
@@ -336,7 +351,7 @@ public class ServerSocket implements Closeable {
if (!isBound()) {
return null;
}
- return new InetSocketAddress(getInetAddress(), getLocalPort());
+ return new InetSocketAddress(localAddress, getLocalPort());
}
/**
diff --git a/luni/src/main/java/java/net/Socket.java b/luni/src/main/java/java/net/Socket.java
index 36fdf28..517571d 100644
--- a/luni/src/main/java/java/net/Socket.java
+++ b/luni/src/main/java/java/net/Socket.java
@@ -312,12 +312,27 @@ public class Socket implements Closeable {
*/
public synchronized void close() throws IOException {
isClosed = true;
- // RI compatibility: the RI returns the any address (but the original local port) after close.
+ // RI compatibility: the RI returns the any address (but the original local port) after
+ // close.
localAddress = Inet4Address.ANY;
impl.close();
}
/**
+ * Sets the Socket and its related SocketImpl state as if a successful close() took place,
+ * without actually performing an OS close().
+ *
+ * @hide used in java.nio
+ */
+ public void onClose() {
+ isClosed = true;
+ // RI compatibility: the RI returns the any address (but the original local port) after
+ // close.
+ localAddress = Inet4Address.ANY;
+ impl.onClose();
+ }
+
+ /**
* Returns the IP address of the target host this socket is connected to, or null if this
* socket is not yet connected.
*/
@@ -329,7 +344,9 @@ public class Socket implements Closeable {
}
/**
- * Returns an input stream to read data from this socket.
+ * Returns an input stream to read data from this socket. If the socket has an associated
+ * {@link SocketChannel} and that channel is in non-blocking mode then reads from the
+ * stream will throw a {@link java.nio.channels.IllegalBlockingModeException}.
*
* @return the byte-oriented input stream.
* @throws IOException
@@ -353,8 +370,8 @@ public class Socket implements Closeable {
}
/**
- * Returns the local IP address this socket is bound to, or {@code InetAddress.ANY} if
- * the socket is unbound.
+ * 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.
*/
public InetAddress getLocalAddress() {
return localAddress;
@@ -371,7 +388,9 @@ public class Socket implements Closeable {
}
/**
- * Returns an output stream to write data into this socket.
+ * Returns an output stream to write data into this socket. If the socket has an associated
+ * {@link SocketChannel} and that channel is in non-blocking mode then writes to the
+ * stream will throw a {@link java.nio.channels.IllegalBlockingModeException}.
*
* @return the byte-oriented output stream.
* @throws IOException
@@ -744,9 +763,12 @@ public class Socket implements Closeable {
throw new BindException("Socket is already bound");
}
- int port = 0;
- InetAddress addr = Inet4Address.ANY;
- if (localAddr != null) {
+ int port;
+ InetAddress addr;
+ if (localAddr == null) {
+ port = 0;
+ addr = Inet4Address.ANY;
+ } else {
if (!(localAddr instanceof InetSocketAddress)) {
throw new IllegalArgumentException("Local address not an InetSocketAddress: " +
localAddr.getClass());
@@ -771,6 +793,17 @@ public class Socket implements Closeable {
}
/**
+ * Sets the Socket and its related SocketImpl state as if a successful bind() took place,
+ * without actually performing an OS bind().
+ *
+ * @hide used in java.nio
+ */
+ public void onBind(InetAddress localAddress, int localPort) {
+ isBound = true;
+ impl.onBind(localAddress, localPort);
+ }
+
+ /**
* Connects this socket to the given remote host address and port specified
* by the SocketAddress {@code remoteAddr}.
*
@@ -851,6 +884,17 @@ public class Socket implements Closeable {
}
/**
+ * Sets the Socket and its related SocketImpl state as if a successful connect() took place,
+ * without actually performing an OS connect().
+ *
+ * @hide internal use only
+ */
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ isConnected = true;
+ impl.onConnect(remoteAddress, remotePort);
+ }
+
+ /**
* Returns whether the incoming channel of the socket has already been
* closed.
*
diff --git a/luni/src/main/java/java/net/SocketImpl.java b/luni/src/main/java/java/net/SocketImpl.java
index 92de9cf..bd36ec7 100644
--- a/luni/src/main/java/java/net/SocketImpl.java
+++ b/luni/src/main/java/java/net/SocketImpl.java
@@ -294,4 +294,31 @@ public abstract class SocketImpl implements SocketOptions {
*/
protected void setPerformancePreferences(int connectionTime, int latency, int bandwidth) {
}
+
+ /**
+ * Initialize the bind() state.
+ * @hide used in java.nio.
+ */
+ public void onBind(InetAddress localAddress, int localPort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the connect() state.
+ * @hide used in java.nio.
+ */
+ public void onConnect(InetAddress remoteAddress, int remotePort) {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
+
+ /**
+ * Initialize the close() state.
+ * @hide used in java.nio.
+ */
+ public void onClose() {
+ // Do not add any code to these methods. They are concrete only to preserve API
+ // compatibility.
+ }
}
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 39f2128..5f74bdc 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -50,10 +50,13 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
private final FileDescriptor fd;
// Our internal DatagramSocket.
- private DatagramSocket socket = null;
+ private DatagramSocket socket;
- // The address to be connected.
- InetSocketAddress connectAddress = null;
+ // The remote address to be connected.
+ InetSocketAddress connectAddress;
+
+ // The local address.
+ InetAddress localAddress;
// local port
private int localPort;
@@ -98,19 +101,38 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
}
/**
- * @see java.nio.channels.DatagramChannel#isConnected()
+ * 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
+ * bind() or connect().
+ *
+ * @param updateSocketState
+ * if the associated socket (if present) needs to be updated
+ * @hide used to sync state, non-private to avoid synthetic method
*/
+ void onBind(boolean updateSocketState) {
+ SocketAddress sa;
+ try {
+ sa = Libcore.os.getsockname(fd);
+ } catch (ErrnoException errnoException) {
+ throw new AssertionError(errnoException);
+ }
+ isBound = true;
+ InetSocketAddress localSocketAddress = (InetSocketAddress) sa;
+ localAddress = localSocketAddress.getAddress();
+ localPort = localSocketAddress.getPort();
+ if (updateSocketState && socket != null) {
+ socket.onBind(localAddress, localPort);
+ }
+ }
+
@Override
synchronized public boolean isConnected() {
return connected;
}
- /**
- * @see java.nio.channels.DatagramChannel#connect(java.net.SocketAddress)
- */
@Override
synchronized public DatagramChannel connect(SocketAddress address) throws IOException {
- // must open
+ // must be open
checkOpen();
// status must be un-connected.
if (connected) {
@@ -119,43 +141,71 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
// check the address
InetSocketAddress inetSocketAddress = SocketChannelImpl.validateAddress(address);
+ InetAddress remoteAddress = inetSocketAddress.getAddress();
+ int remotePort = inetSocketAddress.getPort();
try {
begin();
- IoBridge.connect(fd, inetSocketAddress.getAddress(), inetSocketAddress.getPort());
+ IoBridge.connect(fd, remoteAddress, remotePort);
} catch (ConnectException e) {
// ConnectException means connect fail, not exception
} finally {
end(true);
}
- // set the connected address.
- connectAddress = inetSocketAddress;
- connected = true;
- isBound = true;
+ // connect() performs a bind() if an explicit bind() was not performed. Keep the local
+ // address state held by the channel and the socket up to date.
+ if (!isBound) {
+ onBind(true /* updateSocketState */);
+ }
+
+ // Keep the connected state held by the channel and the socket up to date.
+ onConnect(remoteAddress, remotePort, true /* updateSocketState */);
return this;
}
/**
- * @see java.nio.channels.DatagramChannel#disconnect()
+ * Initialize the state associated with being connected, optionally syncing the socket if there
+ * is one.
+ * @hide used to sync state, non-private to avoid synthetic method
*/
+ void onConnect(InetAddress remoteAddress, int remotePort, boolean updateSocketState) {
+ connected = true;
+ connectAddress = new InetSocketAddress(remoteAddress, remotePort);
+ if (updateSocketState && socket != null) {
+ socket.onConnect(remoteAddress, remotePort);
+ }
+ }
+
@Override
synchronized public DatagramChannel disconnect() throws IOException {
if (!isConnected() || !isOpen()) {
return this;
}
- connected = false;
- connectAddress = null;
+
+ // Keep the disconnected state held by the channel and the socket up to date.
+ onDisconnect(true /* updateSocketState */);
+
try {
Libcore.os.connect(fd, InetAddress.UNSPECIFIED, 0);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsIOException();
}
- if (socket != null) {
- socket.disconnect();
- }
return this;
}
+ /**
+ * Initialize the state associated with being disconnected, optionally syncing the socket if
+ * there is one.
+ * @hide used to sync state, non-private to avoid synthetic method
+ */
+ void onDisconnect(boolean updateSocketState) {
+ connected = false;
+ connectAddress = null;
+ if (updateSocketState && socket != null && socket.isConnected()) {
+ socket.onDisconnect();
+ }
+ }
+
@Override
public SocketAddress receive(ByteBuffer target) throws IOException {
target.checkWritable();
@@ -191,7 +241,7 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
SocketAddress retAddr = null;
DatagramPacket receivePacket;
int oldposition = target.position();
- int received = 0;
+ int received;
// TODO: disallow mapped buffers and lose this conditional?
if (target.hasArray()) {
receivePacket = new DatagramPacket(target.array(), target.position() + target.arrayOffset(), target.remaining());
@@ -200,7 +250,7 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
}
do {
received = IoBridge.recvfrom(false, fd, receivePacket.getData(), receivePacket.getOffset(), receivePacket.getLength(), 0, receivePacket, isConnected());
- if (receivePacket != null && receivePacket.getAddress() != null) {
+ if (receivePacket.getAddress() != null) {
if (received > 0) {
if (target.hasArray()) {
target.position(oldposition + received);
@@ -220,10 +270,10 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
SocketAddress retAddr = null;
DatagramPacket receivePacket = new DatagramPacket(EmptyArray.BYTE, 0);
int oldposition = target.position();
- int received = 0;
+ int received;
do {
received = IoBridge.recvfrom(false, fd, target, 0, receivePacket, isConnected());
- if (receivePacket != null && receivePacket.getAddress() != null) {
+ if (receivePacket.getAddress() != null) {
// copy the data of received packet
if (received > 0) {
target.position(oldposition + received);
@@ -259,7 +309,9 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
if (sendCount > 0) {
source.position(oldPosition + sendCount);
}
- isBound = true;
+ if (!isBound) {
+ onBind(true /* updateSocketState */);
+ }
} finally {
end(sendCount >= 0);
}
@@ -276,7 +328,7 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
return 0;
}
- int readCount = 0;
+ int readCount;
if (target.isDirect() || target.hasArray()) {
readCount = readImpl(target);
if (readCount > 0) {
@@ -405,11 +457,12 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
}
@Override protected synchronized void implCloseSelectableChannel() throws IOException {
- connected = false;
+ // A closed channel is not connected.
+ onDisconnect(true /* updateSocketState */);
+ IoBridge.closeSocket(fd);
+
if (socket != null && !socket.isClosed()) {
- socket.close();
- } else {
- IoBridge.closeSocket(fd);
+ socket.onClose();
}
}
@@ -460,15 +513,29 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
/*
* The internal datagramChannelImpl.
*/
- private DatagramChannelImpl channelImpl;
+ private final DatagramChannelImpl channelImpl;
/*
* Constructor initialize the datagramSocketImpl and datagramChannelImpl
*/
- DatagramSocketAdapter(DatagramSocketImpl socketimpl,
- DatagramChannelImpl channelImpl) {
+ DatagramSocketAdapter(DatagramSocketImpl socketimpl, DatagramChannelImpl channelImpl) {
super(socketimpl);
this.channelImpl = channelImpl;
+
+ // Sync state socket state with the channel it is being created from
+ if (channelImpl.isBound) {
+ onBind(channelImpl.localAddress, channelImpl.localPort);
+ }
+ if (channelImpl.connected) {
+ onConnect(
+ channelImpl.connectAddress.getAddress(),
+ channelImpl.connectAddress.getPort());
+ } else {
+ onDisconnect();
+ }
+ if (!channelImpl.isOpen()) {
+ onClose();
+ }
}
/*
@@ -479,25 +546,16 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
return channelImpl;
}
- /**
- * @see java.net.DatagramSocket#isBound()
- */
@Override
public boolean isBound() {
return channelImpl.isBound;
}
- /**
- * @see java.net.DatagramSocket#isConnected()
- */
@Override
public boolean isConnected() {
return channelImpl.isConnected();
}
- /**
- * @see java.net.DatagramSocket#getInetAddress()
- */
@Override
public InetAddress getInetAddress() {
if (channelImpl.connectAddress == null) {
@@ -514,9 +572,6 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
}
}
- /**
- * @see java.net.DatagramSocket#getPort()
- */
@Override
public int getPort() {
if (channelImpl.connectAddress == null) {
@@ -525,46 +580,78 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
return channelImpl.connectAddress.getPort();
}
- /**
- * @see java.net.DatagramSocket#bind(java.net.SocketAddress)
- */
@Override
public void bind(SocketAddress localAddr) throws SocketException {
if (channelImpl.isConnected()) {
throw new AlreadyConnectedException();
}
super.bind(localAddr);
- channelImpl.isBound = true;
+ channelImpl.onBind(false /* updateSocketState */);
+ }
+
+ @Override
+ public void connect(SocketAddress peer) throws SocketException {
+ if (isConnected()) {
+ // RI compatibility: If the socket is already connected this fails.
+ throw new IllegalStateException("Socket is already connected.");
+ }
+ super.connect(peer);
+ // Connect may have performed an implicit bind(). Sync up here.
+ channelImpl.onBind(false /* updateSocketState */);
+
+ InetSocketAddress inetSocketAddress = (InetSocketAddress) peer;
+ channelImpl.onConnect(
+ inetSocketAddress.getAddress(), inetSocketAddress.getPort(),
+ false /* updateSocketState */);
+ }
+
+ @Override
+ public void connect(InetAddress address, int port) {
+ // To avoid implementing connect() twice call this.connect(SocketAddress) in preference
+ // to super.connect().
+ try {
+ connect(new InetSocketAddress(address, port));
+ } catch (SocketException e) {
+ // Ignored - there is nothing we can report here.
+ }
}
- /**
- * @see java.net.DatagramSocket#receive(java.net.DatagramPacket)
- */
@Override
public void receive(DatagramPacket packet) throws IOException {
if (!channelImpl.isBlocking()) {
throw new IllegalBlockingModeException();
}
+
+ boolean wasBound = isBound();
super.receive(packet);
+ if (!wasBound) {
+ // DatagramSocket.receive() will implicitly bind if it hasn't been done explicitly.
+ // Sync the channel state with the socket.
+ channelImpl.onBind(false /* updateSocketState */);
+ }
}
- /**
- * @see java.net.DatagramSocket#send(java.net.DatagramPacket)
- */
@Override
public void send(DatagramPacket packet) throws IOException {
if (!channelImpl.isBlocking()) {
throw new IllegalBlockingModeException();
}
+
+ // DatagramSocket.send() will implicitly bind if it hasn't been done explicitly. Force
+ // bind() here so that the channel state stays in sync with the socket.
+ boolean wasBound = isBound();
super.send(packet);
+ if (!wasBound) {
+ // DatagramSocket.send() will implicitly bind if it hasn't been done explicitly.
+ // Sync the channel state with the socket.
+ channelImpl.onBind(false /* updateSocketState */);
+ }
}
- /**
- * @see java.net.DatagramSocket#close()
- */
@Override
public void close() {
synchronized (channelImpl) {
+ super.close();
if (channelImpl.isOpen()) {
try {
channelImpl.close();
@@ -572,21 +659,13 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
// Ignore
}
}
- super.close();
}
}
- /**
- * @see java.net.DatagramSocket#disconnect()
- */
@Override
public void disconnect() {
- try {
- channelImpl.disconnect();
- } catch (IOException e) {
- // Ignore
- }
super.disconnect();
+ channelImpl.onDisconnect(false /* updateSocketState */);
}
}
}
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index 3fb61a3..92bc419 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -19,11 +19,9 @@ package java.nio;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.net.PlainServerSocketImpl;
+import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
-import java.net.SocketAddress;
-import java.net.SocketImpl;
import java.net.SocketTimeoutException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.IllegalBlockingModeException;
@@ -41,27 +39,24 @@ import static libcore.io.OsConstants.*;
final class ServerSocketChannelImpl extends ServerSocketChannel implements FileDescriptorChannel {
private final ServerSocketAdapter socket;
- private final SocketImpl impl;
-
- private boolean isBound = false;
private final Object acceptLock = new Object();
public ServerSocketChannelImpl(SelectorProvider sp) throws IOException {
super(sp);
this.socket = new ServerSocketAdapter(this);
- this.impl = socket.getImpl$();
}
@Override public ServerSocket socket() {
return socket;
}
- @Override public SocketChannel accept() throws IOException {
+ @Override
+ public SocketChannel accept() throws IOException {
if (!isOpen()) {
throw new ClosedChannelException();
}
- if (!isBound) {
+ if (!socket.isBound()) {
throw new NotYetBoundException();
}
@@ -81,9 +76,9 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD
}
}
} finally {
- end(result.socket().isConnected());
+ end(result.isConnected());
}
- return result.socket().isConnected() ? result : null;
+ return result.isConnected() ? result : null;
}
private boolean shouldThrowSocketTimeoutExceptionFromAccept(SocketTimeoutException e) {
@@ -100,17 +95,19 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD
}
@Override protected void implConfigureBlocking(boolean blocking) throws IOException {
- IoUtils.setBlocking(impl.getFD$(), blocking);
+ IoUtils.setBlocking(socket.getFD$(), blocking);
}
+ @Override
synchronized protected void implCloseSelectableChannel() throws IOException {
if (!socket.isClosed()) {
socket.close();
}
}
+ @Override
public FileDescriptor getFD() {
- return impl.getFD$();
+ return socket.getFD$();
}
private static class ServerSocketAdapter extends ServerSocket {
@@ -120,13 +117,8 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD
this.channelImpl = aChannelImpl;
}
- @Override public void bind(SocketAddress localAddress, int backlog) throws IOException {
- super.bind(localAddress, backlog);
- channelImpl.isBound = true;
- }
-
@Override public Socket accept() throws IOException {
- if (!channelImpl.isBound) {
+ if (!isBound()) {
throw new IllegalBlockingModeException();
}
SocketChannel sc = channelImpl.accept();
@@ -142,9 +134,12 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD
try {
synchronized (this) {
super.implAccept(clientSocket);
- clientSocketChannel.setConnected();
- clientSocketChannel.setBound(true);
- clientSocketChannel.finishAccept();
+
+ // Sync the client socket's associated channel state with the Socket and OS.
+ InetSocketAddress remoteAddress =
+ new InetSocketAddress(
+ clientSocket.getInetAddress(), clientSocket.getPort());
+ clientSocketChannel.onAccept(remoteAddress, false /* updateSocketState */);
}
connectOK = true;
} finally {
@@ -159,23 +154,17 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD
return channelImpl;
}
- @Override public boolean isBound() {
- return channelImpl.isBound;
- }
-
- @Override public void bind(SocketAddress localAddress) throws IOException {
- super.bind(localAddress);
- channelImpl.isBound = true;
- }
-
@Override public void close() throws IOException {
synchronized (channelImpl) {
+ super.close();
if (channelImpl.isOpen()) {
channelImpl.close();
- } else {
- super.close();
}
}
}
+
+ private FileDescriptor getFD$() {
+ return super.getImpl$().getFD$();
+ }
}
}
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index b05e36c..6af9f9f 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -18,6 +18,8 @@
package java.nio;
import java.io.FileDescriptor;
+import java.io.FilterInputStream;
+import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -30,7 +32,6 @@ import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.SocketUtils;
-import java.net.UnknownHostException;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ConnectionPendingException;
@@ -74,6 +75,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
// The address to be connected.
private InetSocketAddress connectAddress = null;
+ // The local address the socket is bound to.
private InetAddress localAddress = null;
private int localPort;
@@ -133,20 +135,34 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
return socket;
}
- @Override
- synchronized public boolean isConnected() {
- return status == SOCKET_STATUS_CONNECTED;
- }
-
- /*
- * Status setting used by other class.
+ /**
+ * 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
+ * bind() or connect().
+ *
+ * @param updateSocketState
+ * if the associated socket (if present) needs to be updated
+ * @hide package visible for other nio classes
*/
- synchronized void setConnected() {
- status = SOCKET_STATUS_CONNECTED;
+ void onBind(boolean updateSocketState) {
+ SocketAddress sa;
+ try {
+ sa = Libcore.os.getsockname(fd);
+ } catch (ErrnoException errnoException) {
+ throw new AssertionError(errnoException);
+ }
+ isBound = true;
+ InetSocketAddress localSocketAddress = (InetSocketAddress) sa;
+ localAddress = localSocketAddress.getAddress();
+ localPort = localSocketAddress.getPort();
+ if (updateSocketState && socket != null) {
+ socket.onBind(localAddress, localPort);
+ }
}
- void setBound(boolean flag) {
- isBound = flag;
+ @Override
+ synchronized public boolean isConnected() {
+ return status == SOCKET_STATUS_CONNECTED;
}
@Override
@@ -169,16 +185,22 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
normalAddr = InetAddress.getLocalHost();
}
+ boolean isBlocking = isBlocking();
boolean finished = false;
+ int newStatus;
try {
- if (isBlocking()) {
+ if (isBlocking) {
begin();
}
- finished = IoBridge.connect(fd, normalAddr, port);
- isBound = finished;
+ // When in blocking mode, IoBridge.connect() will return without an exception when the
+ // socket is connected. When in non-blocking mode it will return without an exception
+ // without knowing the result of the connection attempt, which could still be going on.
+ IoBridge.connect(fd, normalAddr, port);
+ newStatus = isBlocking ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_PENDING;
+ finished = true;
} catch (IOException e) {
if (isEINPROGRESS(e)) {
- status = SOCKET_STATUS_PENDING;
+ newStatus = SOCKET_STATUS_PENDING;
} else {
if (isOpen()) {
close();
@@ -187,26 +209,36 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
throw e;
}
} finally {
- if (isBlocking()) {
+ if (isBlocking) {
end(finished);
}
}
- initLocalAddressAndPort();
- connectAddress = inetSocketAddress;
- if (socket != null) {
- socket.socketImpl().initRemoteAddressAndPort(connectAddress.getAddress(),
- connectAddress.getPort());
+ // If the channel was not bound, a connection attempt will have caused an implicit bind() to
+ // take place. Keep the local address state held by the channel and the socket up to date.
+ if (!isBound) {
+ onBind(true /* updateSocketState */);
}
- synchronized (this) {
- if (isBlocking()) {
- status = (finished ? SOCKET_STATUS_CONNECTED : SOCKET_STATUS_UNCONNECTED);
- } else {
- status = SOCKET_STATUS_PENDING;
- }
+ // Keep the connected state held by the channel and the socket up to date.
+ onConnectStatusChanged(inetSocketAddress, newStatus, true /* updateSocketState */);
+
+ return status == SOCKET_STATUS_CONNECTED;
+ }
+
+ /**
+ * Initialise the connect() state with the supplied information.
+ *
+ * @param updateSocketState
+ * if the associated socket (if present) needs to be updated
+ * @hide package visible for other nio classes
+ */
+ void onConnectStatusChanged(InetSocketAddress address, int status, boolean updateSocketState) {
+ this.status = status;
+ connectAddress = address;
+ if (status == SOCKET_STATUS_CONNECTED && updateSocketState && socket != null) {
+ socket.onConnect(connectAddress.getAddress(), connectAddress.getPort());
}
- return finished;
}
private boolean isEINPROGRESS(IOException e) {
@@ -222,21 +254,6 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
return false;
}
- private void initLocalAddressAndPort() {
- SocketAddress sa;
- try {
- sa = Libcore.os.getsockname(fd);
- } catch (ErrnoException errnoException) {
- throw new AssertionError(errnoException);
- }
- InetSocketAddress isa = (InetSocketAddress) sa;
- localAddress = isa.getAddress();
- localPort = isa.getPort();
- if (socket != null) {
- socket.socketImpl().initLocalPort(localPort);
- }
- }
-
@Override
public boolean finishConnect() throws IOException {
synchronized (this) {
@@ -257,7 +274,6 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
InetAddress inetAddress = connectAddress.getAddress();
int port = connectAddress.getPort();
finished = IoBridge.isConnected(fd, inetAddress, port, 0, 0); // Return immediately.
- isBound = finished;
} catch (ConnectException e) {
if (isOpen()) {
close();
@@ -270,15 +286,13 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
synchronized (this) {
status = (finished ? SOCKET_STATUS_CONNECTED : status);
- isBound = finished;
+ if (finished && socket != null) {
+ socket.onConnect(connectAddress.getAddress(), connectAddress.getPort());
+ }
}
return finished;
}
- void finishAccept() {
- initLocalAddressAndPort();
- }
-
@Override
public int read(ByteBuffer dst) throws IOException {
dst.checkWritable();
@@ -447,23 +461,17 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
}
/*
- * Get local address.
- */
- public InetAddress getLocalAddress() throws UnknownHostException {
- return isBound ? localAddress : Inet4Address.ANY;
- }
-
- /*
* Do really closing action here.
*/
@Override
protected synchronized void implCloseSelectableChannel() throws IOException {
if (status != SOCKET_STATUS_CLOSED) {
status = SOCKET_STATUS_CLOSED;
+ // IoBridge.closeSocket(fd) is idempotent: It is safe to call on an already-closed file
+ // descriptor.
+ IoBridge.closeSocket(fd);
if (socket != null && !socket.isClosed()) {
- socket.close();
- } else {
- IoBridge.closeSocket(fd);
+ socket.onClose();
}
}
}
@@ -479,6 +487,12 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
return fd;
}
+ /* @hide used by ServerSocketChannelImpl to sync channel state during accept() */
+ public void onAccept(InetSocketAddress remoteAddress, boolean updateSocketState) {
+ onBind(updateSocketState);
+ onConnectStatusChanged(remoteAddress, SOCKET_STATUS_CONNECTED, updateSocketState);
+ }
+
/*
* Adapter classes for internal socket.
*/
@@ -486,15 +500,24 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
private final SocketChannelImpl channel;
private final PlainSocketImpl socketImpl;
- SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel) throws SocketException {
+ SocketAdapter(PlainSocketImpl socketImpl, SocketChannelImpl channel)
+ throws SocketException {
super(socketImpl);
this.socketImpl = socketImpl;
this.channel = channel;
SocketUtils.setCreated(this);
- }
- PlainSocketImpl socketImpl() {
- return socketImpl;
+ // Sync state socket state with the channel it is being created from
+ if (channel.isBound) {
+ onBind(channel.localAddress, channel.localPort);
+ }
+ if (channel.isConnected()) {
+ onConnect(channel.connectAddress.getAddress(), channel.connectAddress.getPort());
+ }
+ if (!channel.isOpen()) {
+ onClose();
+ }
+
}
@Override
@@ -514,11 +537,7 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
@Override
public InetAddress getLocalAddress() {
- try {
- return channel.getLocalAddress();
- } catch (UnknownHostException e) {
- return null;
- }
+ return channel.localAddress != null ? channel.localAddress : Inet4Address.ANY;
}
@Override
@@ -530,10 +549,11 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
throw new AlreadyConnectedException();
}
super.connect(remoteAddr, timeout);
- channel.initLocalAddressAndPort();
+ channel.onBind(false);
if (super.isConnected()) {
- channel.setConnected();
- channel.isBound = super.isBound();
+ InetSocketAddress remoteInetAddress = (InetSocketAddress) remoteAddr;
+ channel.onConnectStatusChanged(
+ remoteInetAddress, SOCKET_STATUS_CONNECTED, false /* updateSocketState */);
}
}
@@ -546,47 +566,29 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
throw new ConnectionPendingException();
}
super.bind(localAddr);
- channel.initLocalAddressAndPort();
- channel.isBound = true;
+ channel.onBind(false);
}
@Override
public void close() throws IOException {
synchronized (channel) {
+ super.close();
if (channel.isOpen()) {
+ // channel.close() recognizes the socket is closed and avoids recursion. There
+ // is no channel.onClose() because the "closed" field is private.
channel.close();
- } else {
- super.close();
}
- channel.status = SocketChannelImpl.SOCKET_STATUS_CLOSED;
}
}
@Override
public OutputStream getOutputStream() throws IOException {
- checkOpenAndConnected();
- if (isOutputShutdown()) {
- throw new SocketException("Socket output is shutdown");
- }
- return new SocketChannelOutputStream(channel);
+ return new BlockingCheckOutputStream(super.getOutputStream(), channel);
}
@Override
public InputStream getInputStream() throws IOException {
- checkOpenAndConnected();
- if (isInputShutdown()) {
- throw new SocketException("Socket input is shutdown");
- }
- return new SocketChannelInputStream(channel);
- }
-
- private void checkOpenAndConnected() throws SocketException {
- if (!channel.isOpen()) {
- throw new SocketException("Socket is closed");
- }
- if (!channel.isConnected()) {
- throw new SocketException("Socket is not connected");
- }
+ return new BlockingCheckInputStream(super.getInputStream(), channel);
}
@Override
@@ -596,86 +598,92 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
}
/*
- * This output stream delegates all operations to the associated channel.
* Throws an IllegalBlockingModeException if the channel is in non-blocking
* mode when performing write operations.
*/
- private static class SocketChannelOutputStream extends OutputStream {
+ private static class BlockingCheckOutputStream extends FilterOutputStream {
private final SocketChannel channel;
- public SocketChannelOutputStream(SocketChannel channel) {
+ public BlockingCheckOutputStream(OutputStream out, SocketChannel channel) {
+ super(out);
this.channel = channel;
}
- /*
- * Closes this stream and channel.
- *
- * @throws IOException thrown if an error occurs during the close
- */
@Override
- public void close() throws IOException {
- channel.close();
+ public void write(byte[] buffer, int offset, int byteCount) throws IOException {
+ checkBlocking();
+ out.write(buffer, offset, byteCount);
}
@Override
- public void write(byte[] buffer, int offset, int byteCount) throws IOException {
- Arrays.checkOffsetAndCount(buffer.length, offset, byteCount);
- ByteBuffer buf = ByteBuffer.wrap(buffer, offset, byteCount);
- if (!channel.isBlocking()) {
- throw new IllegalBlockingModeException();
- }
- channel.write(buf);
+ public void write(int oneByte) throws IOException {
+ checkBlocking();
+ out.write(oneByte);
}
@Override
- public void write(int oneByte) throws IOException {
+ public void write(byte[] buffer) throws IOException {
+ checkBlocking();
+ out.write(buffer);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ // channel.close() recognizes the socket is closed and avoids recursion. There is no
+ // channel.onClose() because the "closed" field is private.
+ channel.close();
+ }
+
+ private void checkBlocking() {
if (!channel.isBlocking()) {
throw new IllegalBlockingModeException();
}
- ByteBuffer buffer = ByteBuffer.allocate(1);
- buffer.put(0, (byte) (oneByte & 0xFF));
- channel.write(buffer);
}
}
/*
- * This input stream delegates all operations to the associated channel.
* Throws an IllegalBlockingModeException if the channel is in non-blocking
* mode when performing read operations.
*/
- private static class SocketChannelInputStream extends InputStream {
+ private static class BlockingCheckInputStream extends FilterInputStream {
private final SocketChannel channel;
- public SocketChannelInputStream(SocketChannel channel) {
+ public BlockingCheckInputStream(InputStream in, SocketChannel channel) {
+ super(in);
this.channel = channel;
}
- /*
- * Closes this stream and channel.
- */
@Override
- public void close() throws IOException {
- channel.close();
+ public int read() throws IOException {
+ checkBlocking();
+ return in.read();
}
@Override
- public int read() throws IOException {
- if (!channel.isBlocking()) {
- throw new IllegalBlockingModeException();
- }
- ByteBuffer buf = ByteBuffer.allocate(1);
- int result = channel.read(buf);
- return (result == -1) ? result : (buf.get(0) & 0xff);
+ public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+ checkBlocking();
+ return in.read(buffer, byteOffset, byteCount);
}
@Override
- public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- Arrays.checkOffsetAndCount(buffer.length, byteOffset, byteCount);
+ public int read(byte[] buffer) throws IOException {
+ checkBlocking();
+ return in.read(buffer);
+ }
+
+ @Override
+ public void close() throws IOException {
+ super.close();
+ // channel.close() recognizes the socket is closed and avoids recursion. There is no
+ // channel.onClose() because the "closed" field is private.
+ channel.close();
+ }
+
+ private void checkBlocking() {
if (!channel.isBlocking()) {
throw new IllegalBlockingModeException();
}
- ByteBuffer buf = ByteBuffer.wrap(buffer, byteOffset, byteCount);
- return channel.read(buf);
}
}
}
diff --git a/luni/src/main/java/libcore/io/IoBridge.java b/luni/src/main/java/libcore/io/IoBridge.java
index e1c4ba6..a48a0ad 100644
--- a/luni/src/main/java/libcore/io/IoBridge.java
+++ b/luni/src/main/java/libcore/io/IoBridge.java
@@ -95,9 +95,9 @@ public final class IoBridge {
* Connects socket 'fd' to 'inetAddress' on 'port', with no timeout. The lack of a timeout
* means this method won't throw SocketTimeoutException.
*/
- public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
+ public static void connect(FileDescriptor fd, InetAddress inetAddress, int port) throws SocketException {
try {
- return IoBridge.connect(fd, inetAddress, port, 0);
+ IoBridge.connect(fd, inetAddress, port, 0);
} catch (SocketTimeoutException ex) {
throw new AssertionError(ex); // Can't happen for a connect without a timeout.
}
@@ -107,9 +107,9 @@ public final class IoBridge {
* Connects socket 'fd' to 'inetAddress' on 'port', with a the given 'timeoutMs'.
* Use timeoutMs == 0 for a blocking connect with no timeout.
*/
- public static boolean connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
+ public static void connect(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws SocketException, SocketTimeoutException {
try {
- return connectErrno(fd, inetAddress, port, timeoutMs);
+ connectErrno(fd, inetAddress, port, timeoutMs);
} catch (ErrnoException errnoException) {
throw new ConnectException(connectDetail(inetAddress, port, timeoutMs, errnoException), errnoException);
} catch (SocketException ex) {
@@ -121,11 +121,11 @@ public final class IoBridge {
}
}
- private static boolean connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
+ private static void connectErrno(FileDescriptor fd, InetAddress inetAddress, int port, int timeoutMs) throws ErrnoException, IOException {
// With no timeout, just call connect(2) directly.
if (timeoutMs == 0) {
Libcore.os.connect(fd, inetAddress, port);
- return true;
+ return;
}
// For connect with a timeout, we:
@@ -143,7 +143,7 @@ public final class IoBridge {
try {
Libcore.os.connect(fd, inetAddress, port);
IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
- return true; // We connected immediately.
+ return; // We connected immediately.
} catch (ErrnoException errnoException) {
if (errnoException.errno != EINPROGRESS) {
throw errnoException;
@@ -160,7 +160,6 @@ public final class IoBridge {
}
} while (!IoBridge.isConnected(fd, inetAddress, port, timeoutMs, remainingTimeoutMs));
IoUtils.setBlocking(fd, true); // 4. set the socket back to blocking.
- return true; // Or we'd have thrown.
}
private static String connectDetail(InetAddress inetAddress, int port, int timeoutMs, ErrnoException cause) {
diff --git a/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
index 6560a7b..0d0c69f 100644
--- a/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java
@@ -19,7 +19,6 @@ package libcore.java.nio.channels;
import dalvik.annotation.BrokenTest;
import java.io.IOException;
-import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -198,14 +197,12 @@ public class OldSocketChannelTest extends TestCase {
// expected
}
- SocketChannel channel1IP = null;
try {
- channel1IP = SocketChannel.open(null);
+ SocketChannel.open(null);
fail("Should throw an IllegalArgumentException");
} catch (IllegalArgumentException e) {
// correct
}
- assertNull(channel1IP);
}
private void ensureServerClosed() throws IOException {
@@ -355,48 +352,59 @@ public class OldSocketChannelTest extends TestCase {
isConstructorCalled = true;
}
+ @Override
public Socket socket() {
return null;
}
+ @Override
public boolean isConnected() {
return false;
}
+ @Override
public boolean isConnectionPending() {
return false;
}
+ @Override
public boolean connect(SocketAddress address) throws IOException {
return false;
}
+ @Override
public boolean finishConnect() throws IOException {
return false;
}
+ @Override
public int read(ByteBuffer target) throws IOException {
return 0;
}
+ @Override
public long read(ByteBuffer[] targets, int offset, int length)
throws IOException {
return 0;
}
+ @Override
public int write(ByteBuffer source) throws IOException {
return 0;
}
+ @Override
public long write(ByteBuffer[] sources, int offset, int length)
throws IOException {
return 0;
}
+ @Override
protected void implCloseSelectableChannel() throws IOException {
// empty
}
+ @Override
protected void implConfigureBlocking(boolean blockingMode)
throws IOException {
// empty
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 6ab91ab..fbb2d8d 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
@@ -16,10 +16,13 @@
package libcore.java.nio.channels;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.net.ConnectException;
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.Selector;
@@ -61,6 +64,7 @@ public class SocketChannelTest extends junit.framework.TestCase {
}
}
+ // https://code.google.com/p/android/issues/detail?id=56684
public void test_56684() throws Exception {
mockOs.enqueueFault("connect", ENETUNREACH);
@@ -78,7 +82,98 @@ public class SocketChannelTest extends junit.framework.TestCase {
try {
sc.finishConnect();
+ fail();
} catch (ClosedChannelException expected) {
}
}
+
+ /** Checks that closing a Socket's output stream also closes the Socket and SocketChannel. */
+ public void test_channelSocketOutputStreamClosureState() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress());
+ sc.configureBlocking(true);
+
+ Socket scSocket = sc.socket();
+ OutputStream os = scSocket.getOutputStream();
+
+ assertTrue(sc.isOpen());
+ assertFalse(scSocket.isClosed());
+
+ os.close();
+
+ assertFalse(sc.isOpen());
+ assertTrue(scSocket.isClosed());
+
+ ss.close();
+ }
+
+ /** Checks that closing a Socket's input stream also closes the Socket and SocketChannel. */
+ public void test_channelSocketInputStreamClosureState() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open(ss.getLocalSocketAddress());
+ sc.configureBlocking(true);
+
+ Socket scSocket = sc.socket();
+ InputStream is = scSocket.getInputStream();
+
+ assertTrue(sc.isOpen());
+ assertFalse(scSocket.isClosed());
+
+ is.close();
+
+ assertFalse(sc.isOpen());
+ assertTrue(scSocket.isClosed());
+
+ ss.close();
+ }
+
+ /**
+ * Tests connect() and object state for a blocking SocketChannel. Blocking mode is the default.
+ */
+ public void test_connect_blocking() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open();
+ assertTrue(sc.isBlocking());
+
+ assertTrue(sc.connect(ss.getLocalSocketAddress()));
+
+ assertTrue(sc.socket().isBound());
+ assertTrue(sc.isConnected());
+ assertTrue(sc.socket().isConnected());
+ assertFalse(sc.socket().isClosed());
+ assertTrue(sc.isBlocking());
+
+ ss.close();
+ sc.close();
+ }
+
+ /** Tests connect() and object state for a non-blocking SocketChannel. */
+ public void test_connect_nonBlocking() throws Exception {
+ ServerSocket ss = new ServerSocket(0);
+
+ SocketChannel sc = SocketChannel.open();
+ assertTrue(sc.isBlocking());
+ sc.configureBlocking(false);
+ assertFalse(sc.isBlocking());
+
+ if (!sc.connect(ss.getLocalSocketAddress())) {
+ do {
+ assertTrue(sc.socket().isBound());
+ assertFalse(sc.isConnected());
+ assertFalse(sc.socket().isConnected());
+ assertFalse(sc.socket().isClosed());
+ } while (!sc.finishConnect());
+ }
+ assertTrue(sc.socket().isBound());
+ assertTrue(sc.isConnected());
+ assertTrue(sc.socket().isConnected());
+ assertFalse(sc.socket().isClosed());
+ assertFalse(sc.isBlocking());
+
+ ss.close();
+ sc.close();
+ }
}