summaryrefslogtreecommitdiffstats
path: root/luni
diff options
context:
space:
mode:
authorNeil Fuller <nfuller@google.com>2014-02-12 09:39:58 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2014-02-12 09:39:59 +0000
commit28261e443646049e8278ee0b64a2df9aa50f3b43 (patch)
treebfea6495cb5163714e3618e227eb6e038579f109 /luni
parent1bdb39d7af546f432f74e31c9c601fb30406166d (diff)
parentff81b740a338ba964e0ba1c40d925fb2ceb37bf2 (diff)
downloadlibcore-28261e443646049e8278ee0b64a2df9aa50f3b43.zip
libcore-28261e443646049e8278ee0b64a2df9aa50f3b43.tar.gz
libcore-28261e443646049e8278ee0b64a2df9aa50f3b43.tar.bz2
Merge "Adding additional methods to NetworkChannel."
Diffstat (limited to 'luni')
-rw-r--r--luni/src/main/java/java/net/MulticastSocket.java3
-rw-r--r--luni/src/main/java/java/net/SocketOption.java43
-rw-r--r--luni/src/main/java/java/net/SocketOptions.java81
-rw-r--r--luni/src/main/java/java/net/StandardSocketOptions.java407
-rw-r--r--luni/src/main/java/java/nio/DatagramChannelImpl.java26
-rw-r--r--luni/src/main/java/java/nio/NioUtils.java67
-rw-r--r--luni/src/main/java/java/nio/ServerSocketChannelImpl.java23
-rw-r--r--luni/src/main/java/java/nio/SocketChannelImpl.java23
-rw-r--r--luni/src/main/java/java/nio/channels/DatagramChannel.java26
-rw-r--r--luni/src/main/java/java/nio/channels/NetworkChannel.java40
-rw-r--r--luni/src/main/java/java/nio/channels/ServerSocketChannel.java26
-rw-r--r--luni/src/main/java/java/nio/channels/SocketChannel.java26
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java367
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java102
-rw-r--r--luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java307
15 files changed, 1543 insertions, 24 deletions
diff --git a/luni/src/main/java/java/net/MulticastSocket.java b/luni/src/main/java/java/net/MulticastSocket.java
index 6f4a582..970468a 100644
--- a/luni/src/main/java/java/net/MulticastSocket.java
+++ b/luni/src/main/java/java/net/MulticastSocket.java
@@ -351,7 +351,8 @@ public class MulticastSocket extends DatagramSocket {
/**
* Disables multicast loopback if {@code disable == true}.
* See {@link SocketOptions#IP_MULTICAST_LOOP}, and note that the sense of this is the
- * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}.
+ * opposite of the underlying Unix {@code IP_MULTICAST_LOOP}: true means disabled, false
+ * means enabled.
*
* @throws SocketException if an error occurs.
*/
diff --git a/luni/src/main/java/java/net/SocketOption.java b/luni/src/main/java/java/net/SocketOption.java
new file mode 100644
index 0000000..3f65494
--- /dev/null
+++ b/luni/src/main/java/java/net/SocketOption.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.net;
+
+/**
+ * An option associated with a socket.
+ *
+ * <p>See {@link java.nio.channels.NetworkChannel#setOption},
+ * {@link java.nio.channels.NetworkChannel#getOption} and
+ * {@link java.nio.channels.NetworkChannel#supportedOptions} for methods that use SocketOption.
+ *
+ * <p>See {@link StandardSocketOptions} for valid SocketOptions.
+ *
+ * @param <T> the type of the value
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public interface SocketOption<T> {
+
+ /**
+ * Returns the name of the option.
+ */
+ String name();
+
+ /**
+ * Returns the type of the value of the option.
+ */
+ Class<T> type();
+}
diff --git a/luni/src/main/java/java/net/SocketOptions.java b/luni/src/main/java/java/net/SocketOptions.java
index e23fc97..d0df689 100644
--- a/luni/src/main/java/java/net/SocketOptions.java
+++ b/luni/src/main/java/java/net/SocketOptions.java
@@ -28,19 +28,27 @@ package java.net;
*/
public interface SocketOptions {
/**
- * Number of seconds to wait when closing a socket if there
- * is still some buffered data to be sent.
+ * Number of seconds to wait when closing a socket if there is still some buffered data to be
+ * sent.
*
- * <p>If this option is set to 0, the TCP socket is closed forcefully and the
- * call to {@code close} returns immediately.
+ * <p>The option can be set to disabled using {@link #setOption(int, Object)} with a value of
+ * {@code Boolean.FALSE}.
*
- * <p>If this option is set to a value greater than 0, the value is interpreted
- * as the number of seconds to wait. If all data could be sent
- * during this time, the socket is closed normally. Otherwise the connection will be
- * closed forcefully.
+ * <p>If this option is set to 0, the TCP socket is closed forcefully and the call to
+ * {@code close} returns immediately.
*
- * <p>Valid values for this option are in the range 0 to 65535 inclusive. (Larger
+ * If this option is disabled, closing a socket will return immediately and the close will be
+ * handled in the background.
+ *
+ * <p>If this option is set to a value greater than 0, the value is interpreted as the number of
+ * seconds to wait. If all data could be sent during this time, the socket is closed normally.
+ * Otherwise the connection will be closed forcefully.
+ *
+ * <p>Valid numeric values for this option are in the range 0 to 65535 inclusive. (Larger
* timeouts will be treated as 65535s timeouts; roughly 18 hours.)
+ *
+ * <p>This option is intended for use with sockets in blocking mode. The behavior of this option
+ * for non-blocking sockets is undefined.
*/
public static final int SO_LINGER = 128;
@@ -54,16 +62,21 @@ public interface SocketOptions {
public static final int SO_TIMEOUT = 4102;
/**
- * This boolean option specifies whether data is sent immediately on this socket.
- * As a side-effect this could lead to low packet efficiency. The
- * socket implementation uses the Nagle's algorithm to try to reach a higher
- * packet efficiency if this option is disabled.
+ * This boolean option specifies whether data is sent immediately on this socket or buffered.
+ * <p>
+ * If set to {@code Boolean.TRUE} the Nagle algorithm is disabled and there is no buffering.
+ * This could lead to low packet efficiency. When set to {@code Boolean.FALSE} the the socket
+ * implementation uses buffering to try to reach a higher packet efficiency.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122: Requirements for Internet
+ * Hosts -- Communication Layers</a> for more information about buffering and the Nagle
+ * algorithm.
*/
public static final int TCP_NODELAY = 1;
/**
* This is an IPv4-only socket option whose functionality is subsumed by
- * {@link #IP_MULTICAST_IF} and not implemented on Android.
+ * {@link #IP_MULTICAST_IF2} and not implemented on Android.
*/
public static final int IP_MULTICAST_IF = 16;
@@ -73,9 +86,18 @@ public interface SocketOptions {
public static final int SO_BINDADDR = 15;
/**
- * This boolean option specifies whether a reuse of a local address is allowed even
- * if another socket is not yet removed by the operating system. It's only
- * available on a {@code MulticastSocket}.
+ * This boolean option specifies whether a reuse of a local address is allowed when another
+ * socket has not yet been removed by the operating system.
+ *
+ * <p>For connection-oriented sockets, if this option is disabled and if there is another socket
+ * in state TIME_WAIT on a given address then another socket binding to that address would fail.
+ * Setting this value after a socket is bound has no effect.
+ *
+ * <p>For datagram sockets this option determines whether several sockets can listen on the
+ * same address; when enabled each socket will receive a copy of the datagram.
+ *
+ * <p>See <a href="https://www.ietf.org/rfc/rfc793.txt">RFC 793: Transmission Control Protocol
+ * </a> for more information about socket re-use.
*/
public static final int SO_REUSEADDR = 4;
@@ -93,11 +115,18 @@ public interface SocketOptions {
* This is a hint to the kernel; the kernel may use a larger buffer.
*
* <p>For datagram sockets, packets larger than this value will be discarded.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc1323.txt">RFC1323: TCP Extensions for High
+ * Performance</a> for more information about TCP/IP buffering.
*/
public static final int SO_RCVBUF = 4098;
/**
- * This boolean option specifies whether the kernel sends keepalive messages.
+ * This boolean option specifies whether the kernel sends keepalive messages on
+ * connection-oriented sockets.
+ *
+ * <p>See <a href="http://www.ietf.org/rfc/rfc1122.txt">RFC 1122: Requirements for Internet
+ * Hosts -- Communication Layers</a> for more information on keep-alive.
*/
public static final int SO_KEEPALIVE = 8;
@@ -114,15 +143,18 @@ public interface SocketOptions {
/**
* This boolean option specifies whether the local loopback of multicast packets is
- * enabled or disabled. This option is enabled by default on multicast
- * sockets. Note that the sense of this option in Java is the
- * <i>opposite</i> of the underlying Unix {@code IP_MULTICAST_LOOP}.
- * See {@link MulticastSocket#setLoopbackMode}.
+ * enabled or disabled. This loopback is enabled by default on multicast sockets.
+ *
+ * <p>See <a href="http://tools.ietf.org/rfc/rfc1112.txt">RFC 1112: Host Extensions for IP
+ * Multicasting</a> for more information about IP multicast.
+ *
+ * <p>See {@link MulticastSocket#setLoopbackMode}.
*/
public static final int IP_MULTICAST_LOOP = 18;
/**
- * This boolean option can be used to enable broadcasting on datagram sockets.
+ * This boolean option can be used to enable or disable broadcasting on datagram sockets. This
+ * option must be enabled to send broadcast messages. The default value is false.
*/
public static final int SO_BROADCAST = 32;
@@ -135,6 +167,9 @@ public interface SocketOptions {
/**
* This integer option sets the outgoing interface for multicast packets
* using an interface index.
+ *
+ * <p>See <a href="http://tools.ietf.org/rfc/rfc1112.txt">RFC 1112: Host Extensions for IP
+ * Multicasting</a> for more information about IP multicast.
*/
public static final int IP_MULTICAST_IF2 = 31;
diff --git a/luni/src/main/java/java/net/StandardSocketOptions.java b/luni/src/main/java/java/net/StandardSocketOptions.java
new file mode 100644
index 0000000..3d10caf
--- /dev/null
+++ b/luni/src/main/java/java/net/StandardSocketOptions.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package java.net;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import libcore.io.IoBridge;
+
+/**
+ * Defines the set standard of socket options that can be supported by network channels.
+ *
+ * <p>See {@link java.nio.channels.NetworkChannel} for more information, particularly
+ * {@link java.nio.channels.NetworkChannel#supportedOptions()} for the options that are supported
+ * for each type of socket.
+ *
+ * @since 1.7
+ * @hide Until ready for a public API change
+ */
+public final class StandardSocketOptions {
+
+ /**
+ * The outgoing interface for multicast packets.
+ *
+ * <p>See {@link SocketOptions#IP_MULTICAST_IF2} for further documentation.
+ */
+ public static final SocketOption<NetworkInterface> IP_MULTICAST_IF =
+ new NetworkInterfaceSocketOption("IP_MULTICAST_IF", SocketOptions.IP_MULTICAST_IF2);
+
+ /**
+ * Whether the local loopback of multicast packets is enabled (true) or disabled (false). This
+ * option is enabled by default.
+ *
+ * <p>See {@link SocketOptions#IP_MULTICAST_LOOP} for further documentation.
+ */
+ public static final SocketOption<Boolean> IP_MULTICAST_LOOP =
+ new BooleanSocketOption("IP_MULTICAST_LOOP", SocketOptions.IP_MULTICAST_LOOP);
+
+ /**
+ * The time-to-live (TTL) for multicast packets. The value must be between 0 and 255 inclusive.
+ * A 0 value restricts the packet to the originating host. See also {@link #IP_MULTICAST_LOOP}.
+ * The default value is 1.
+ *
+ * <p>See <a href="http://tools.ietf.org/rfc/rfc1112.txt">RFC 1112: Host Extensions for IP
+ * Multicasting</a> for more information about IP multicast.
+ */
+ public static final SocketOption<Integer> IP_MULTICAST_TTL =
+ new ByteRangeSocketOption("IP_MULTICAST_TTL", IoBridge.JAVA_IP_MULTICAST_TTL);
+
+ /**
+ * The value for the type-of-service field of the IPv4 header, or the traffic class field of the
+ * IPv6 header. These correspond to the IP_TOS and IPV6_TCLASS socket options. These may be
+ * ignored by the underlying OS. Values must be between 0 and 255 inclusive.
+ *
+ * <p>See {@link SocketOptions#IP_TOS} for further documentation.
+ */
+ public static final SocketOption<Integer> IP_TOS =
+ new ByteRangeSocketOption("IP_TOS", SocketOptions.IP_TOS);
+
+ /**
+ * Whether broadcasting on datagram sockets is enabled or disabled. This option must be enabled to
+ * send broadcast messages. The default value is false.
+ *
+ * <p>See {@link SocketOptions#SO_BROADCAST} for further documentation.
+ */
+ public static final SocketOption<Boolean> SO_BROADCAST =
+ new BooleanSocketOption("SO_BROADCAST", SocketOptions.SO_BROADCAST);
+
+ /**
+ * Whether the kernel sends keepalive messages on connection-oriented sockets.
+ *
+ * <p>See {@link SocketOptions#SO_KEEPALIVE} for further documentation.
+ */
+ public static final SocketOption<Boolean> SO_KEEPALIVE =
+ new BooleanSocketOption("SO_KEEPALIVE", SocketOptions.SO_KEEPALIVE);
+
+ /**
+ * Number of seconds to wait when closing a socket if there is still some buffered data to be
+ * sent.
+ *
+ * <p>If this option is negative this option is disabled. This is the default value. If the value
+ * is 0 or positive it is enabled.
+ *
+ * <p>See {@link SocketOptions#SO_LINGER} for further documentation.
+ *
+ */
+ public static final SocketOption<Integer> SO_LINGER =
+ new SocketOptionImpl<Integer>("SO_LINGER", Integer.class, SocketOptions.SO_LINGER) {
+ @Override
+ protected Object validateAndConvertValueBeforeSet(
+ FileDescriptor fd, Integer value) {
+ Object objectValue = super.validateAndConvertValueBeforeSet(fd, value);
+ if (value != null && value < 0) {
+ // IoBridge requires a "false" object to disable linger.
+ objectValue = Boolean.FALSE;
+ }
+ return objectValue;
+ }
+
+ @Override
+ protected Integer validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ // IoBridge returns a "false" object to indicate that linger is disabled.
+ if (value != null && value instanceof Boolean) {
+ value = -1;
+ }
+ return super.validateAndConvertValueAfterGet(fd, value);
+ }
+ };
+
+ /**
+ * The size in bytes of a socket's receive buffer. This must be an integer greater than 0.
+ * This is a hint to the kernel; the kernel may use a larger buffer.
+ *
+ * <p>See {@link SocketOptions#SO_RCVBUF} for further documentation.
+ */
+ public static final SocketOption<Integer> SO_RCVBUF =
+ new PositiveIntegerSocketOption("SO_RCVBUF", SocketOptions.SO_RCVBUF);
+
+ /**
+ * Whether a reuse of a local address is allowed when another socket has not yet been removed by
+ * the operating system.
+ *
+ * <p>See {@link SocketOptions#SO_REUSEADDR} for further documentation.
+ */
+ public static final SocketOption<Boolean> SO_REUSEADDR =
+ new BooleanSocketOption("SO_REUSEADDR", SocketOptions.SO_REUSEADDR);
+
+ /**
+ * The size in bytes of a socket's send buffer. This must be an integer greater than 0.
+ * This is a hint to the kernel; the kernel may use a larger buffer.
+ *
+ * <p>See {@link SocketOptions#SO_SNDBUF} for further documentation.
+ */
+ public static final SocketOption<Integer> SO_SNDBUF =
+ new PositiveIntegerSocketOption("SO_SNDBUF", SocketOptions.SO_SNDBUF);
+
+ /**
+ * Specifies whether data is sent immediately on this socket or buffered.
+ *
+ * <p>See {@link SocketOptions#TCP_NODELAY} for further documentation.
+ */
+ public static final SocketOption<Boolean> TCP_NODELAY =
+ new BooleanSocketOption("TCP_NODELAY", SocketOptions.TCP_NODELAY);
+
+ /**
+ * The set of supported options for UDP sockets.
+ *
+ * @hide internal use only
+ */
+ public static final Set<SocketOption<?>> DATAGRAM_SOCKET_OPTIONS;
+
+ static {
+ HashSet<SocketOption<?>> mutableSet = new HashSet<SocketOption<?>>(8);
+ mutableSet.add(IP_MULTICAST_IF);
+ mutableSet.add(IP_MULTICAST_LOOP);
+ mutableSet.add(IP_MULTICAST_TTL);
+ mutableSet.add(IP_TOS);
+ mutableSet.add(SO_BROADCAST);
+ mutableSet.add(SO_REUSEADDR);
+ mutableSet.add(SO_RCVBUF);
+ mutableSet.add(SO_SNDBUF);
+ DATAGRAM_SOCKET_OPTIONS = Collections.unmodifiableSet(mutableSet);
+ }
+
+ /**
+ * The set of supported options for TCP sockets.
+ *
+ * @hide internal use only
+ */
+ public static final Set<SocketOption<?>> SOCKET_OPTIONS;
+
+ static {
+ HashSet<SocketOption<?>> mutableSet = new HashSet<SocketOption<?>>(7);
+ mutableSet.add(IP_TOS);
+ mutableSet.add(SO_KEEPALIVE);
+ mutableSet.add(SO_LINGER);
+ mutableSet.add(TCP_NODELAY);
+ mutableSet.add(SO_RCVBUF);
+ mutableSet.add(SO_REUSEADDR);
+ mutableSet.add(SO_SNDBUF);
+ SOCKET_OPTIONS = Collections.unmodifiableSet(mutableSet);
+ }
+
+ /**
+ * The set of supported options for TCP server sockets.
+ *
+ * @hide internal use only
+ */
+ public static final Set<SocketOption<?>> SERVER_SOCKET_OPTIONS;
+
+ static {
+ HashSet<SocketOption<?>> mutableSet = new HashSet<SocketOption<?>>(2);
+ mutableSet.add(SO_RCVBUF);
+ mutableSet.add(SO_REUSEADDR);
+ SERVER_SOCKET_OPTIONS = Collections.unmodifiableSet(mutableSet);
+ }
+
+ /**
+ * A base class for SocketOption objects that passes the values to/from {@link IoBridge} as they
+ * are. For use with simple types like Integer and Boolean, and can be extended for more
+ * validation / type conversion.
+ *
+ * @hide internal use only
+ */
+ public static class SocketOptionImpl<T> implements SocketOption<T> {
+
+ protected final String name;
+
+ private final Class<T> type;
+
+ protected final int socketOption;
+
+ public SocketOptionImpl(String name, Class<T> type, int socketOption) {
+ this.name = name;
+ this.type = type;
+ this.socketOption = socketOption;
+ }
+
+ @Override
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Class<T> type() {
+ return type;
+ }
+
+ /**
+ * Sets the socket option of the file descriptor to value using IoBridge.
+ *
+ * @hide internal method
+ */
+ public final void setValue(FileDescriptor fd, T value) throws IOException {
+ // Sanity check required because of type erasure.
+ if (value != null && !type.isAssignableFrom(value.getClass())) {
+ throw new AssertionError("Invalid type " + value + " of value for " + name);
+ }
+ Object objectValue = validateAndConvertValueBeforeSet(fd, value);
+ IoBridge.setSocketOption(fd, socketOption, objectValue);
+ }
+
+ /**
+ * Throws IllegalArgumentException if the value is outside of the acceptable range.
+ * Subclasses can override to apply option-specific validate, and may also convert the value
+ * to a different type or value. The default implementation prevents null values and returns
+ * the value unchanged.
+ */
+ protected Object validateAndConvertValueBeforeSet(FileDescriptor fd, T value) {
+ if (value == null) {
+ throw new IllegalArgumentException("value for " + name + " must not be null");
+ }
+ return value;
+ }
+
+ /**
+ * Gets the value of the socket option.
+ *
+ * @hide internal method
+ */
+ public final T getValue(FileDescriptor fd) throws IOException {
+ Object value = IoBridge.getSocketOption(fd, socketOption);
+ T typedValue = validateAndConvertValueAfterGet(fd, value);
+ if (typedValue != null && !type.isAssignableFrom(typedValue.getClass())) {
+ // Sanity check required because of type erasure.
+ throw new AssertionError("Unexpected type of value returned for " + name);
+ }
+ return typedValue;
+ }
+
+ /**
+ * Throws AssertionError if the value is outside of the acceptable range.
+ * Implementations may also convert the value to a different type or
+ * value. The default implementation does nothing.
+ */
+ @SuppressWarnings("unchecked")
+ protected T validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ return (T) value;
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an boolean value.
+ */
+ private static class BooleanSocketOption extends SocketOptionImpl<Boolean> {
+
+ public BooleanSocketOption(String name, int socketOption) {
+ super(name, Boolean.class, socketOption);
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an network interface value.
+ */
+ private static class NetworkInterfaceSocketOption extends SocketOptionImpl<NetworkInterface> {
+
+ public NetworkInterfaceSocketOption(String name, int socketOption) {
+ super(name, NetworkInterface.class, socketOption);
+ }
+
+ @Override
+ public Integer validateAndConvertValueBeforeSet(FileDescriptor fd, NetworkInterface value) {
+ if (value == null) {
+ throw new IllegalArgumentException("value for " + name + " must not be null");
+ }
+ int nicIndex = value.getIndex();
+ if (nicIndex == -1) {
+ throw new IllegalArgumentException("The NetworkInterface must have a valid index");
+ }
+ return nicIndex;
+ }
+
+ @Override
+ public NetworkInterface validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ if (value == null) {
+ return null;
+ } else if (!(value instanceof Integer)) {
+ throw new AssertionError("Unexpected type of value returned for " + name);
+ }
+
+ int nicIndex = (Integer) value;
+ try {
+ return NetworkInterface.getByIndex(nicIndex);
+ } catch (SocketException e) {
+ throw new IllegalArgumentException(
+ "Unable to resolve NetworkInterface index: " + nicIndex, e);
+ }
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an integer in the range 0-255.
+ */
+ private static class ByteRangeSocketOption extends SocketOptionImpl<Integer> {
+
+ public ByteRangeSocketOption(String name, int socketOption) {
+ super(name, Integer.class, socketOption);
+ }
+
+ @Override
+ protected Object validateAndConvertValueBeforeSet(FileDescriptor fd, Integer value) {
+ if (value == null || value < 0 || value > 255) {
+ throw new IllegalArgumentException(name + " must be >= 0 and <= 255, is " + value);
+ }
+ return value;
+ }
+
+ @Override
+ protected Integer validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ if (!(value instanceof Integer)) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ int intValue = (Integer) value;
+ if (intValue < 0 || intValue > 255) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ return intValue;
+ }
+ }
+
+ /**
+ * A SocketOption capable of setting / getting an integer in the range 1..
+ */
+ private static class PositiveIntegerSocketOption extends SocketOptionImpl<Integer> {
+
+ public PositiveIntegerSocketOption(String name, int socketOption) {
+ super(name, Integer.class, socketOption);
+ }
+
+ @Override
+ protected Integer validateAndConvertValueBeforeSet(FileDescriptor fd, Integer value) {
+ if (value < 1) {
+ throw new IllegalArgumentException(name + " value must be > 0");
+ }
+ return value;
+ }
+
+ @Override
+ protected Integer validateAndConvertValueAfterGet(FileDescriptor fd, Object value) {
+ if (!(value instanceof Integer)) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ int intValue = (Integer) value;
+ if (intValue < 1) {
+ throw new AssertionError("Unexpected value for option " + name + ": " + value);
+ }
+ return intValue;
+ }
+ }
+}
diff --git a/luni/src/main/java/java/nio/DatagramChannelImpl.java b/luni/src/main/java/java/nio/DatagramChannelImpl.java
index 3c4a980..cb940db 100644
--- a/luni/src/main/java/java/nio/DatagramChannelImpl.java
+++ b/luni/src/main/java/java/nio/DatagramChannelImpl.java
@@ -30,6 +30,8 @@ import java.net.InetSocketAddress;
import java.net.PlainDatagramSocketImpl;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
@@ -39,6 +41,8 @@ import java.nio.channels.NotYetConnectedException;
import java.nio.channels.UnsupportedAddressTypeException;
import java.nio.channels.spi.SelectorProvider;
import java.util.Arrays;
+import java.util.Set;
+
import libcore.io.ErrnoException;
import libcore.io.IoBridge;
import libcore.io.IoUtils;
@@ -155,6 +159,28 @@ class DatagramChannelImpl extends DatagramChannel implements FileDescriptorChann
return isBound ? new InetSocketAddress(localAddress, localPort) : null;
}
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ return NioUtils.getSocketOption(
+ this, StandardSocketOptions.DATAGRAM_SOCKET_OPTIONS, option);
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> DatagramChannel setOption(SocketOption<T> option, T value) throws IOException {
+ checkOpen();
+ NioUtils.setSocketOption(
+ this, StandardSocketOptions.DATAGRAM_SOCKET_OPTIONS, option, value);
+ return this;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return StandardSocketOptions.DATAGRAM_SOCKET_OPTIONS;
+ }
+
@Override
synchronized public boolean isConnected() {
return connected;
diff --git a/luni/src/main/java/java/nio/NioUtils.java b/luni/src/main/java/java/nio/NioUtils.java
index a1a46b6..34af76b 100644
--- a/luni/src/main/java/java/nio/NioUtils.java
+++ b/luni/src/main/java/java/nio/NioUtils.java
@@ -17,7 +17,12 @@
package java.nio;
import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
+import java.nio.channels.ClosedChannelException;
import java.nio.channels.FileChannel;
+import java.util.Set;
/**
* @hide internal use only
@@ -62,4 +67,66 @@ public final class NioUtils {
public static int unsafeArrayOffset(ByteBuffer b) {
return ((ByteArrayBuffer) b).arrayOffset;
}
+
+ /**
+ * Sets the supplied option on the channel to have the value if option is a member of
+ * allowedOptions.
+ *
+ * @throws IOException
+ * if the value could not be set due to IO errors.
+ * @throws IllegalArgumentException
+ * if the socket option or the value is invalid.
+ * @throws UnsupportedOperationException
+ * if the option is not a member of allowedOptions.
+ * @throws ClosedChannelException
+ * if the channel is closed
+ */
+ public static <T> void setSocketOption(
+ FileDescriptorChannel channel, Set<SocketOption<?>> allowedOptions,
+ SocketOption<T> option, T value)
+ throws IOException {
+
+ if (!(option instanceof StandardSocketOptions.SocketOptionImpl)) {
+ throw new IllegalArgumentException("SocketOption must come from StandardSocketOptions");
+ }
+ if (!allowedOptions.contains(option)) {
+ throw new UnsupportedOperationException(
+ option + " is not supported for this type of socket");
+ }
+ if (!channel.getFD().valid()) {
+ throw new ClosedChannelException();
+ }
+ ((StandardSocketOptions.SocketOptionImpl<T>) option).setValue(channel.getFD(), value);
+ }
+
+ /**
+ * Gets the supplied option from the channel if option is a member of allowedOptions.
+ *
+ * @throws IOException
+ * if the value could not be read due to IO errors.
+ * @throws IllegalArgumentException
+ * if the socket option is invalid.
+ * @throws UnsupportedOperationException
+ * if the option is not a member of allowedOptions.
+ * @throws ClosedChannelException
+ * if the channel is closed
+ */
+ public static <T> T getSocketOption(
+ FileDescriptorChannel channel, Set<SocketOption<?>> allowedOptions,
+ SocketOption<T> option)
+ throws IOException {
+
+ if (!(option instanceof StandardSocketOptions.SocketOptionImpl)) {
+ throw new IllegalArgumentException("SocketOption must come from StandardSocketOptions");
+ }
+ if (!allowedOptions.contains(option)) {
+ throw new UnsupportedOperationException(
+ option + " is not supported for this type of socket");
+ }
+ if (!channel.getFD().valid()) {
+ throw new ClosedChannelException();
+ }
+ return ((StandardSocketOptions.SocketOptionImpl<T>) option).getValue(channel.getFD());
+ }
+
}
diff --git a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
index 9ae282e..3d6cd22 100644
--- a/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/ServerSocketChannelImpl.java
@@ -23,7 +23,9 @@ import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
+import java.net.SocketOption;
import java.net.SocketTimeoutException;
+import java.net.StandardSocketOptions;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.IllegalBlockingModeException;
@@ -32,6 +34,8 @@ import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.UnsupportedAddressTypeException;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Set;
+
import libcore.io.ErrnoException;
import libcore.io.IoUtils;
import static libcore.io.OsConstants.*;
@@ -80,6 +84,25 @@ final class ServerSocketChannelImpl extends ServerSocketChannel implements FileD
return socket.getLocalSocketAddress();
}
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ return NioUtils.getSocketOption(this, StandardSocketOptions.SERVER_SOCKET_OPTIONS, option);
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> ServerSocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ NioUtils.setSocketOption(this, StandardSocketOptions.SERVER_SOCKET_OPTIONS, option, value);
+ return this;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return StandardSocketOptions.SERVER_SOCKET_OPTIONS;
+ }
+
@Override
public SocketChannel accept() throws IOException {
if (!isOpen()) {
diff --git a/luni/src/main/java/java/nio/SocketChannelImpl.java b/luni/src/main/java/java/nio/SocketChannelImpl.java
index 977c433..5d3db43 100644
--- a/luni/src/main/java/java/nio/SocketChannelImpl.java
+++ b/luni/src/main/java/java/nio/SocketChannelImpl.java
@@ -31,7 +31,9 @@ import java.net.PlainSocketImpl;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
+import java.net.SocketOption;
import java.net.SocketUtils;
+import java.net.StandardSocketOptions;
import java.nio.channels.AlreadyBoundException;
import java.nio.channels.AlreadyConnectedException;
import java.nio.channels.ClosedChannelException;
@@ -44,6 +46,8 @@ import java.nio.channels.UnresolvedAddressException;
import java.nio.channels.UnsupportedAddressTypeException;
import java.nio.channels.spi.SelectorProvider;
import java.util.Arrays;
+import java.util.Set;
+
import libcore.io.ErrnoException;
import libcore.io.Libcore;
import libcore.io.IoBridge;
@@ -192,6 +196,25 @@ class SocketChannelImpl extends SocketChannel implements FileDescriptorChannel {
return isBound ? new InetSocketAddress(localAddress, localPort) : null;
}
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ return NioUtils.getSocketOption(this, StandardSocketOptions.SOCKET_OPTIONS, option);
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> SocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ NioUtils.setSocketOption(this, StandardSocketOptions.SOCKET_OPTIONS, option, value);
+ return this;
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ return StandardSocketOptions.SOCKET_OPTIONS;
+ }
+
@Override
synchronized public boolean isConnected() {
return status == SOCKET_STATUS_CONNECTED;
diff --git a/luni/src/main/java/java/nio/channels/DatagramChannel.java b/luni/src/main/java/java/nio/channels/DatagramChannel.java
index 2040b8e..2aa228d 100644
--- a/luni/src/main/java/java/nio/channels/DatagramChannel.java
+++ b/luni/src/main/java/java/nio/channels/DatagramChannel.java
@@ -20,9 +20,11 @@ package java.nio.channels;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.SocketAddress;
+import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Set;
/**
* A {@code DatagramChannel} is a selectable channel that represents a partial
@@ -104,6 +106,30 @@ public abstract class DatagramChannel extends AbstractSelectableChannel
throw new UnsupportedOperationException("Subclasses must override this method");
}
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> DatagramChannel setOption(SocketOption<T> option, T value) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
/**
* Returns whether this channel's socket is connected or not.
*
diff --git a/luni/src/main/java/java/nio/channels/NetworkChannel.java b/luni/src/main/java/java/nio/channels/NetworkChannel.java
index b3505b9..9b46e30 100644
--- a/luni/src/main/java/java/nio/channels/NetworkChannel.java
+++ b/luni/src/main/java/java/nio/channels/NetworkChannel.java
@@ -19,6 +19,8 @@ package java.nio.channels;
import java.io.Closeable;
import java.io.IOException;
import java.net.SocketAddress;
+import java.net.SocketOption;
+import java.util.Set;
/**
* A common interface for channels that are backed by network sockets.
@@ -65,4 +67,42 @@ public interface NetworkChannel extends AutoCloseable, Channel, Closeable {
* @hide Until ready for a public API change
*/
SocketAddress getLocalAddress() throws IOException;
+
+ /**
+ * Returns the value for the socket option.
+ *
+ * @throws UnsupportedOperationException
+ * if the option is not supported by the socket.
+ * @throws java.nio.channels.ClosedChannelException
+ * if the socket is closed
+ * @throws IOException
+ * if the value cannot be read.
+ * @hide Until ready for a public API change
+ * @see java.net.StandardSocketOptions
+ */
+ <T> T getOption(SocketOption<T> option) throws IOException;
+
+ /**
+ * Sets the value for the socket option.
+ *
+ * @return this NetworkChannel
+ * @throws UnsupportedOperationException
+ * if the option is not supported by the socket.
+ * @throws IllegalArgumentException
+ * if the value is not valid for the option.
+ * @throws java.nio.channels.ClosedChannelException
+ * if the socket is closed
+ * @throws IOException
+ * if the value cannot be written.
+ * @hide Until ready for a public API change
+ * @see java.net.StandardSocketOptions
+ */
+ <T> NetworkChannel setOption(SocketOption<T> option, T value) throws IOException;
+
+ /**
+ * Returns the set of socket options supported by this channel.
+ *
+ * @hide Until ready for a public API change
+ */
+ Set<SocketOption<?>> supportedOptions();
}
diff --git a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
index aeb6d8f..c720451 100644
--- a/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
+++ b/luni/src/main/java/java/nio/channels/ServerSocketChannel.java
@@ -20,8 +20,10 @@ package java.nio.channels;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.SocketAddress;
+import java.net.SocketOption;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Set;
/**
* A {@code ServerSocketChannel} is a partial abstraction of a selectable,
@@ -129,6 +131,30 @@ public abstract class ServerSocketChannel extends AbstractSelectableChannel
throw new UnsupportedOperationException("Subclasses must override this method");
}
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> ServerSocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
/**
* Accepts a connection to this server-socket channel.
* <p>
diff --git a/luni/src/main/java/java/nio/channels/SocketChannel.java b/luni/src/main/java/java/nio/channels/SocketChannel.java
index 12dfe38..a6d1551 100644
--- a/luni/src/main/java/java/nio/channels/SocketChannel.java
+++ b/luni/src/main/java/java/nio/channels/SocketChannel.java
@@ -20,9 +20,11 @@ package java.nio.channels;
import java.io.IOException;
import java.net.Socket;
import java.net.SocketAddress;
+import java.net.SocketOption;
import java.nio.ByteBuffer;
import java.nio.channels.spi.AbstractSelectableChannel;
import java.nio.channels.spi.SelectorProvider;
+import java.util.Set;
/**
* A {@code SocketChannel} is a selectable channel that provides a partial
@@ -156,6 +158,30 @@ public abstract class SocketChannel extends AbstractSelectableChannel implements
throw new UnsupportedOperationException("Subclasses must override this method");
}
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> T getOption(SocketOption<T> option) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public <T> SocketChannel setOption(SocketOption<T> option, T value) throws IOException {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
+ /** @hide Until ready for a public API change */
+ @Override
+ public Set<SocketOption<?>> supportedOptions() {
+ // This method was added for interoperability with Java 7, where it is abstract. It is
+ // concrete here to avoid breaking existing Android applications that extend this class.
+ throw new UnsupportedOperationException("Subclasses must override this method");
+ }
+
/**
* Indicates whether this channel's socket is connected.
*
diff --git a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
index a0092d0..5927583 100644
--- a/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/DatagramChannelTest.java
@@ -16,9 +16,18 @@
package libcore.java.nio.channels;
+import java.io.IOException;
import java.net.DatagramSocket;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
+import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
+import java.util.Enumeration;
+import java.util.Set;
public class DatagramChannelTest extends junit.framework.TestCase {
public void test_read_intoReadOnlyByteArrays() throws Exception {
@@ -79,4 +88,362 @@ public class DatagramChannelTest extends junit.framework.TestCase {
dc.close();
}
}
+
+ public void test_supportedOptions() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ Set<SocketOption<?>> options = dc.supportedOptions();
+
+ // Probe some values. This is not intended to be complete.
+ assertTrue(options.contains(StandardSocketOptions.SO_REUSEADDR));
+ assertFalse(options.contains(StandardSocketOptions.TCP_NODELAY));
+ }
+
+ public void test_getOption_unsupportedOption() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.getOption(StandardSocketOptions.TCP_NODELAY);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+
+ dc.close();
+ }
+
+ public void test_getOption_afterClose() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.close();
+
+ try {
+ dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_setOption_afterClose() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.close();
+
+ try {
+ dc.setOption(StandardSocketOptions.SO_RCVBUF, 1234);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_getOption_SO_RCVBUF_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertTrue(value > 0);
+ assertEquals(value, dc.socket().getReceiveBufferSize());
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_RCVBUF_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetReceiveBufferSizeOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetReceiveBufferSizeOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ try {
+ dc.setOption(StandardSocketOptions.SO_RCVBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, dc.socket().getReceiveBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ dc.setOption(StandardSocketOptions.SO_RCVBUF, newBufferSize);
+ actualValue = dc.getOption(StandardSocketOptions.SO_RCVBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, dc.socket().getReceiveBufferSize());
+ }
+
+ public void test_getOption_SO_SNDBUF_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertTrue(value > 0);
+ assertEquals(value, dc.socket().getSendBufferSize());
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_SNDBUF_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetSendBufferSizeOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetSendBufferSizeOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ try {
+ dc.setOption(StandardSocketOptions.SO_SNDBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, dc.socket().getSendBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ dc.setOption(StandardSocketOptions.SO_SNDBUF, newBufferSize);
+ actualValue = dc.getOption(StandardSocketOptions.SO_SNDBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, dc.socket().getSendBufferSize());
+ }
+
+ public void test_getOption_IP_MULTICAST_IF_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ NetworkInterface networkInterface = dc.getOption(StandardSocketOptions.IP_MULTICAST_IF);
+ assertNull(networkInterface);
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_MULTICAST_IF_nullCheck() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, null);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_IF_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ assertTrue(networkInterfaces.hasMoreElements());
+ while (networkInterfaces.hasMoreElements()) {
+ trySetNetworkInterfaceOption(dc, networkInterfaces.nextElement());
+ }
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_IF_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 0));
+
+ Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
+ assertTrue(networkInterfaces.hasMoreElements());
+ while (networkInterfaces.hasMoreElements()) {
+ trySetNetworkInterfaceOption(dc, networkInterfaces.nextElement());
+ }
+
+ dc.close();
+ }
+
+ private static void trySetNetworkInterfaceOption(
+ DatagramChannel dc, NetworkInterface networkInterface) throws IOException {
+
+ NetworkInterface initialValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_IF);
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, null);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ assertEquals(initialValue, dc.getOption(StandardSocketOptions.IP_MULTICAST_IF));
+
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_IF, networkInterface);
+ NetworkInterface actualValue =
+ dc.getOption(StandardSocketOptions.IP_MULTICAST_IF);
+ assertEquals(networkInterface, actualValue);
+ }
+
+ public void test_getOption_IP_MULTICAST_LOOP_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ assertTrue(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_MULTICAST_LOOP_nullCheck() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, null);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_LOOP_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ assertTrue(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false);
+ assertFalse(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_LOOP_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(new InetSocketAddress(Inet4Address.getLoopbackAddress(), 0));
+
+ assertTrue(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_LOOP, false);
+ assertFalse(dc.getOption(StandardSocketOptions.IP_MULTICAST_LOOP));
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_MULTICAST_TTL_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ assertEquals(1, value);
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_MULTICAST_TTL_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetMulticastTtlOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetMulticastTtlOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ try {
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_TTL, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ assertEquals(initialValue, actualValue);
+
+ int newTtl = initialValue + 1;
+ dc.setOption(StandardSocketOptions.IP_MULTICAST_TTL, newTtl);
+ actualValue = dc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ assertEquals(newTtl, actualValue);
+ }
+
+ public void test_setOption_IP_MULTICAST_TTL_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null);
+
+ trySetMulticastTtlOption(dc);
+
+ dc.close();
+ }
+
+ public void test_getOption_SO_BROADCAST_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ assertFalse(dc.getOption(StandardSocketOptions.SO_BROADCAST));
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_BROADCAST_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetSoBroadcastOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetSoBroadcastOption(DatagramChannel dc) throws IOException {
+ boolean initialValue = dc.getOption(StandardSocketOptions.SO_BROADCAST);
+
+ dc.setOption(StandardSocketOptions.SO_BROADCAST, !initialValue);
+ boolean actualValue = dc.getOption(StandardSocketOptions.SO_BROADCAST);
+ assertEquals(!initialValue, actualValue);
+ }
+
+ public void test_setOption_SO_BROADCAST_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null);
+
+ trySetSoBroadcastOption(dc);
+
+ dc.close();
+ }
+
+ public void test_getOption_IP_TOS_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ int value = dc.getOption(StandardSocketOptions.IP_TOS);
+ assertEquals(0, value);
+ assertEquals(value, dc.socket().getTrafficClass());
+
+ dc.close();
+ }
+
+ public void test_setOption_IP_TOS_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ trySetTosOption(dc);
+
+ dc.close();
+ }
+
+ private static void trySetTosOption(DatagramChannel dc) throws IOException {
+ int initialValue = dc.getOption(StandardSocketOptions.IP_TOS);
+ try {
+ dc.setOption(StandardSocketOptions.IP_TOS, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ assertEquals(initialValue, (int) dc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, dc.socket().getTrafficClass());
+
+ try {
+ dc.setOption(StandardSocketOptions.IP_TOS, 256);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ assertEquals(initialValue, (int) dc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, dc.socket().getTrafficClass());
+
+ int newValue = (initialValue + 1) % 255;
+ dc.setOption(StandardSocketOptions.IP_TOS, newValue);
+ assertEquals(newValue, (int) dc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(newValue, dc.socket().getTrafficClass());
+ }
+
+ public void test_setOption_IP_TOS_afterBind() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+ dc.bind(null);
+
+ trySetTosOption(dc);
+
+ dc.close();
+ }
+
+ public void test_getOption_SO_REUSEADDR_defaults() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ boolean value = dc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ assertFalse(value);
+ assertFalse(dc.socket().getReuseAddress());
+
+ dc.close();
+ }
+
+ public void test_setOption_SO_REUSEADDR_afterOpen() throws Exception {
+ DatagramChannel dc = DatagramChannel.open();
+
+ boolean initialValue = dc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ dc.setOption(StandardSocketOptions.SO_REUSEADDR, !initialValue);
+ assertEquals(!initialValue, (boolean) dc.getOption(StandardSocketOptions.SO_REUSEADDR));
+ assertEquals(!initialValue, dc.socket().getReuseAddress());
+
+ dc.close();
+ }
+
}
diff --git a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
index bceb759..ed39d46 100644
--- a/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/ServerSocketChannelTest.java
@@ -21,9 +21,13 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
+import java.nio.channels.ClosedChannelException;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Enumeration;
+import java.util.Set;
public class ServerSocketChannelTest extends junit.framework.TestCase {
// http://code.google.com/p/android/issues/detail?id=16579
@@ -127,4 +131,102 @@ public class ServerSocketChannelTest extends junit.framework.TestCase {
return false;
}
}
+
+ public void test_supportedOptions() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ Set<SocketOption<?>> options = ssc.supportedOptions();
+
+ // Probe some values. This is not intended to be complete.
+ assertTrue(options.contains(StandardSocketOptions.SO_REUSEADDR));
+ assertFalse(options.contains(StandardSocketOptions.IP_MULTICAST_TTL));
+ }
+
+ public void test_getOption_unsupportedOption() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ try {
+ ssc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ fail();
+ } catch (UnsupportedOperationException expected) {}
+
+ ssc.close();
+ }
+
+ public void test_getOption_afterClose() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.close();
+
+ try {
+ ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_setOption_afterClose() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+ ssc.close();
+
+ try {
+ ssc.setOption(StandardSocketOptions.SO_RCVBUF, 1234);
+ fail();
+ } catch (ClosedChannelException expected) {}
+ }
+
+ public void test_getOption_SO_RCVBUF_defaults() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ int value = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertTrue(value > 0);
+ assertEquals(value, ssc.socket().getReceiveBufferSize());
+
+ ssc.close();
+ }
+
+ public void test_setOption_SO_RCVBUF_afterOpen() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ trySetReceiveBufferSizeOption(ssc);
+
+ ssc.close();
+ }
+
+ private static void trySetReceiveBufferSizeOption(ServerSocketChannel ssc) throws IOException {
+ int initialValue = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ try {
+ ssc.setOption(StandardSocketOptions.SO_RCVBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {}
+ int actualValue = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, ssc.socket().getReceiveBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ ssc.setOption(StandardSocketOptions.SO_RCVBUF, newBufferSize);
+ actualValue = ssc.getOption(StandardSocketOptions.SO_RCVBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, ssc.socket().getReceiveBufferSize());
+ }
+
+ public void test_getOption_SO_REUSEADDR_defaults() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ boolean value = ssc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ assertTrue(value);
+ assertTrue(ssc.socket().getReuseAddress());
+
+ ssc.close();
+ }
+
+ public void test_setOption_SO_REUSEADDR_afterOpen() throws Exception {
+ ServerSocketChannel ssc = ServerSocketChannel.open();
+
+ boolean initialValue = ssc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ ssc.setOption(StandardSocketOptions.SO_REUSEADDR, !initialValue);
+ assertEquals(!initialValue, (boolean) ssc.getOption(StandardSocketOptions.SO_REUSEADDR));
+ assertEquals(!initialValue, ssc.socket().getReuseAddress());
+
+ ssc.close();
+ }
+
}
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 553e20c..118a168 100644
--- a/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
+++ b/luni/src/test/java/libcore/java/nio/channels/SocketChannelTest.java
@@ -24,11 +24,15 @@ import java.net.Socket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
+import java.net.SocketOption;
+import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.nio.channels.SelectionKey;
+import java.util.Set;
+
import tests.io.MockOs;
import static libcore.io.OsConstants.*;
@@ -256,4 +260,307 @@ public class SocketChannelTest extends junit.framework.TestCase {
ss.close();
sc.close();
}
+
+ public void test_supportedOptions() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ Set<SocketOption<?>> options = sc.supportedOptions();
+
+ // Probe some values. This is not intended to be complete.
+ assertTrue(options.contains(StandardSocketOptions.SO_REUSEADDR));
+ assertFalse(options.contains(StandardSocketOptions.IP_MULTICAST_TTL));
+ }
+
+ public void test_getOption_unsupportedOption() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ try {
+ sc.getOption(StandardSocketOptions.IP_MULTICAST_TTL);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ }
+
+ sc.close();
+ }
+
+ public void test_getOption_afterClose() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.close();
+
+ try {
+ sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ public void test_setOption_afterClose() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.close();
+
+ try {
+ sc.setOption(StandardSocketOptions.SO_RCVBUF, 1234);
+ fail();
+ } catch (ClosedChannelException expected) {
+ }
+ }
+
+ public void test_getOption_SO_RCVBUF_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int value = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertTrue(value > 0);
+ assertEquals(value, sc.socket().getReceiveBufferSize());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_RCVBUF_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetReceiveBufferSizeOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetReceiveBufferSizeOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ try {
+ sc.setOption(StandardSocketOptions.SO_RCVBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ int actualValue = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, sc.socket().getReceiveBufferSize());
+
+ int newBufferSize = initialValue - 1;
+ sc.setOption(StandardSocketOptions.SO_RCVBUF, newBufferSize);
+ actualValue = sc.getOption(StandardSocketOptions.SO_RCVBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, sc.socket().getReceiveBufferSize());
+ }
+
+ public void test_getOption_SO_SNDBUF_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int bufferSize = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertTrue(bufferSize > 0);
+ assertEquals(bufferSize, sc.socket().getSendBufferSize());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_SNDBUF_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetSendBufferSizeOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetSendBufferSizeOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ try {
+ sc.setOption(StandardSocketOptions.SO_SNDBUF, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ int actualValue = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ assertEquals(initialValue, actualValue);
+ assertEquals(initialValue, sc.socket().getSendBufferSize());
+
+ int newValue = initialValue - 1;
+ sc.setOption(StandardSocketOptions.SO_SNDBUF, newValue);
+ actualValue = sc.getOption(StandardSocketOptions.SO_SNDBUF);
+ // The Linux Kernel actually doubles the value it is given and may choose to ignore it.
+ // This assertion may be brittle.
+ assertTrue(actualValue != initialValue);
+ assertEquals(actualValue, sc.socket().getSendBufferSize());
+ }
+
+ public void test_getOption_SO_KEEPALIVE_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ assertFalse(sc.getOption(StandardSocketOptions.SO_KEEPALIVE));
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_KEEPALIVE_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetSoKeepaliveOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetSoKeepaliveOption(SocketChannel sc) throws IOException {
+ boolean initialValue = sc.getOption(StandardSocketOptions.SO_KEEPALIVE);
+
+ sc.setOption(StandardSocketOptions.SO_KEEPALIVE, !initialValue);
+ boolean actualValue = sc.getOption(StandardSocketOptions.SO_KEEPALIVE);
+ assertEquals(!initialValue, actualValue);
+ }
+
+ public void test_setOption_SO_KEEPALIVE_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetSoKeepaliveOption(sc);
+
+ sc.close();
+ }
+
+ public void test_getOption_IP_TOS_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int value = sc.getOption(StandardSocketOptions.IP_TOS);
+ assertEquals(0, value);
+ assertEquals(value, sc.socket().getTrafficClass());
+
+ sc.close();
+ }
+
+ public void test_setOption_IP_TOS_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetTosOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetTosOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.IP_TOS);
+ try {
+ sc.setOption(StandardSocketOptions.IP_TOS, -1);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ assertEquals(initialValue, (int) sc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, sc.socket().getTrafficClass());
+
+ try {
+ sc.setOption(StandardSocketOptions.IP_TOS, 256);
+ fail();
+ } catch (IllegalArgumentException expected) {
+ }
+ assertEquals(initialValue, (int) sc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(initialValue, sc.socket().getTrafficClass());
+
+ int newValue = (initialValue + 1) % 255;
+ sc.setOption(StandardSocketOptions.IP_TOS, newValue);
+ assertEquals(newValue, (int) sc.getOption(StandardSocketOptions.IP_TOS));
+ assertEquals(newValue, sc.socket().getTrafficClass());
+ }
+
+ public void test_setOption_IP_TOS_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetTosOption(sc);
+
+ sc.close();
+ }
+
+ public void test_getOption_SO_LINGER_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ int value = sc.getOption(StandardSocketOptions.SO_LINGER);
+ assertTrue(value < 0);
+ assertEquals(value, sc.socket().getSoLinger());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_LINGER_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetLingerOption(sc);
+
+ sc.close();
+ }
+
+ private static void trySetLingerOption(SocketChannel sc) throws IOException {
+ int initialValue = sc.getOption(StandardSocketOptions.SO_LINGER);
+ // Any negative value disables the setting, -1 is used to report SO_LINGER being disabled.
+ sc.setOption(StandardSocketOptions.SO_LINGER, -2);
+ int soLingerDisabled = -1;
+ assertEquals(soLingerDisabled, (int) sc.getOption(StandardSocketOptions.SO_LINGER));
+ assertEquals(soLingerDisabled, sc.socket().getSoLinger());
+
+ sc.setOption(StandardSocketOptions.SO_LINGER, 65536);
+ assertEquals(65535, (int) sc.getOption(StandardSocketOptions.SO_LINGER));
+ assertEquals(65535, sc.socket().getSoLinger());
+
+ int newValue = (initialValue + 1) % 65535;
+ sc.setOption(StandardSocketOptions.SO_LINGER, newValue);
+ assertEquals(newValue, (int) sc.getOption(StandardSocketOptions.SO_LINGER));
+ assertEquals(newValue, sc.socket().getSoLinger());
+ }
+
+ public void test_setOption_SO_LINGER_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetLingerOption(sc);
+
+ sc.close();
+ }
+
+ public void test_getOption_SO_REUSEADDR_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ boolean value = sc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ assertFalse(value);
+ assertFalse(sc.socket().getReuseAddress());
+
+ sc.close();
+ }
+
+ public void test_setOption_SO_REUSEADDR_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ boolean initialValue = sc.getOption(StandardSocketOptions.SO_REUSEADDR);
+ sc.setOption(StandardSocketOptions.SO_REUSEADDR, !initialValue);
+ assertEquals(!initialValue, (boolean) sc.getOption(StandardSocketOptions.SO_REUSEADDR));
+ assertEquals(!initialValue, sc.socket().getReuseAddress());
+
+ sc.close();
+ }
+
+ public void test_getOption_TCP_NODELAY_defaults() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ boolean value = sc.getOption(StandardSocketOptions.TCP_NODELAY);
+ assertFalse(value);
+ assertFalse(sc.socket().getTcpNoDelay());
+
+ sc.close();
+ }
+
+ public void test_setOption_TCP_NODELAY_afterOpen() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+
+ trySetNoDelay(sc);
+
+ sc.close();
+ }
+
+ private static void trySetNoDelay(SocketChannel sc) throws IOException {
+ boolean initialValue = sc.getOption(StandardSocketOptions.TCP_NODELAY);
+ sc.setOption(StandardSocketOptions.TCP_NODELAY, !initialValue);
+ assertEquals(!initialValue, (boolean) sc.getOption(StandardSocketOptions.TCP_NODELAY));
+ assertEquals(!initialValue, sc.socket().getTcpNoDelay());
+ }
+
+ public void test_setOption_TCP_NODELAY_afterBind() throws Exception {
+ SocketChannel sc = SocketChannel.open();
+ sc.bind(null);
+
+ trySetNoDelay(sc);
+
+ sc.close();
+ }
+
}