/*
* 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.NetworkBridge;
/**
* 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", NetworkBridge.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);
NetworkBridge.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 = NetworkBridge.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;
}
}
}