summaryrefslogtreecommitdiffstats
path: root/luni/src
diff options
context:
space:
mode:
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();
+ }
}