diff options
Diffstat (limited to 'luni/src')
-rw-r--r-- | luni/src/main/java/java/net/DatagramSocket.java | 57 | ||||
-rw-r--r-- | luni/src/main/java/java/net/DatagramSocketImpl.java | 36 | ||||
-rw-r--r-- | luni/src/main/java/java/net/PlainDatagramSocketImpl.java | 35 | ||||
-rw-r--r-- | luni/src/main/java/java/net/PlainSocketImpl.java | 39 | ||||
-rw-r--r-- | luni/src/main/java/java/net/ServerSocket.java | 29 | ||||
-rw-r--r-- | luni/src/main/java/java/net/Socket.java | 60 | ||||
-rw-r--r-- | luni/src/main/java/java/net/SocketImpl.java | 27 | ||||
-rw-r--r-- | luni/src/main/java/java/nio/DatagramChannelImpl.java | 211 | ||||
-rw-r--r-- | luni/src/main/java/java/nio/ServerSocketChannelImpl.java | 55 | ||||
-rw-r--r-- | luni/src/main/java/java/nio/SocketChannelImpl.java | 280 | ||||
-rw-r--r-- | luni/src/main/java/libcore/io/IoBridge.java | 15 | ||||
-rw-r--r-- | luni/src/test/java/libcore/java/nio/channels/OldSocketChannelTest.java | 16 | ||||
-rw-r--r-- | luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java | 95 |
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(); + } } |